diff options
author | dartraiden <wowemuh@gmail.com> | 2023-01-02 21:10:29 +0300 |
---|---|---|
committer | dartraiden <wowemuh@gmail.com> | 2023-01-02 21:10:29 +0300 |
commit | 1979fd80424d16b2e489f9b57d01d9c7811d25a2 (patch) | |
tree | 960d42c5fe4a51f0fe2850bea91256e226bce221 /src | |
parent | adfbbb217d4f4a05acf198755f219a5223d31c27 (diff) |
Update copyrights
Diffstat (limited to 'src')
314 files changed, 35602 insertions, 35602 deletions
diff --git a/src/core/stdautoaway/src/autoaway.cpp b/src/core/stdautoaway/src/autoaway.cpp index f9a7fa8379..024487ed5a 100644 --- a/src/core/stdautoaway/src/autoaway.cpp +++ b/src/core/stdautoaway/src/autoaway.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdautoaway/src/idle.cpp b/src/core/stdautoaway/src/idle.cpp index aaabe0ddb2..b4faa31c4b 100644 --- a/src/core/stdautoaway/src/idle.cpp +++ b/src/core/stdautoaway/src/idle.cpp @@ -1,89 +1,89 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static UINT_PTR g_hTimer; - -int IdleOptInit(WPARAM wParam, LPARAM); - -static int IdleObject_IsUserIdle() -{ - if (g_plugin.bIdleMethod) { - uint32_t dwTick = Miranda_GetIdle(); - return GetTickCount() - dwTick > (g_plugin.iIdleTime1st * 60 * 1000); - } - - LASTINPUTINFO ii = { sizeof(ii) }; - if (GetLastInputInfo(&ii)) - return GetTickCount() - ii.dwTime > (g_plugin.iIdleTime1st * 60 * 1000); - - return FALSE; -} - -static void CALLBACK IdleTimer(HWND, UINT, UINT_PTR idEvent, DWORD) -{ - if (g_hTimer != idEvent) - return; - - if (g_plugin.bIdleCheck && IdleObject_IsUserIdle()) - Idle_Enter(1); - - else if (g_plugin.bIdleOnSaver && IsScreenSaverRunning()) - Idle_Enter(2); - - else if (g_plugin.bIdleOnFullScr && IsFullScreen()) - Idle_Enter(3); - - else if (g_plugin.bIdleOnLock && IsWorkstationLocked()) - Idle_Enter(4); - - else if (g_plugin.bIdleOnTerminal && IsTerminalDisconnected()) - Idle_Enter(5); - - else // not idle - Idle_Enter(-1); -} - -void IdleObject_Create() -{ - g_hTimer = SetTimer(nullptr, 0, 2000, IdleTimer); -} - -void IdleObject_Destroy() -{ - KillTimer(nullptr, g_hTimer); -} - -void LoadIdleModule(void) -{ - IdleObject_Create(); - - HookEvent(ME_OPT_INITIALISE, IdleOptInit); -} - -void UnloadIdleModule() -{ - IdleObject_Destroy(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static UINT_PTR g_hTimer;
+
+int IdleOptInit(WPARAM wParam, LPARAM);
+
+static int IdleObject_IsUserIdle()
+{
+ if (g_plugin.bIdleMethod) {
+ uint32_t dwTick = Miranda_GetIdle();
+ return GetTickCount() - dwTick > (g_plugin.iIdleTime1st * 60 * 1000);
+ }
+
+ LASTINPUTINFO ii = { sizeof(ii) };
+ if (GetLastInputInfo(&ii))
+ return GetTickCount() - ii.dwTime > (g_plugin.iIdleTime1st * 60 * 1000);
+
+ return FALSE;
+}
+
+static void CALLBACK IdleTimer(HWND, UINT, UINT_PTR idEvent, DWORD)
+{
+ if (g_hTimer != idEvent)
+ return;
+
+ if (g_plugin.bIdleCheck && IdleObject_IsUserIdle())
+ Idle_Enter(1);
+
+ else if (g_plugin.bIdleOnSaver && IsScreenSaverRunning())
+ Idle_Enter(2);
+
+ else if (g_plugin.bIdleOnFullScr && IsFullScreen())
+ Idle_Enter(3);
+
+ else if (g_plugin.bIdleOnLock && IsWorkstationLocked())
+ Idle_Enter(4);
+
+ else if (g_plugin.bIdleOnTerminal && IsTerminalDisconnected())
+ Idle_Enter(5);
+
+ else // not idle
+ Idle_Enter(-1);
+}
+
+void IdleObject_Create()
+{
+ g_hTimer = SetTimer(nullptr, 0, 2000, IdleTimer);
+}
+
+void IdleObject_Destroy()
+{
+ KillTimer(nullptr, g_hTimer);
+}
+
+void LoadIdleModule(void)
+{
+ IdleObject_Create();
+
+ HookEvent(ME_OPT_INITIALISE, IdleOptInit);
+}
+
+void UnloadIdleModule()
+{
+ IdleObject_Destroy();
+}
diff --git a/src/core/stdautoaway/src/main.cpp b/src/core/stdautoaway/src/main.cpp index 971fd166ff..1022f2a4d5 100644 --- a/src/core/stdautoaway/src/main.cpp +++ b/src/core/stdautoaway/src/main.cpp @@ -2,7 +2,7 @@ Standard auto away module for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stdautoaway/src/options.cpp b/src/core/stdautoaway/src/options.cpp index 17a8f8381b..fed12a11f8 100644 --- a/src/core/stdautoaway/src/options.cpp +++ b/src/core/stdautoaway/src/options.cpp @@ -1,134 +1,134 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static const uint16_t aa_Status[] = { ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND }; - -int IdleGetStatusIndex(uint16_t status) -{ - for (int j = 0; j < _countof(aa_Status); j++) - if (aa_Status[j] == status) - return j; - - return 0; -} - -class COptionsDlg : public CDlgBase -{ - CCtrlEdit edt1sttime; - CCtrlSpin spinIdle; - CCtrlCombo cmbAAStatus; - CCtrlCheck chkShort, chkOnWindows, chkOnMiranda, chkScreenSaver, chkFullScreen, chkLocked; - CCtrlCheck chkPrivate, chkStatusLock, chkTerminal, chkSoundsOff, chkShortIdle; - - void ShowHide() - { - BOOL bChecked = chkShort.GetState(); - chkOnWindows.Enable(bChecked); - chkOnMiranda.Enable(bChecked); - edt1sttime.Enable(bChecked); - - bChecked = chkShortIdle.GetState(); - cmbAAStatus.Enable(bChecked); - chkStatusLock.Enable(bChecked); - } - -public: - COptionsDlg() : - CDlgBase(g_plugin, IDD_OPT_IDLE), - edt1sttime(this, IDC_IDLE1STTIME), - spinIdle(this, IDC_IDLESPIN, 60, 1), - cmbAAStatus(this, IDC_AASTATUS), - chkShort(this, IDC_IDLESHORT), - chkLocked(this, IDC_LOCKED), - chkPrivate(this, IDC_IDLEPRIVATE), - chkTerminal(this, IDC_IDLETERMINAL), - chkOnWindows(this, IDC_IDLEONWINDOWS), - chkSoundsOff(this, IDC_IDLESOUNDSOFF), - chkOnMiranda(this, IDC_IDLEONMIRANDA), - chkShortIdle(this, IDC_AASHORTIDLE), - chkStatusLock(this, IDC_IDLESTATUSLOCK), - chkFullScreen(this, IDC_FULLSCREEN), - chkScreenSaver(this, IDC_SCREENSAVER) - { - CreateLink(chkShort, g_plugin.bIdleCheck); - CreateLink(chkLocked, g_plugin.bIdleOnLock); - CreateLink(chkPrivate, g_plugin.bIdlePrivate); - CreateLink(chkTerminal, g_plugin.bIdleOnTerminal); - CreateLink(chkShortIdle, g_plugin.bAAEnable); - CreateLink(chkOnMiranda, g_plugin.bIdleMethod); - CreateLink(chkSoundsOff, g_plugin.bIdleSoundsOff); - CreateLink(chkStatusLock, g_plugin.bIdleStatusLock); - CreateLink(chkFullScreen, g_plugin.bIdleOnFullScr); - CreateLink(chkScreenSaver, g_plugin.bIdleOnSaver); - - chkShortIdle.OnChange = chkShort.OnChange = Callback(this, &COptionsDlg::onChange); - } - - bool OnInitDialog() override - { - chkOnWindows.SetState(!g_plugin.bIdleMethod); - - spinIdle.SetPosition(g_plugin.iIdleTime1st); - - for (auto &it : aa_Status) - cmbAAStatus.AddString(Clist_GetStatusModeDescription(it, 0)); - cmbAAStatus.SetCurSel(IdleGetStatusIndex(g_plugin.bAAStatus)); - - ShowHide(); - return true; - } - - bool OnApply() override - { - g_plugin.iIdleTime1st = spinIdle.GetPosition(); - - int curSel = cmbAAStatus.GetCurSel(); - if (curSel != CB_ERR) - g_plugin.bAAStatus = aa_Status[curSel]; - - // destroy any current idle and reset settings. - IdleObject_Destroy(); - IdleObject_Create(); - return true; - } - - void onChange(CCtrlCheck*) - { - ShowHide(); - } -}; - -int IdleOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 100000000; - odp.szGroup.a = LPGEN("Status"); - odp.szTitle.a = LPGEN("Idle"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new COptionsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static const uint16_t aa_Status[] = { ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND };
+
+int IdleGetStatusIndex(uint16_t status)
+{
+ for (int j = 0; j < _countof(aa_Status); j++)
+ if (aa_Status[j] == status)
+ return j;
+
+ return 0;
+}
+
+class COptionsDlg : public CDlgBase
+{
+ CCtrlEdit edt1sttime;
+ CCtrlSpin spinIdle;
+ CCtrlCombo cmbAAStatus;
+ CCtrlCheck chkShort, chkOnWindows, chkOnMiranda, chkScreenSaver, chkFullScreen, chkLocked;
+ CCtrlCheck chkPrivate, chkStatusLock, chkTerminal, chkSoundsOff, chkShortIdle;
+
+ void ShowHide()
+ {
+ BOOL bChecked = chkShort.GetState();
+ chkOnWindows.Enable(bChecked);
+ chkOnMiranda.Enable(bChecked);
+ edt1sttime.Enable(bChecked);
+
+ bChecked = chkShortIdle.GetState();
+ cmbAAStatus.Enable(bChecked);
+ chkStatusLock.Enable(bChecked);
+ }
+
+public:
+ COptionsDlg() :
+ CDlgBase(g_plugin, IDD_OPT_IDLE),
+ edt1sttime(this, IDC_IDLE1STTIME),
+ spinIdle(this, IDC_IDLESPIN, 60, 1),
+ cmbAAStatus(this, IDC_AASTATUS),
+ chkShort(this, IDC_IDLESHORT),
+ chkLocked(this, IDC_LOCKED),
+ chkPrivate(this, IDC_IDLEPRIVATE),
+ chkTerminal(this, IDC_IDLETERMINAL),
+ chkOnWindows(this, IDC_IDLEONWINDOWS),
+ chkSoundsOff(this, IDC_IDLESOUNDSOFF),
+ chkOnMiranda(this, IDC_IDLEONMIRANDA),
+ chkShortIdle(this, IDC_AASHORTIDLE),
+ chkStatusLock(this, IDC_IDLESTATUSLOCK),
+ chkFullScreen(this, IDC_FULLSCREEN),
+ chkScreenSaver(this, IDC_SCREENSAVER)
+ {
+ CreateLink(chkShort, g_plugin.bIdleCheck);
+ CreateLink(chkLocked, g_plugin.bIdleOnLock);
+ CreateLink(chkPrivate, g_plugin.bIdlePrivate);
+ CreateLink(chkTerminal, g_plugin.bIdleOnTerminal);
+ CreateLink(chkShortIdle, g_plugin.bAAEnable);
+ CreateLink(chkOnMiranda, g_plugin.bIdleMethod);
+ CreateLink(chkSoundsOff, g_plugin.bIdleSoundsOff);
+ CreateLink(chkStatusLock, g_plugin.bIdleStatusLock);
+ CreateLink(chkFullScreen, g_plugin.bIdleOnFullScr);
+ CreateLink(chkScreenSaver, g_plugin.bIdleOnSaver);
+
+ chkShortIdle.OnChange = chkShort.OnChange = Callback(this, &COptionsDlg::onChange);
+ }
+
+ bool OnInitDialog() override
+ {
+ chkOnWindows.SetState(!g_plugin.bIdleMethod);
+
+ spinIdle.SetPosition(g_plugin.iIdleTime1st);
+
+ for (auto &it : aa_Status)
+ cmbAAStatus.AddString(Clist_GetStatusModeDescription(it, 0));
+ cmbAAStatus.SetCurSel(IdleGetStatusIndex(g_plugin.bAAStatus));
+
+ ShowHide();
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ g_plugin.iIdleTime1st = spinIdle.GetPosition();
+
+ int curSel = cmbAAStatus.GetCurSel();
+ if (curSel != CB_ERR)
+ g_plugin.bAAStatus = aa_Status[curSel];
+
+ // destroy any current idle and reset settings.
+ IdleObject_Destroy();
+ IdleObject_Create();
+ return true;
+ }
+
+ void onChange(CCtrlCheck*)
+ {
+ ShowHide();
+ }
+};
+
+int IdleOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 100000000;
+ odp.szGroup.a = LPGEN("Status");
+ odp.szTitle.a = LPGEN("Idle");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new COptionsDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/core/stdautoaway/src/stdafx.h b/src/core/stdautoaway/src/stdafx.h index be6446162a..aed3748da5 100644 --- a/src/core/stdautoaway/src/stdafx.h +++ b/src/core/stdautoaway/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdautoaway/src/version.h b/src/core/stdautoaway/src/version.h index 50fcd17758..e85f99715c 100644 --- a/src/core/stdautoaway/src/version.h +++ b/src/core/stdautoaway/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for away state processing."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdAutoAway"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdaway/src/awaymsg.cpp b/src/core/stdaway/src/awaymsg.cpp index 94f097fbe1..391a61383e 100644 --- a/src/core/stdaway/src/awaymsg.cpp +++ b/src/core/stdaway/src/awaymsg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdaway/src/main.cpp b/src/core/stdaway/src/main.cpp index 1dfab72a34..bf275c259a 100644 --- a/src/core/stdaway/src/main.cpp +++ b/src/core/stdaway/src/main.cpp @@ -2,7 +2,7 @@ Standard away message processing module for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stdaway/src/options.cpp b/src/core/stdaway/src/options.cpp index 64943461d9..1287a623d3 100644 --- a/src/core/stdaway/src/options.cpp +++ b/src/core/stdaway/src/options.cpp @@ -1,140 +1,140 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static const int statusModes[] = -{ - ID_STATUS_OFFLINE, ID_STATUS_ONLINE, ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND, - ID_STATUS_FREECHAT, ID_STATUS_INVISIBLE, ID_STATUS_IDLE -}; - -class CAwayMsgOptsDlg : public CDlgBase -{ - struct - { - int ignore; - int noDialog; - int usePrevious; - wchar_t msg[1024]; - } - m_info[_countof(statusModes)]; - - int oldPage = -1; - - CCtrlEdit etdMsg; - CCtrlCombo cmbStatus; - CCtrlCheck chkDontReply, chkUsePrev, chkUseSpecific, chkNoDialog; - -public: - CAwayMsgOptsDlg() : - CDlgBase(g_plugin, IDD_OPT_AWAYMSG), - etdMsg(this, IDC_MSG), - cmbStatus(this, IDC_STATUS), - chkUsePrev(this, IDC_USEPREVIOUS), - chkNoDialog(this, IDC_NODIALOG), - chkDontReply(this, IDC_DONTREPLY), - chkUseSpecific(this, IDC_USESPECIFIC) - { - chkUsePrev.OnChange = chkDontReply.OnChange = chkUseSpecific.OnChange = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status); - cmbStatus.OnSelChanged = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status); - } - - bool OnInitDialog() override - { - for (auto &it : statusModes) { - if (!(protoModeMsgFlags & Proto_Status2Flag(it))) - continue; - - int j = cmbStatus.AddString(Clist_GetStatusModeDescription(it, 0), it); - m_info[j].ignore = g_plugin.GetStatusModeByte(it, "Ignore"); - m_info[j].noDialog = g_plugin.GetStatusModeByte(it, "NoDlg", true); - m_info[j].usePrevious = g_plugin.GetStatusModeByte(it, "UsePrev"); - - DBVARIANT dbv; - if (g_plugin.getWString(StatusModeToDbSetting(it, "Default"), &dbv)) - if (g_plugin.getWString(StatusModeToDbSetting(it, "Msg"), &dbv)) - dbv.pwszVal = mir_wstrdup(GetDefaultMessage(it)); - mir_wstrcpy(m_info[j].msg, dbv.pwszVal); - mir_free(dbv.pwszVal); - } - - cmbStatus.SetCurSel(0); - onSelChange_Status(0); - return true; - } - - bool OnApply() override - { - onSelChange_Status(0); - - for (int i = cmbStatus.GetCount() - 1; i >= 0; i--) { - int status = cmbStatus.GetItemData(i); - g_plugin.SetStatusModeByte(status, "Ignore", (uint8_t)m_info[i].ignore); - g_plugin.SetStatusModeByte(status, "NoDlg", (uint8_t)m_info[i].noDialog); - g_plugin.SetStatusModeByte(status, "UsePrev", (uint8_t)m_info[i].usePrevious); - g_plugin.setWString(StatusModeToDbSetting(status, "Default"), m_info[i].msg); - } - return true; - } - - void onSelChange_Status(CCtrlCombo*) - { - if (oldPage != -1) { - m_info[oldPage].ignore = chkDontReply.GetState(); - m_info[oldPage].noDialog = chkNoDialog.GetState(); - m_info[oldPage].usePrevious = chkUsePrev.GetState(); - etdMsg.GetText(m_info[oldPage].msg, _countof(m_info[oldPage].msg)); - } - - int i = cmbStatus.GetCurSel(); - chkDontReply.SetState(i < 0 ? 0 : m_info[i].ignore); - chkNoDialog.SetState(i < 0 ? 0 : m_info[i].noDialog); - chkUsePrev.SetState(i < 0 ? 0 : m_info[i].usePrevious); - chkUseSpecific.SetState(i < 0 ? 0 : !m_info[i].usePrevious); - - etdMsg.SetText(i < 0 ? L"" : m_info[i].msg); - - chkNoDialog.Enable(i < 0 ? 0 : !m_info[i].ignore); - chkUsePrev.Enable(i < 0 ? 0 : !m_info[i].ignore); - chkUseSpecific.Enable(i < 0 ? 0 : !m_info[i].ignore); - etdMsg.Enable(i < 0 ? 0 : !(m_info[i].ignore || m_info[i].usePrevious)); - oldPage = i; - } -}; - -int AwayMsgOptInitialise(WPARAM wParam, LPARAM) -{ - if (protoModeMsgFlags == 0) - return 0; - - OPTIONSDIALOGPAGE odp = {}; - odp.flags = ODPF_BOLDGROUPS; - odp.position = 870000000; - odp.szTitle.a = LPGEN("Status messages"); - odp.szGroup.a = LPGEN("Status"); - odp.pDialog = new CAwayMsgOptsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static const int statusModes[] =
+{
+ ID_STATUS_OFFLINE, ID_STATUS_ONLINE, ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND,
+ ID_STATUS_FREECHAT, ID_STATUS_INVISIBLE, ID_STATUS_IDLE
+};
+
+class CAwayMsgOptsDlg : public CDlgBase
+{
+ struct
+ {
+ int ignore;
+ int noDialog;
+ int usePrevious;
+ wchar_t msg[1024];
+ }
+ m_info[_countof(statusModes)];
+
+ int oldPage = -1;
+
+ CCtrlEdit etdMsg;
+ CCtrlCombo cmbStatus;
+ CCtrlCheck chkDontReply, chkUsePrev, chkUseSpecific, chkNoDialog;
+
+public:
+ CAwayMsgOptsDlg() :
+ CDlgBase(g_plugin, IDD_OPT_AWAYMSG),
+ etdMsg(this, IDC_MSG),
+ cmbStatus(this, IDC_STATUS),
+ chkUsePrev(this, IDC_USEPREVIOUS),
+ chkNoDialog(this, IDC_NODIALOG),
+ chkDontReply(this, IDC_DONTREPLY),
+ chkUseSpecific(this, IDC_USESPECIFIC)
+ {
+ chkUsePrev.OnChange = chkDontReply.OnChange = chkUseSpecific.OnChange = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status);
+ cmbStatus.OnSelChanged = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status);
+ }
+
+ bool OnInitDialog() override
+ {
+ for (auto &it : statusModes) {
+ if (!(protoModeMsgFlags & Proto_Status2Flag(it)))
+ continue;
+
+ int j = cmbStatus.AddString(Clist_GetStatusModeDescription(it, 0), it);
+ m_info[j].ignore = g_plugin.GetStatusModeByte(it, "Ignore");
+ m_info[j].noDialog = g_plugin.GetStatusModeByte(it, "NoDlg", true);
+ m_info[j].usePrevious = g_plugin.GetStatusModeByte(it, "UsePrev");
+
+ DBVARIANT dbv;
+ if (g_plugin.getWString(StatusModeToDbSetting(it, "Default"), &dbv))
+ if (g_plugin.getWString(StatusModeToDbSetting(it, "Msg"), &dbv))
+ dbv.pwszVal = mir_wstrdup(GetDefaultMessage(it));
+ mir_wstrcpy(m_info[j].msg, dbv.pwszVal);
+ mir_free(dbv.pwszVal);
+ }
+
+ cmbStatus.SetCurSel(0);
+ onSelChange_Status(0);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ onSelChange_Status(0);
+
+ for (int i = cmbStatus.GetCount() - 1; i >= 0; i--) {
+ int status = cmbStatus.GetItemData(i);
+ g_plugin.SetStatusModeByte(status, "Ignore", (uint8_t)m_info[i].ignore);
+ g_plugin.SetStatusModeByte(status, "NoDlg", (uint8_t)m_info[i].noDialog);
+ g_plugin.SetStatusModeByte(status, "UsePrev", (uint8_t)m_info[i].usePrevious);
+ g_plugin.setWString(StatusModeToDbSetting(status, "Default"), m_info[i].msg);
+ }
+ return true;
+ }
+
+ void onSelChange_Status(CCtrlCombo*)
+ {
+ if (oldPage != -1) {
+ m_info[oldPage].ignore = chkDontReply.GetState();
+ m_info[oldPage].noDialog = chkNoDialog.GetState();
+ m_info[oldPage].usePrevious = chkUsePrev.GetState();
+ etdMsg.GetText(m_info[oldPage].msg, _countof(m_info[oldPage].msg));
+ }
+
+ int i = cmbStatus.GetCurSel();
+ chkDontReply.SetState(i < 0 ? 0 : m_info[i].ignore);
+ chkNoDialog.SetState(i < 0 ? 0 : m_info[i].noDialog);
+ chkUsePrev.SetState(i < 0 ? 0 : m_info[i].usePrevious);
+ chkUseSpecific.SetState(i < 0 ? 0 : !m_info[i].usePrevious);
+
+ etdMsg.SetText(i < 0 ? L"" : m_info[i].msg);
+
+ chkNoDialog.Enable(i < 0 ? 0 : !m_info[i].ignore);
+ chkUsePrev.Enable(i < 0 ? 0 : !m_info[i].ignore);
+ chkUseSpecific.Enable(i < 0 ? 0 : !m_info[i].ignore);
+ etdMsg.Enable(i < 0 ? 0 : !(m_info[i].ignore || m_info[i].usePrevious));
+ oldPage = i;
+ }
+};
+
+int AwayMsgOptInitialise(WPARAM wParam, LPARAM)
+{
+ if (protoModeMsgFlags == 0)
+ return 0;
+
+ OPTIONSDIALOGPAGE odp = {};
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.position = 870000000;
+ odp.szTitle.a = LPGEN("Status messages");
+ odp.szGroup.a = LPGEN("Status");
+ odp.pDialog = new CAwayMsgOptsDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/core/stdaway/src/sendmsg.cpp b/src/core/stdaway/src/sendmsg.cpp index 1eaaac5cc4..dff644a928 100644 --- a/src/core/stdaway/src/sendmsg.cpp +++ b/src/core/stdaway/src/sendmsg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdaway/src/stdafx.h b/src/core/stdaway/src/stdafx.h index 4cd3dd5b24..83d5a859c9 100644 --- a/src/core/stdaway/src/stdafx.h +++ b/src/core/stdaway/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdaway/src/version.h b/src/core/stdaway/src/version.h index 1cbc94e44c..03131a5179 100644 --- a/src/core/stdaway/src/version.h +++ b/src/core/stdaway/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for the away messages processing."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdAway"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdclist/src/clc.h b/src/core/stdclist/src/clc.h index 9ecb9767af..c5a6581b38 100644 --- a/src/core/stdclist/src/clc.h +++ b/src/core/stdclist/src/clc.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/clcfonts.cpp b/src/core/stdclist/src/clcfonts.cpp index c5c4af1a9a..e435026332 100644 --- a/src/core/stdclist/src/clcfonts.cpp +++ b/src/core/stdclist/src/clcfonts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/clcopts.cpp b/src/core/stdclist/src/clcopts.cpp index 688fba0c86..077cd80ca0 100644 --- a/src/core/stdclist/src/clcopts.cpp +++ b/src/core/stdclist/src/clcopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/clcpaint.cpp b/src/core/stdclist/src/clcpaint.cpp index 9bf6af7581..d2e59b1878 100644 --- a/src/core/stdclist/src/clcpaint.cpp +++ b/src/core/stdclist/src/clcpaint.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/clistmenus.cpp b/src/core/stdclist/src/clistmenus.cpp index 7d10154fde..57da5b4f22 100644 --- a/src/core/stdclist/src/clistmenus.cpp +++ b/src/core/stdclist/src/clistmenus.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/clistopts.cpp b/src/core/stdclist/src/clistopts.cpp index 29683b6506..3a6f435e24 100644 --- a/src/core/stdclist/src/clistopts.cpp +++ b/src/core/stdclist/src/clistopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/cluiopts.cpp b/src/core/stdclist/src/cluiopts.cpp index 785c0605b1..4816b59b7f 100644 --- a/src/core/stdclist/src/cluiopts.cpp +++ b/src/core/stdclist/src/cluiopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/contact.cpp b/src/core/stdclist/src/contact.cpp index daba1cdc61..cf9ba02cb7 100644 --- a/src/core/stdclist/src/contact.cpp +++ b/src/core/stdclist/src/contact.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/init.cpp b/src/core/stdclist/src/init.cpp index 3ca412c5f7..f379863284 100644 --- a/src/core/stdclist/src/init.cpp +++ b/src/core/stdclist/src/init.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/stdafx.cxx b/src/core/stdclist/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/src/core/stdclist/src/stdafx.cxx +++ b/src/core/stdclist/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/core/stdclist/src/stdafx.h b/src/core/stdclist/src/stdafx.h index d50af5e471..62b233fe55 100644 --- a/src/core/stdclist/src/stdafx.h +++ b/src/core/stdclist/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdclist/src/version.h b/src/core/stdclist/src/version.h index 1b178cd258..a652e79523 100644 --- a/src/core/stdclist/src/version.h +++ b/src/core/stdclist/src/version.h @@ -9,4 +9,4 @@ #define __DESCRIPTION "Core module for displaying contacts."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdClist"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdcrypt/src/encrypt.cpp b/src/core/stdcrypt/src/encrypt.cpp index e9689aa374..9d605848a2 100644 --- a/src/core/stdcrypt/src/encrypt.cpp +++ b/src/core/stdcrypt/src/encrypt.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdcrypt/src/main.cpp b/src/core/stdcrypt/src/main.cpp index bc53c00f54..e1dc9218a6 100644 --- a/src/core/stdcrypt/src/main.cpp +++ b/src/core/stdcrypt/src/main.cpp @@ -1,7 +1,7 @@ /*
Standard encryption plugin for Miranda NG
-Copyright (C) 2012-22 George Hazan
+Copyright (C) 2012-23 George Hazan
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stdcrypt/src/stdafx.h b/src/core/stdcrypt/src/stdafx.h index 1ea23e6c16..4b6c1e7319 100644 --- a/src/core/stdcrypt/src/stdafx.h +++ b/src/core/stdcrypt/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdcrypt/src/stdcrypt.h b/src/core/stdcrypt/src/stdcrypt.h index 791e827d1a..3966a81631 100644 --- a/src/core/stdcrypt/src/stdcrypt.h +++ b/src/core/stdcrypt/src/stdcrypt.h @@ -1,7 +1,7 @@ /*
Standard encryption plugin for Miranda NG
-Copyright (C) 2012-22 George Hazan
+Copyright (C) 2012-23 George Hazan
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stdcrypt/src/utils.cpp b/src/core/stdcrypt/src/utils.cpp index fd5b3cff22..a3279e805e 100644 --- a/src/core/stdcrypt/src/utils.cpp +++ b/src/core/stdcrypt/src/utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdcrypt/src/version.h b/src/core/stdcrypt/src/version.h index 6f6d4f9c96..30de347877 100644 --- a/src/core/stdcrypt/src/version.h +++ b/src/core/stdcrypt/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for encryption."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdCrypt"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdemail/src/email.cpp b/src/core/stdemail/src/email.cpp index b6c68d672f..0f40657fff 100644 --- a/src/core/stdemail/src/email.cpp +++ b/src/core/stdemail/src/email.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdemail/src/main.cpp b/src/core/stdemail/src/main.cpp index 344e9501a9..19aaf95380 100644 --- a/src/core/stdemail/src/main.cpp +++ b/src/core/stdemail/src/main.cpp @@ -2,7 +2,7 @@ Standard e-mail urls launcher for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stdemail/src/stdafx.h b/src/core/stdemail/src/stdafx.h index 8214c32bfe..4520b2f62f 100644 --- a/src/core/stdemail/src/stdafx.h +++ b/src/core/stdemail/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdemail/src/version.h b/src/core/stdemail/src/version.h index a158424f02..dd12d45a66 100644 --- a/src/core/stdemail/src/version.h +++ b/src/core/stdemail/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for e-mail urls handling."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdEmail"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdfile/src/file.cpp b/src/core/stdfile/src/file.cpp index 8dcb922619..234f84404f 100644 --- a/src/core/stdfile/src/file.cpp +++ b/src/core/stdfile/src/file.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/file.h b/src/core/stdfile/src/file.h index b89671ab8a..2bdddb04e5 100644 --- a/src/core/stdfile/src/file.h +++ b/src/core/stdfile/src/file.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/fileexistsdlg.cpp b/src/core/stdfile/src/fileexistsdlg.cpp index bd715cfa24..c2e4f6dfa8 100644 --- a/src/core/stdfile/src/fileexistsdlg.cpp +++ b/src/core/stdfile/src/fileexistsdlg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/fileopts.cpp b/src/core/stdfile/src/fileopts.cpp index f5385174e1..f0d9e1bda2 100644 --- a/src/core/stdfile/src/fileopts.cpp +++ b/src/core/stdfile/src/fileopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/filerecvdlg.cpp b/src/core/stdfile/src/filerecvdlg.cpp index 54aefe44c5..7a364e210e 100644 --- a/src/core/stdfile/src/filerecvdlg.cpp +++ b/src/core/stdfile/src/filerecvdlg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/filesenddlg.cpp b/src/core/stdfile/src/filesenddlg.cpp index 017725e89d..024cf68129 100644 --- a/src/core/stdfile/src/filesenddlg.cpp +++ b/src/core/stdfile/src/filesenddlg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/filexferdlg.cpp b/src/core/stdfile/src/filexferdlg.cpp index bcd6c16c54..32ef89ecf3 100644 --- a/src/core/stdfile/src/filexferdlg.cpp +++ b/src/core/stdfile/src/filexferdlg.cpp @@ -1,765 +1,765 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include <io.h> -#include "file.h" - -static int CheckVirusScanned(HWND hwnd, FileDlgData *dat, int i) -{ - if (dat->send) return 1; - if (dat->fileVirusScanned == nullptr) return 0; - if (dat->fileVirusScanned[i]) return 1; - if (g_plugin.getByte("WarnBeforeOpening", 1) == 0) return 1; - - return IDYES == MessageBox(hwnd, - TranslateT("This file has not yet been scanned for viruses. Are you certain you want to open it?"), - TranslateT("File received"), - MB_YESNO | MB_DEFBUTTON2); -} - -#define M_VIRUSSCANDONE (WM_USER+100) -struct virusscanthreadstartinfo -{ - wchar_t *szFile; - int returnCode; - HWND hwndReply; -}; - -wchar_t* PFTS_StringToTchar(int flags, const wchar_t *s) -{ - if (flags & PFTS_UTF) - return mir_utf8decodeW((char*)s); - if (flags & PFTS_UNICODE) - return mir_wstrdup(s); - return mir_a2u((char*)s); -} - -int PFTS_CompareWithTchar(PROTOFILETRANSFERSTATUS *ft, const wchar_t *s, wchar_t *r) -{ - if (ft->flags & PFTS_UTF) { - wchar_t *ts = mir_utf8decodeW((char*)s); - int res = mir_wstrcmp(ts, r); - mir_free(ts); - return res; - } - if (ft->flags & PFTS_UNICODE) - return mir_wstrcmp(s, r); - - wchar_t *ts = mir_a2u((char*)s); - int res = mir_wstrcmp(ts, r); - mir_free(ts); - return res; -} - -static void SetOpenFileButtonStyle(HWND hwndButton, int enabled) -{ - EnableWindow(hwndButton, enabled); -} - -void FillSendData(FileDlgData *dat, DBEVENTINFO &dbei) -{ - dbei.szModule = Proto_GetBaseAccountName(dat->hContact); - dbei.eventType = EVENTTYPE_FILE; - dbei.flags = DBEF_SENT; - dbei.timestamp = time(0); - char *szFileNames = mir_utf8encodeW(dat->szFilenames), *szMsg = mir_utf8encodeW(dat->szMsg); - dbei.flags |= DBEF_UTF; - - dbei.cbBlob = int(sizeof(uint32_t) + mir_strlen(szFileNames) + mir_strlen(szMsg) + 2); - dbei.pBlob = (uint8_t*)mir_alloc(dbei.cbBlob); - *(PDWORD)dbei.pBlob = 0; - mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t), szFileNames); - mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t) + mir_strlen(szFileNames) + 1, szMsg); - - mir_free(szFileNames), mir_free(szMsg); -} - -static void __cdecl RunVirusScannerThread(virusscanthreadstartinfo *info) -{ - DBVARIANT dbv; - if (!g_plugin.getWString("ScanCmdLine", &dbv)) { - if (dbv.pwszVal[0]) { - STARTUPINFO si = { 0 }; - si.cb = sizeof(si); - wchar_t *pszReplace = wcsstr(dbv.pwszVal, L"%f"); - wchar_t szCmdLine[768]; - if (pszReplace) { - if (info->szFile[mir_wstrlen(info->szFile) - 1] == '\\') - info->szFile[mir_wstrlen(info->szFile) - 1] = '\0'; - *pszReplace = 0; - mir_snwprintf(szCmdLine, L"%s\"%s\"%s", dbv.pwszVal, info->szFile, pszReplace + 2); - } - else - wcsncpy_s(szCmdLine, dbv.pwszVal, _TRUNCATE); - - PROCESS_INFORMATION pi; - if (CreateProcess(nullptr, szCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { - if (WaitForSingleObject(pi.hProcess, 3600 * 1000) == WAIT_OBJECT_0) - PostMessage(info->hwndReply, M_VIRUSSCANDONE, info->returnCode, 0); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } - } - db_free(&dbv); - } - mir_free(info->szFile); - mir_free(info); -} - -static void SetFilenameControls(HWND hwndDlg, FileDlgData *dat, PROTOFILETRANSFERSTATUS *fts) -{ - wchar_t msg[MAX_PATH]; - wchar_t *fnbuf = nullptr, *fn = nullptr; - SHFILEINFO shfi = {}; - - if (fts->szCurrentFile.w) { - fnbuf = mir_wstrdup(fts->szCurrentFile.w); - if ((fn = wcsrchr(fnbuf, '\\')) == nullptr) - fn = fnbuf; - else fn++; - } - - if (dat->hIcon) DestroyIcon(dat->hIcon); dat->hIcon = nullptr; - - if (fn && (fts->totalFiles > 1)) { - mir_snwprintf(msg, L"%s: %s (%d %s %d)", Clist_GetContactDisplayName(fts->hContact), fn, fts->currentFileNumber + 1, TranslateT("of"), fts->totalFiles); - - SHGetFileInfo(fn, FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); - dat->hIcon = shfi.hIcon; - } - else if (fn) { - mir_snwprintf(msg, L"%s: %s", Clist_GetContactDisplayName(fts->hContact), fn); - - SHGetFileInfo(fn, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); - dat->hIcon = shfi.hIcon; - } - else { - mir_wstrncpy(msg, Clist_GetContactDisplayName(fts->hContact), _countof(msg)); - HICON hIcon = Skin_LoadIcon(SKINICON_OTHER_DOWNARROW); - dat->hIcon = CopyIcon(hIcon); - IcoLib_ReleaseIcon(hIcon, NULL); - } - - mir_free(fnbuf); - - SendDlgItemMessage(hwndDlg, IDC_FILEICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIcon); - SetDlgItemText(hwndDlg, IDC_CONTACTNAME, msg); -} - -enum { FTS_TEXT, FTS_PROGRESS, FTS_OPEN }; -static void SetFtStatus(HWND hwndDlg, wchar_t *text, int mode) -{ - SetDlgItemText(hwndDlg, IDC_STATUS, TranslateW(text)); - SetDlgItemText(hwndDlg, IDC_TRANSFERCOMPLETED, TranslateW(text)); - - ShowWindow(GetDlgItem(hwndDlg, IDC_STATUS), (mode == FTS_TEXT) ? SW_SHOW : SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), (mode == FTS_PROGRESS) ? SW_SHOW : SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_TRANSFERCOMPLETED), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_FILEICON), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE); -} - -static void HideProgressControls(HWND hwndDlg) -{ - RECT rc; - char buf[64]; - - GetWindowRect(GetDlgItem(hwndDlg, IDC_ALLPRECENTS), &rc); - MapWindowPoints(nullptr, hwndDlg, (LPPOINT)&rc, 2); - SetWindowPos(hwndDlg, nullptr, 0, 0, 100, rc.bottom + 3, SWP_NOMOVE | SWP_NOZORDER); - ShowWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), SW_HIDE); - - _strtime(buf); - SetDlgItemTextA(hwndDlg, IDC_ALLPRECENTS, buf); - - PostMessage(GetParent(hwndDlg), WM_FT_RESIZE, 0, (LPARAM)hwndDlg); -} - -static int FileTransferDlgResizer(HWND, LPARAM param, UTILRESIZECONTROL *urc) -{ - auto *dat = (FileDlgData *)param; - - switch (urc->wId) { - case IDC_CONTACTNAME: - case IDC_STATUS: - case IDC_ALLFILESPROGRESS: - case IDC_TRANSFERCOMPLETED: - return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; - - case IDC_FRAME: - return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; - case IDC_ALLPRECENTS: - case IDCANCEL: - case IDC_OPENFILE: - case IDC_OPENFOLDER: - return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; - - case IDC_ALLTRANSFERRED: - if (dat->waitingForAcceptance) - return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; - - urc->rcItem.right = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx) / 3; - return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP; - - case IDC_ALLSPEED: - if (dat->waitingForAcceptance) - return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; - - urc->rcItem.right = urc->rcItem.right - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx; - urc->rcItem.left = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left) / 3; - return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -INT_PTR CALLBACK DlgProcFileTransfer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - FileDlgData *dat = (FileDlgData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - dat = (FileDlgData *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - dat->hNotifyEvent = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_RECVEVENT); - dat->transferStatus.currentFileNumber = -1; - if (dat->send) { - if (db_mc_isMeta(dat->hContact)) - dat->hContact = db_mc_getMostOnline(dat->hContact); - dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILE, (WPARAM)dat->szMsg, (LPARAM)dat->files); - SetFtStatus(hwndDlg, LPGENW("Request sent, waiting for acceptance..."), FTS_TEXT); - SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); - dat->waitingForAcceptance = 1; - // hide "open" button since it may cause potential access violations... - ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFILE), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFOLDER), SW_HIDE); - } - else { //recv - CreateDirectoryTreeW(dat->szSavePath); - dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILEALLOW, (WPARAM)dat->fs, (LPARAM)dat->szSavePath); - dat->transferStatus.szWorkingDir.w = mir_wstrdup(dat->szSavePath); - if (!Contact::OnList(dat->hContact)) - dat->resumeBehaviour = FILERESUME_ASK; - else - dat->resumeBehaviour = g_plugin.getByte("IfExists", FILERESUME_ASK); - SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); - } - - /* check we actually got an fs handle back from the protocol */ - if (!dat->fs) { - SetFtStatus(hwndDlg, LPGENW("Unable to initiate transfer."), FTS_TEXT); - dat->waitingForAcceptance = 0; - } - { - LOGFONT lf; - HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0); - GetObject(hFont, sizeof(lf), &lf); - lf.lfWeight = FW_BOLD; - hFont = CreateFontIndirect(&lf); - SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_SETFONT, (WPARAM)hFont, 0); - - SHFILEINFO shfi = {}; - SHGetFileInfo(L"", FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); - dat->hIconFolder = shfi.hIcon; - } - dat->hIcon = nullptr; - { - char *szProto = Proto_GetBaseAccountName(dat->hContact); - uint16_t status = db_get_w(dat->hContact, szProto, "Status", ID_STATUS_ONLINE); - SendDlgItemMessage(hwndDlg, IDC_CONTACT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadProtoIcon(szProto, status)); - } - - SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Contact menu"), 0); - SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONSETASFLATBTN, TRUE, 0); - - Button_SetSkin_IcoLib(hwndDlg, IDC_OPENFILE, SKINICON_OTHER_DOWNARROW, LPGEN("Open...")); - SendDlgItemMessage(hwndDlg, IDC_OPENFILE, BUTTONSETASPUSHBTN, TRUE, 0); - - SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIconFolder); - SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open folder"), 0); - SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONSETASFLATBTN, TRUE, 0); - - Button_SetSkin_IcoLib(hwndDlg, IDCANCEL, SKINICON_OTHER_DELETE, LPGEN("Cancel")); - - SetDlgItemText(hwndDlg, IDC_CONTACTNAME, Clist_GetContactDisplayName(dat->hContact)); - - if (!dat->waitingForAcceptance) - SetTimer(hwndDlg, 1, 1000, nullptr); - return TRUE; - - case WM_TIMER: - memmove(dat->bytesRecvedHistory + 1, dat->bytesRecvedHistory, sizeof(dat->bytesRecvedHistory) - sizeof(dat->bytesRecvedHistory[0])); - dat->bytesRecvedHistory[0] = dat->transferStatus.totalProgress; - if (dat->bytesRecvedHistorySize < _countof(dat->bytesRecvedHistory)) - dat->bytesRecvedHistorySize++; - - wchar_t szSpeed[32], szTime[32], szDisplay[96]; - SYSTEMTIME st; - ULARGE_INTEGER li; - FILETIME ft; - - GetSensiblyFormattedSize((dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) / dat->bytesRecvedHistorySize, szSpeed, _countof(szSpeed), 0, 1, NULL); - if (dat->bytesRecvedHistory[0] == dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) - mir_wstrcpy(szTime, L"??:??:??"); - else { - li.QuadPart = 10000000ll * (dat->transferStatus.currentFileSize - dat->transferStatus.currentFileProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]); - ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart; - FileTimeToSystemTime(&ft, &st); - GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime)); - } - if (dat->bytesRecvedHistory[0] != dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) { - li.QuadPart = 10000000ll * (dat->transferStatus.totalBytes - dat->transferStatus.totalProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]); - ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart; - FileTimeToSystemTime(&ft, &st); - GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime)); - } - - mir_snwprintf(szDisplay, L"%s/%s (%s %s)", szSpeed, TranslateT("sec"), szTime, TranslateT("remaining")); - SetDlgItemText(hwndDlg, IDC_ALLSPEED, szDisplay); - break; - - case WM_MEASUREITEM: - return Menu_MeasureItem(lParam); - - case WM_DRAWITEM: - return Menu_DrawItem(lParam); - - case WM_FT_CLEANUP: - if (!dat->fs) { - PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg); - DestroyWindow(hwndDlg); - } - break; - - case WM_COMMAND: - if (!dat) - break; - - if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, dat->hContact)) - break; - - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg); - DestroyWindow(hwndDlg); - break; - - case IDC_CONTACT: - { - RECT rc; - HMENU hMenu = Menu_BuildContactMenu(dat->hContact); - GetWindowRect((HWND)lParam, &rc); - TrackPopupMenu(hMenu, 0, rc.left, rc.bottom, 0, hwndDlg, NULL); - DestroyMenu(hMenu); - } - break; - - case IDC_TRANSFERCOMPLETED: - if (dat->transferStatus.currentFileNumber <= 1 && CheckVirusScanned(hwndDlg, dat, 0)) { - ShellExecute(NULL, NULL, dat->files[0], NULL, NULL, SW_SHOW); - break; - } - - case IDC_OPENFOLDER: - { - wchar_t *path = dat->transferStatus.szWorkingDir.w; - if (!path || !path[0]) { - path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w); - wchar_t *p = wcsrchr(path, '\\'); if (p) *p = 0; - } - - if (path) ShellExecute(NULL, L"open", path, NULL, NULL, SW_SHOW); - } - break; - - case IDC_OPENFILE: - wchar_t **files; - if (dat->send) { - if (dat->files == nullptr) - files = dat->transferStatus.pszFiles.w; - else - files = dat->files; - } - else files = dat->files; - - HMENU hMenu = CreatePopupMenu(); - AppendMenu(hMenu, MF_STRING, 1, TranslateT("Open folder")); - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - - if (files && *files) { - int limit; - wchar_t *pszFilename, *pszNewFileName; - - if (dat->send) - limit = dat->transferStatus.totalFiles; - else - limit = dat->transferStatus.currentFileNumber; - - // Loop over all transfered files and add them to the menu - for (int i = 0; i < limit; i++) { - pszFilename = wcsrchr(files[i], '\\'); - if (pszFilename == nullptr) - pszFilename = files[i]; - else - pszFilename++; - - if (pszFilename) { - size_t cbFileNameLen = mir_wstrlen(pszFilename); - - pszNewFileName = (wchar_t*)mir_alloc(cbFileNameLen * 2 * sizeof(wchar_t)); - wchar_t *p = pszNewFileName; - for (size_t pszlen = 0; pszlen < cbFileNameLen; pszlen++) { - *p++ = pszFilename[pszlen]; - if (pszFilename[pszlen] == '&') - *p++ = '&'; - } - *p = '\0'; - AppendMenu(hMenu, MF_STRING, i + 10, pszNewFileName); - mir_free(pszNewFileName); - } - } - } - - RECT rc; - GetWindowRect((HWND)lParam, &rc); - CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_CHECKED); - int ret = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_RIGHTALIGN, rc.right, rc.bottom, 0, hwndDlg, nullptr); - CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_UNCHECKED); - DestroyMenu(hMenu); - - if (ret == 1) { - wchar_t *path = dat->transferStatus.szWorkingDir.w; - if (!path || !path[0]) { - path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w); - wchar_t *p = wcsrchr(path, '\\'); - if (p) - *p = 0; - } - - if (path) ShellExecute(nullptr, L"open", path, nullptr, nullptr, SW_SHOW); - } - else if (ret && CheckVirusScanned(hwndDlg, dat, ret)) - ShellExecute(nullptr, nullptr, files[ret - 10], nullptr, nullptr, SW_SHOW); - } - break; - - case M_FILEEXISTSDLGREPLY: - EnableWindow(hwndDlg, TRUE); - { - PROTOFILERESUME *pfr = (PROTOFILERESUME *)lParam; - wchar_t *szOriginalFilename = (wchar_t *)wParam; - char *szProto = Proto_GetBaseAccountName(dat->hContact); - - switch (pfr->action) { - case FILERESUME_CANCEL: - if (dat->fs) ProtoChainSend(dat->hContact, PSS_FILECANCEL, (WPARAM)dat->fs, 0); - dat->fs = nullptr; - mir_free(szOriginalFilename); - if (pfr->szFilename) mir_free((char *)pfr->szFilename); - mir_free(pfr); - return 0; - case FILERESUME_RESUMEALL: - case FILERESUME_OVERWRITEALL: - dat->resumeBehaviour = pfr->action; - pfr->action &= ~FILERESUMEF_ALL; - break; - case FILERESUME_RENAMEALL: - pfr->action = FILERESUME_RENAME; - { - wchar_t *pszExtension, *pszFilename; - if ((pszFilename = wcsrchr(szOriginalFilename, '\\')) == nullptr) pszFilename = szOriginalFilename; - if ((pszExtension = wcsrchr(pszFilename + 1, '.')) == nullptr) pszExtension = pszFilename + mir_wstrlen(pszFilename); - if (pfr->szFilename) mir_free((wchar_t *)pfr->szFilename); - size_t size = (pszExtension - szOriginalFilename) + 21 + mir_wstrlen(pszExtension); - pfr->szFilename = (wchar_t *)mir_alloc(sizeof(wchar_t) * size); - for (int i = 1;; i++) { - mir_snwprintf((wchar_t *)pfr->szFilename, size, L"%.*s (%u)%s", pszExtension - szOriginalFilename, szOriginalFilename, i, pszExtension); - if (_waccess(pfr->szFilename, 0) != 0) - break; - } - } - break; - } - mir_free(szOriginalFilename); - CallProtoService(szProto, PS_FILERESUME, (WPARAM)dat->fs, (LPARAM)pfr); - delete pfr; - } - break; - - case HM_RECVEVENT: - { - ACKDATA *ack = (ACKDATA *)lParam; - if (ack->hProcess != dat->fs) break; - if (ack->type != ACKTYPE_FILE) break; - if (ack->hContact != dat->hContact) break; - - if (dat->waitingForAcceptance) { - SetTimer(hwndDlg, 1, 1000, nullptr); - dat->waitingForAcceptance = 0; - } - - switch (ack->result) { - case ACKRESULT_SENTREQUEST: SetFtStatus(hwndDlg, LPGENW("Decision sent"), FTS_TEXT); break; - case ACKRESULT_CONNECTING: SetFtStatus(hwndDlg, LPGENW("Connecting..."), FTS_TEXT); break; - case ACKRESULT_CONNECTPROXY: SetFtStatus(hwndDlg, LPGENW("Connecting to proxy..."), FTS_TEXT); break; - case ACKRESULT_CONNECTED: SetFtStatus(hwndDlg, LPGENW("Connected"), FTS_TEXT); break; - case ACKRESULT_LISTENING: SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); break; - case ACKRESULT_INITIALISING: SetFtStatus(hwndDlg, LPGENW("Initializing..."), FTS_TEXT); break; - case ACKRESULT_NEXTFILE: - SetFtStatus(hwndDlg, LPGENW("Moving to next file..."), FTS_TEXT); - SetDlgItemTextA(hwndDlg, IDC_FILENAME, ""); - if (dat->transferStatus.currentFileNumber == 1 && dat->transferStatus.totalFiles > 1 && !dat->send) - SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); - if (dat->transferStatus.currentFileNumber != -1 && dat->files && !dat->send && g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE) == VIRUSSCAN_DURINGDL) { - if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY) - PostMessage(hwndDlg, M_VIRUSSCANDONE, dat->transferStatus.currentFileNumber, 0); - else { - virusscanthreadstartinfo *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo)); - vstsi->hwndReply = hwndDlg; - vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]); - vstsi->returnCode = dat->transferStatus.currentFileNumber; - mir_forkThread<virusscanthreadstartinfo>(RunVirusScannerThread, vstsi); - } - } - break; - - case ACKRESULT_FILERESUME: - UpdateProtoFileTransferStatus(&dat->transferStatus, (PROTOFILETRANSFERSTATUS *)ack->lParam); - { - PROTOFILETRANSFERSTATUS *fts = &dat->transferStatus; - SetFilenameControls(hwndDlg, dat, fts); - if (_waccess(fts->szCurrentFile.w, 0)) - break; - - SetFtStatus(hwndDlg, LPGENW("File already exists"), FTS_TEXT); - if (dat->resumeBehaviour == FILERESUME_ASK) { - TDlgProcFileExistsParam param = { hwndDlg, fts }; - ShowWindow(hwndDlg, SW_SHOWNORMAL); - CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILEEXISTS), hwndDlg, DlgProcFileExists, (LPARAM)¶m); - EnableWindow(hwndDlg, FALSE); - } - else { - PROTOFILERESUME *pfr = new PROTOFILERESUME(); - pfr->action = dat->resumeBehaviour; - pfr->szFilename = nullptr; - PostMessage(hwndDlg, M_FILEEXISTSDLGREPLY, (WPARAM)mir_wstrdup(fts->szCurrentFile.w), (LPARAM)pfr); - } - } - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 1); - return TRUE; - - case ACKRESULT_DATA: - { - PROTOFILETRANSFERSTATUS *fts = (PROTOFILETRANSFERSTATUS *)ack->lParam; - wchar_t str[64], str2[64], szSizeDone[32], szSizeTotal[32];//, *contactName; - - if (dat->fileVirusScanned == nullptr) - dat->fileVirusScanned = (int *)mir_calloc(sizeof(int) * fts->totalFiles); - - // This needs to be here - otherwise we get holes in the files array - if (!dat->send) { - if (dat->files == nullptr) - dat->files = (wchar_t **)mir_calloc((fts->totalFiles + 1) * sizeof(wchar_t *)); - if (fts->currentFileNumber < fts->totalFiles && dat->files[fts->currentFileNumber] == nullptr) - dat->files[fts->currentFileNumber] = PFTS_StringToTchar(fts->flags, fts->szCurrentFile.w); - } - - /* HACK: for 0.3.3, limit updates to around 1.1 ack per second */ - if (fts->totalProgress != fts->totalBytes && GetTickCount() < (dat->dwTicks + 650)) - break; // the last update was less than a second ago! - dat->dwTicks = GetTickCount(); - - // Update local transfer status with data from protocol - UpdateProtoFileTransferStatus(&dat->transferStatus, fts); - fts = &dat->transferStatus; - - bool firstTime = false; - if ((GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), GWL_STYLE) & WS_VISIBLE) == 0) { - SetFtStatus(hwndDlg, (fts->flags & PFTS_SENDING) ? LPGENW("Sending...") : LPGENW("Receiving..."), FTS_PROGRESS); - SetFilenameControls(hwndDlg, dat, fts); - firstTime = true; - } - - const unsigned long lastPos = SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_GETPOS, 0, 0); - const unsigned long nextPos = fts->totalBytes ? (100ll * fts->totalProgress / fts->totalBytes) : 0; - if (lastPos != nextPos || firstTime) { - SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_SETPOS, nextPos, 0); - mir_snwprintf(str, L"%u%%", nextPos); - SetDlgItemText(hwndDlg, IDC_ALLPRECENTS, str); - } - - int units; - GetSensiblyFormattedSize(fts->totalBytes, szSizeTotal, _countof(szSizeTotal), 0, 1, &units); - GetSensiblyFormattedSize(fts->totalProgress, szSizeDone, _countof(szSizeDone), units, 0, NULL); - mir_snwprintf(str, L"%s/%s", szSizeDone, szSizeTotal); - str2[0] = 0; - GetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str2, _countof(str2)); - if (mir_wstrcmp(str, str2)) - SetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str); - } - break; - - case ACKRESULT_SUCCESS: - case ACKRESULT_FAILED: - case ACKRESULT_DENIED: - HideProgressControls(hwndDlg); - KillTimer(hwndDlg, 1); - if (!dat->send) - SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); - SetDlgItemText(hwndDlg, IDCANCEL, TranslateT("Close")); - if (dat->hNotifyEvent) - UnhookEvent(dat->hNotifyEvent); - dat->hNotifyEvent = nullptr; - - if (ack->result == ACKRESULT_DENIED) { - dat->fs = nullptr; /* protocol will free structure */ - Skin_PlaySound("FileDenied"); - SetFtStatus(hwndDlg, LPGENW("File transfer denied"), FTS_TEXT); - } - else if (ack->result == ACKRESULT_FAILED) { - dat->fs = nullptr; /* protocol will free structure */ - Skin_PlaySound("FileFailed"); - SetFtStatus(hwndDlg, LPGENW("File transfer failed"), FTS_TEXT); - } - else { - Skin_PlaySound("FileDone"); - if (dat->send) { - dat->fs = nullptr; /* protocol will free structure */ - SetFtStatus(hwndDlg, LPGENW("Transfer completed."), FTS_TEXT); - - DBEVENTINFO dbei = {}; - FillSendData(dat, dbei); - db_event_add(dat->hContact, &dbei); - if (dbei.pBlob) - mir_free(dbei.pBlob); - dat->files = nullptr; //protocol library frees this - } - else { - SetFtStatus(hwndDlg, - (dat->transferStatus.totalFiles == 1) ? - LPGENW("Transfer completed, open file.") : - LPGENW("Transfer completed, open folder."), - FTS_OPEN); - - int useScanner = g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE); - if (useScanner != VIRUSSCAN_DISABLE) { - auto *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo)); - vstsi->hwndReply = hwndDlg; - if (useScanner == VIRUSSCAN_DURINGDL) { - vstsi->returnCode = dat->transferStatus.currentFileNumber; - if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY) { - PostMessage(hwndDlg, M_VIRUSSCANDONE, vstsi->returnCode, 0); - mir_free(vstsi); - vstsi = nullptr; - } - else vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]); - } - else { - vstsi->szFile = mir_wstrdup(dat->transferStatus.szWorkingDir.w); - vstsi->returnCode = -1; - } - SetFtStatus(hwndDlg, LPGENW("Scanning for viruses..."), FTS_TEXT); - if (vstsi) - mir_forkThread<virusscanthreadstartinfo>(RunVirusScannerThread, vstsi); - } - else dat->fs = nullptr; /* protocol will free structure */ - - dat->transferStatus.currentFileNumber = dat->transferStatus.totalFiles; - } - } - - PostMessage(GetParent(hwndDlg), WM_FT_COMPLETED, ack->result, (LPARAM)hwndDlg); - break; - } - } - break; - - case M_VIRUSSCANDONE: - { - int done = 1; - if ((int)wParam == -1) { - for (int i = 0; i < dat->transferStatus.totalFiles; i++) - dat->fileVirusScanned[i] = 1; - } - else { - dat->fileVirusScanned[wParam] = 1; - for (int i = 0; i < dat->transferStatus.totalFiles; i++) - if (!dat->fileVirusScanned[i]) { - done = 0; - break; - } - } - if (done) { - dat->fs = nullptr; /* protocol will free structure */ - SetFtStatus(hwndDlg, LPGENW("Transfer and virus scan complete"), FTS_TEXT); - } - } - break; - - case WM_SIZE: - Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FILETRANSFERINFO), FileTransferDlgResizer, LPARAM(dat)); - - RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - RedrawWindow(GetDlgItem(hwndDlg, IDC_CONTACTNAME), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - RedrawWindow(GetDlgItem(hwndDlg, IDC_STATUS), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - break; - - case WM_DESTROY: - KillTimer(hwndDlg, 1); - - HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0); - DeleteObject(hFont); - - Button_FreeIcon_IcoLib(hwndDlg, IDC_CONTACT); - Button_FreeIcon_IcoLib(hwndDlg, IDC_OPENFILE); - Button_FreeIcon_IcoLib(hwndDlg, IDCANCEL); - - delete dat; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - break; - } - return FALSE; -} - -FileDlgData::~FileDlgData() -{ - if (fs) - ProtoChainSend(hContact, PSS_FILECANCEL, (WPARAM)fs, 0); - if (hPreshutdownEvent) - UnhookEvent(hPreshutdownEvent); - if (hNotifyEvent) - UnhookEvent(hNotifyEvent); - - FreeProtoFileTransferStatus(&transferStatus); - FreeFilesMatrix(&files); - - mir_free(fileVirusScanned); - if (hIcon) - DestroyIcon(hIcon); - if (hIconFolder) - DestroyIcon(hIconFolder); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include <io.h>
+#include "file.h"
+
+static int CheckVirusScanned(HWND hwnd, FileDlgData *dat, int i)
+{
+ if (dat->send) return 1;
+ if (dat->fileVirusScanned == nullptr) return 0;
+ if (dat->fileVirusScanned[i]) return 1;
+ if (g_plugin.getByte("WarnBeforeOpening", 1) == 0) return 1;
+
+ return IDYES == MessageBox(hwnd,
+ TranslateT("This file has not yet been scanned for viruses. Are you certain you want to open it?"),
+ TranslateT("File received"),
+ MB_YESNO | MB_DEFBUTTON2);
+}
+
+#define M_VIRUSSCANDONE (WM_USER+100)
+struct virusscanthreadstartinfo
+{
+ wchar_t *szFile;
+ int returnCode;
+ HWND hwndReply;
+};
+
+wchar_t* PFTS_StringToTchar(int flags, const wchar_t *s)
+{
+ if (flags & PFTS_UTF)
+ return mir_utf8decodeW((char*)s);
+ if (flags & PFTS_UNICODE)
+ return mir_wstrdup(s);
+ return mir_a2u((char*)s);
+}
+
+int PFTS_CompareWithTchar(PROTOFILETRANSFERSTATUS *ft, const wchar_t *s, wchar_t *r)
+{
+ if (ft->flags & PFTS_UTF) {
+ wchar_t *ts = mir_utf8decodeW((char*)s);
+ int res = mir_wstrcmp(ts, r);
+ mir_free(ts);
+ return res;
+ }
+ if (ft->flags & PFTS_UNICODE)
+ return mir_wstrcmp(s, r);
+
+ wchar_t *ts = mir_a2u((char*)s);
+ int res = mir_wstrcmp(ts, r);
+ mir_free(ts);
+ return res;
+}
+
+static void SetOpenFileButtonStyle(HWND hwndButton, int enabled)
+{
+ EnableWindow(hwndButton, enabled);
+}
+
+void FillSendData(FileDlgData *dat, DBEVENTINFO &dbei)
+{
+ dbei.szModule = Proto_GetBaseAccountName(dat->hContact);
+ dbei.eventType = EVENTTYPE_FILE;
+ dbei.flags = DBEF_SENT;
+ dbei.timestamp = time(0);
+ char *szFileNames = mir_utf8encodeW(dat->szFilenames), *szMsg = mir_utf8encodeW(dat->szMsg);
+ dbei.flags |= DBEF_UTF;
+
+ dbei.cbBlob = int(sizeof(uint32_t) + mir_strlen(szFileNames) + mir_strlen(szMsg) + 2);
+ dbei.pBlob = (uint8_t*)mir_alloc(dbei.cbBlob);
+ *(PDWORD)dbei.pBlob = 0;
+ mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t), szFileNames);
+ mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t) + mir_strlen(szFileNames) + 1, szMsg);
+
+ mir_free(szFileNames), mir_free(szMsg);
+}
+
+static void __cdecl RunVirusScannerThread(virusscanthreadstartinfo *info)
+{
+ DBVARIANT dbv;
+ if (!g_plugin.getWString("ScanCmdLine", &dbv)) {
+ if (dbv.pwszVal[0]) {
+ STARTUPINFO si = { 0 };
+ si.cb = sizeof(si);
+ wchar_t *pszReplace = wcsstr(dbv.pwszVal, L"%f");
+ wchar_t szCmdLine[768];
+ if (pszReplace) {
+ if (info->szFile[mir_wstrlen(info->szFile) - 1] == '\\')
+ info->szFile[mir_wstrlen(info->szFile) - 1] = '\0';
+ *pszReplace = 0;
+ mir_snwprintf(szCmdLine, L"%s\"%s\"%s", dbv.pwszVal, info->szFile, pszReplace + 2);
+ }
+ else
+ wcsncpy_s(szCmdLine, dbv.pwszVal, _TRUNCATE);
+
+ PROCESS_INFORMATION pi;
+ if (CreateProcess(nullptr, szCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) {
+ if (WaitForSingleObject(pi.hProcess, 3600 * 1000) == WAIT_OBJECT_0)
+ PostMessage(info->hwndReply, M_VIRUSSCANDONE, info->returnCode, 0);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+ }
+ db_free(&dbv);
+ }
+ mir_free(info->szFile);
+ mir_free(info);
+}
+
+static void SetFilenameControls(HWND hwndDlg, FileDlgData *dat, PROTOFILETRANSFERSTATUS *fts)
+{
+ wchar_t msg[MAX_PATH];
+ wchar_t *fnbuf = nullptr, *fn = nullptr;
+ SHFILEINFO shfi = {};
+
+ if (fts->szCurrentFile.w) {
+ fnbuf = mir_wstrdup(fts->szCurrentFile.w);
+ if ((fn = wcsrchr(fnbuf, '\\')) == nullptr)
+ fn = fnbuf;
+ else fn++;
+ }
+
+ if (dat->hIcon) DestroyIcon(dat->hIcon); dat->hIcon = nullptr;
+
+ if (fn && (fts->totalFiles > 1)) {
+ mir_snwprintf(msg, L"%s: %s (%d %s %d)", Clist_GetContactDisplayName(fts->hContact), fn, fts->currentFileNumber + 1, TranslateT("of"), fts->totalFiles);
+
+ SHGetFileInfo(fn, FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
+ dat->hIcon = shfi.hIcon;
+ }
+ else if (fn) {
+ mir_snwprintf(msg, L"%s: %s", Clist_GetContactDisplayName(fts->hContact), fn);
+
+ SHGetFileInfo(fn, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
+ dat->hIcon = shfi.hIcon;
+ }
+ else {
+ mir_wstrncpy(msg, Clist_GetContactDisplayName(fts->hContact), _countof(msg));
+ HICON hIcon = Skin_LoadIcon(SKINICON_OTHER_DOWNARROW);
+ dat->hIcon = CopyIcon(hIcon);
+ IcoLib_ReleaseIcon(hIcon, NULL);
+ }
+
+ mir_free(fnbuf);
+
+ SendDlgItemMessage(hwndDlg, IDC_FILEICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIcon);
+ SetDlgItemText(hwndDlg, IDC_CONTACTNAME, msg);
+}
+
+enum { FTS_TEXT, FTS_PROGRESS, FTS_OPEN };
+static void SetFtStatus(HWND hwndDlg, wchar_t *text, int mode)
+{
+ SetDlgItemText(hwndDlg, IDC_STATUS, TranslateW(text));
+ SetDlgItemText(hwndDlg, IDC_TRANSFERCOMPLETED, TranslateW(text));
+
+ ShowWindow(GetDlgItem(hwndDlg, IDC_STATUS), (mode == FTS_TEXT) ? SW_SHOW : SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), (mode == FTS_PROGRESS) ? SW_SHOW : SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TRANSFERCOMPLETED), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_FILEICON), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE);
+}
+
+static void HideProgressControls(HWND hwndDlg)
+{
+ RECT rc;
+ char buf[64];
+
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_ALLPRECENTS), &rc);
+ MapWindowPoints(nullptr, hwndDlg, (LPPOINT)&rc, 2);
+ SetWindowPos(hwndDlg, nullptr, 0, 0, 100, rc.bottom + 3, SWP_NOMOVE | SWP_NOZORDER);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), SW_HIDE);
+
+ _strtime(buf);
+ SetDlgItemTextA(hwndDlg, IDC_ALLPRECENTS, buf);
+
+ PostMessage(GetParent(hwndDlg), WM_FT_RESIZE, 0, (LPARAM)hwndDlg);
+}
+
+static int FileTransferDlgResizer(HWND, LPARAM param, UTILRESIZECONTROL *urc)
+{
+ auto *dat = (FileDlgData *)param;
+
+ switch (urc->wId) {
+ case IDC_CONTACTNAME:
+ case IDC_STATUS:
+ case IDC_ALLFILESPROGRESS:
+ case IDC_TRANSFERCOMPLETED:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+
+ case IDC_FRAME:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM;
+ case IDC_ALLPRECENTS:
+ case IDCANCEL:
+ case IDC_OPENFILE:
+ case IDC_OPENFOLDER:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
+
+ case IDC_ALLTRANSFERRED:
+ if (dat->waitingForAcceptance)
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+
+ urc->rcItem.right = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx) / 3;
+ return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP;
+
+ case IDC_ALLSPEED:
+ if (dat->waitingForAcceptance)
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
+
+ urc->rcItem.right = urc->rcItem.right - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx;
+ urc->rcItem.left = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left) / 3;
+ return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+INT_PTR CALLBACK DlgProcFileTransfer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ FileDlgData *dat = (FileDlgData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ dat = (FileDlgData *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ dat->hNotifyEvent = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_RECVEVENT);
+ dat->transferStatus.currentFileNumber = -1;
+ if (dat->send) {
+ if (db_mc_isMeta(dat->hContact))
+ dat->hContact = db_mc_getMostOnline(dat->hContact);
+ dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILE, (WPARAM)dat->szMsg, (LPARAM)dat->files);
+ SetFtStatus(hwndDlg, LPGENW("Request sent, waiting for acceptance..."), FTS_TEXT);
+ SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1);
+ dat->waitingForAcceptance = 1;
+ // hide "open" button since it may cause potential access violations...
+ ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFILE), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFOLDER), SW_HIDE);
+ }
+ else { //recv
+ CreateDirectoryTreeW(dat->szSavePath);
+ dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILEALLOW, (WPARAM)dat->fs, (LPARAM)dat->szSavePath);
+ dat->transferStatus.szWorkingDir.w = mir_wstrdup(dat->szSavePath);
+ if (!Contact::OnList(dat->hContact))
+ dat->resumeBehaviour = FILERESUME_ASK;
+ else
+ dat->resumeBehaviour = g_plugin.getByte("IfExists", FILERESUME_ASK);
+ SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT);
+ }
+
+ /* check we actually got an fs handle back from the protocol */
+ if (!dat->fs) {
+ SetFtStatus(hwndDlg, LPGENW("Unable to initiate transfer."), FTS_TEXT);
+ dat->waitingForAcceptance = 0;
+ }
+ {
+ LOGFONT lf;
+ HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0);
+ GetObject(hFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hFont = CreateFontIndirect(&lf);
+ SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_SETFONT, (WPARAM)hFont, 0);
+
+ SHFILEINFO shfi = {};
+ SHGetFileInfo(L"", FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
+ dat->hIconFolder = shfi.hIcon;
+ }
+ dat->hIcon = nullptr;
+ {
+ char *szProto = Proto_GetBaseAccountName(dat->hContact);
+ uint16_t status = db_get_w(dat->hContact, szProto, "Status", ID_STATUS_ONLINE);
+ SendDlgItemMessage(hwndDlg, IDC_CONTACT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadProtoIcon(szProto, status));
+ }
+
+ SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Contact menu"), 0);
+ SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONSETASFLATBTN, TRUE, 0);
+
+ Button_SetSkin_IcoLib(hwndDlg, IDC_OPENFILE, SKINICON_OTHER_DOWNARROW, LPGEN("Open..."));
+ SendDlgItemMessage(hwndDlg, IDC_OPENFILE, BUTTONSETASPUSHBTN, TRUE, 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIconFolder);
+ SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open folder"), 0);
+ SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONSETASFLATBTN, TRUE, 0);
+
+ Button_SetSkin_IcoLib(hwndDlg, IDCANCEL, SKINICON_OTHER_DELETE, LPGEN("Cancel"));
+
+ SetDlgItemText(hwndDlg, IDC_CONTACTNAME, Clist_GetContactDisplayName(dat->hContact));
+
+ if (!dat->waitingForAcceptance)
+ SetTimer(hwndDlg, 1, 1000, nullptr);
+ return TRUE;
+
+ case WM_TIMER:
+ memmove(dat->bytesRecvedHistory + 1, dat->bytesRecvedHistory, sizeof(dat->bytesRecvedHistory) - sizeof(dat->bytesRecvedHistory[0]));
+ dat->bytesRecvedHistory[0] = dat->transferStatus.totalProgress;
+ if (dat->bytesRecvedHistorySize < _countof(dat->bytesRecvedHistory))
+ dat->bytesRecvedHistorySize++;
+
+ wchar_t szSpeed[32], szTime[32], szDisplay[96];
+ SYSTEMTIME st;
+ ULARGE_INTEGER li;
+ FILETIME ft;
+
+ GetSensiblyFormattedSize((dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) / dat->bytesRecvedHistorySize, szSpeed, _countof(szSpeed), 0, 1, NULL);
+ if (dat->bytesRecvedHistory[0] == dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1])
+ mir_wstrcpy(szTime, L"??:??:??");
+ else {
+ li.QuadPart = 10000000ll * (dat->transferStatus.currentFileSize - dat->transferStatus.currentFileProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]);
+ ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart;
+ FileTimeToSystemTime(&ft, &st);
+ GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime));
+ }
+ if (dat->bytesRecvedHistory[0] != dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) {
+ li.QuadPart = 10000000ll * (dat->transferStatus.totalBytes - dat->transferStatus.totalProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]);
+ ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart;
+ FileTimeToSystemTime(&ft, &st);
+ GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime));
+ }
+
+ mir_snwprintf(szDisplay, L"%s/%s (%s %s)", szSpeed, TranslateT("sec"), szTime, TranslateT("remaining"));
+ SetDlgItemText(hwndDlg, IDC_ALLSPEED, szDisplay);
+ break;
+
+ case WM_MEASUREITEM:
+ return Menu_MeasureItem(lParam);
+
+ case WM_DRAWITEM:
+ return Menu_DrawItem(lParam);
+
+ case WM_FT_CLEANUP:
+ if (!dat->fs) {
+ PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg);
+ DestroyWindow(hwndDlg);
+ }
+ break;
+
+ case WM_COMMAND:
+ if (!dat)
+ break;
+
+ if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, dat->hContact))
+ break;
+
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL:
+ PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg);
+ DestroyWindow(hwndDlg);
+ break;
+
+ case IDC_CONTACT:
+ {
+ RECT rc;
+ HMENU hMenu = Menu_BuildContactMenu(dat->hContact);
+ GetWindowRect((HWND)lParam, &rc);
+ TrackPopupMenu(hMenu, 0, rc.left, rc.bottom, 0, hwndDlg, NULL);
+ DestroyMenu(hMenu);
+ }
+ break;
+
+ case IDC_TRANSFERCOMPLETED:
+ if (dat->transferStatus.currentFileNumber <= 1 && CheckVirusScanned(hwndDlg, dat, 0)) {
+ ShellExecute(NULL, NULL, dat->files[0], NULL, NULL, SW_SHOW);
+ break;
+ }
+
+ case IDC_OPENFOLDER:
+ {
+ wchar_t *path = dat->transferStatus.szWorkingDir.w;
+ if (!path || !path[0]) {
+ path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w);
+ wchar_t *p = wcsrchr(path, '\\'); if (p) *p = 0;
+ }
+
+ if (path) ShellExecute(NULL, L"open", path, NULL, NULL, SW_SHOW);
+ }
+ break;
+
+ case IDC_OPENFILE:
+ wchar_t **files;
+ if (dat->send) {
+ if (dat->files == nullptr)
+ files = dat->transferStatus.pszFiles.w;
+ else
+ files = dat->files;
+ }
+ else files = dat->files;
+
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, 1, TranslateT("Open folder"));
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+
+ if (files && *files) {
+ int limit;
+ wchar_t *pszFilename, *pszNewFileName;
+
+ if (dat->send)
+ limit = dat->transferStatus.totalFiles;
+ else
+ limit = dat->transferStatus.currentFileNumber;
+
+ // Loop over all transfered files and add them to the menu
+ for (int i = 0; i < limit; i++) {
+ pszFilename = wcsrchr(files[i], '\\');
+ if (pszFilename == nullptr)
+ pszFilename = files[i];
+ else
+ pszFilename++;
+
+ if (pszFilename) {
+ size_t cbFileNameLen = mir_wstrlen(pszFilename);
+
+ pszNewFileName = (wchar_t*)mir_alloc(cbFileNameLen * 2 * sizeof(wchar_t));
+ wchar_t *p = pszNewFileName;
+ for (size_t pszlen = 0; pszlen < cbFileNameLen; pszlen++) {
+ *p++ = pszFilename[pszlen];
+ if (pszFilename[pszlen] == '&')
+ *p++ = '&';
+ }
+ *p = '\0';
+ AppendMenu(hMenu, MF_STRING, i + 10, pszNewFileName);
+ mir_free(pszNewFileName);
+ }
+ }
+ }
+
+ RECT rc;
+ GetWindowRect((HWND)lParam, &rc);
+ CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_CHECKED);
+ int ret = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_RIGHTALIGN, rc.right, rc.bottom, 0, hwndDlg, nullptr);
+ CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_UNCHECKED);
+ DestroyMenu(hMenu);
+
+ if (ret == 1) {
+ wchar_t *path = dat->transferStatus.szWorkingDir.w;
+ if (!path || !path[0]) {
+ path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w);
+ wchar_t *p = wcsrchr(path, '\\');
+ if (p)
+ *p = 0;
+ }
+
+ if (path) ShellExecute(nullptr, L"open", path, nullptr, nullptr, SW_SHOW);
+ }
+ else if (ret && CheckVirusScanned(hwndDlg, dat, ret))
+ ShellExecute(nullptr, nullptr, files[ret - 10], nullptr, nullptr, SW_SHOW);
+ }
+ break;
+
+ case M_FILEEXISTSDLGREPLY:
+ EnableWindow(hwndDlg, TRUE);
+ {
+ PROTOFILERESUME *pfr = (PROTOFILERESUME *)lParam;
+ wchar_t *szOriginalFilename = (wchar_t *)wParam;
+ char *szProto = Proto_GetBaseAccountName(dat->hContact);
+
+ switch (pfr->action) {
+ case FILERESUME_CANCEL:
+ if (dat->fs) ProtoChainSend(dat->hContact, PSS_FILECANCEL, (WPARAM)dat->fs, 0);
+ dat->fs = nullptr;
+ mir_free(szOriginalFilename);
+ if (pfr->szFilename) mir_free((char *)pfr->szFilename);
+ mir_free(pfr);
+ return 0;
+ case FILERESUME_RESUMEALL:
+ case FILERESUME_OVERWRITEALL:
+ dat->resumeBehaviour = pfr->action;
+ pfr->action &= ~FILERESUMEF_ALL;
+ break;
+ case FILERESUME_RENAMEALL:
+ pfr->action = FILERESUME_RENAME;
+ {
+ wchar_t *pszExtension, *pszFilename;
+ if ((pszFilename = wcsrchr(szOriginalFilename, '\\')) == nullptr) pszFilename = szOriginalFilename;
+ if ((pszExtension = wcsrchr(pszFilename + 1, '.')) == nullptr) pszExtension = pszFilename + mir_wstrlen(pszFilename);
+ if (pfr->szFilename) mir_free((wchar_t *)pfr->szFilename);
+ size_t size = (pszExtension - szOriginalFilename) + 21 + mir_wstrlen(pszExtension);
+ pfr->szFilename = (wchar_t *)mir_alloc(sizeof(wchar_t) * size);
+ for (int i = 1;; i++) {
+ mir_snwprintf((wchar_t *)pfr->szFilename, size, L"%.*s (%u)%s", pszExtension - szOriginalFilename, szOriginalFilename, i, pszExtension);
+ if (_waccess(pfr->szFilename, 0) != 0)
+ break;
+ }
+ }
+ break;
+ }
+ mir_free(szOriginalFilename);
+ CallProtoService(szProto, PS_FILERESUME, (WPARAM)dat->fs, (LPARAM)pfr);
+ delete pfr;
+ }
+ break;
+
+ case HM_RECVEVENT:
+ {
+ ACKDATA *ack = (ACKDATA *)lParam;
+ if (ack->hProcess != dat->fs) break;
+ if (ack->type != ACKTYPE_FILE) break;
+ if (ack->hContact != dat->hContact) break;
+
+ if (dat->waitingForAcceptance) {
+ SetTimer(hwndDlg, 1, 1000, nullptr);
+ dat->waitingForAcceptance = 0;
+ }
+
+ switch (ack->result) {
+ case ACKRESULT_SENTREQUEST: SetFtStatus(hwndDlg, LPGENW("Decision sent"), FTS_TEXT); break;
+ case ACKRESULT_CONNECTING: SetFtStatus(hwndDlg, LPGENW("Connecting..."), FTS_TEXT); break;
+ case ACKRESULT_CONNECTPROXY: SetFtStatus(hwndDlg, LPGENW("Connecting to proxy..."), FTS_TEXT); break;
+ case ACKRESULT_CONNECTED: SetFtStatus(hwndDlg, LPGENW("Connected"), FTS_TEXT); break;
+ case ACKRESULT_LISTENING: SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); break;
+ case ACKRESULT_INITIALISING: SetFtStatus(hwndDlg, LPGENW("Initializing..."), FTS_TEXT); break;
+ case ACKRESULT_NEXTFILE:
+ SetFtStatus(hwndDlg, LPGENW("Moving to next file..."), FTS_TEXT);
+ SetDlgItemTextA(hwndDlg, IDC_FILENAME, "");
+ if (dat->transferStatus.currentFileNumber == 1 && dat->transferStatus.totalFiles > 1 && !dat->send)
+ SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1);
+ if (dat->transferStatus.currentFileNumber != -1 && dat->files && !dat->send && g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE) == VIRUSSCAN_DURINGDL) {
+ if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY)
+ PostMessage(hwndDlg, M_VIRUSSCANDONE, dat->transferStatus.currentFileNumber, 0);
+ else {
+ virusscanthreadstartinfo *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo));
+ vstsi->hwndReply = hwndDlg;
+ vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]);
+ vstsi->returnCode = dat->transferStatus.currentFileNumber;
+ mir_forkThread<virusscanthreadstartinfo>(RunVirusScannerThread, vstsi);
+ }
+ }
+ break;
+
+ case ACKRESULT_FILERESUME:
+ UpdateProtoFileTransferStatus(&dat->transferStatus, (PROTOFILETRANSFERSTATUS *)ack->lParam);
+ {
+ PROTOFILETRANSFERSTATUS *fts = &dat->transferStatus;
+ SetFilenameControls(hwndDlg, dat, fts);
+ if (_waccess(fts->szCurrentFile.w, 0))
+ break;
+
+ SetFtStatus(hwndDlg, LPGENW("File already exists"), FTS_TEXT);
+ if (dat->resumeBehaviour == FILERESUME_ASK) {
+ TDlgProcFileExistsParam param = { hwndDlg, fts };
+ ShowWindow(hwndDlg, SW_SHOWNORMAL);
+ CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILEEXISTS), hwndDlg, DlgProcFileExists, (LPARAM)¶m);
+ EnableWindow(hwndDlg, FALSE);
+ }
+ else {
+ PROTOFILERESUME *pfr = new PROTOFILERESUME();
+ pfr->action = dat->resumeBehaviour;
+ pfr->szFilename = nullptr;
+ PostMessage(hwndDlg, M_FILEEXISTSDLGREPLY, (WPARAM)mir_wstrdup(fts->szCurrentFile.w), (LPARAM)pfr);
+ }
+ }
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 1);
+ return TRUE;
+
+ case ACKRESULT_DATA:
+ {
+ PROTOFILETRANSFERSTATUS *fts = (PROTOFILETRANSFERSTATUS *)ack->lParam;
+ wchar_t str[64], str2[64], szSizeDone[32], szSizeTotal[32];//, *contactName;
+
+ if (dat->fileVirusScanned == nullptr)
+ dat->fileVirusScanned = (int *)mir_calloc(sizeof(int) * fts->totalFiles);
+
+ // This needs to be here - otherwise we get holes in the files array
+ if (!dat->send) {
+ if (dat->files == nullptr)
+ dat->files = (wchar_t **)mir_calloc((fts->totalFiles + 1) * sizeof(wchar_t *));
+ if (fts->currentFileNumber < fts->totalFiles && dat->files[fts->currentFileNumber] == nullptr)
+ dat->files[fts->currentFileNumber] = PFTS_StringToTchar(fts->flags, fts->szCurrentFile.w);
+ }
+
+ /* HACK: for 0.3.3, limit updates to around 1.1 ack per second */
+ if (fts->totalProgress != fts->totalBytes && GetTickCount() < (dat->dwTicks + 650))
+ break; // the last update was less than a second ago!
+ dat->dwTicks = GetTickCount();
+
+ // Update local transfer status with data from protocol
+ UpdateProtoFileTransferStatus(&dat->transferStatus, fts);
+ fts = &dat->transferStatus;
+
+ bool firstTime = false;
+ if ((GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), GWL_STYLE) & WS_VISIBLE) == 0) {
+ SetFtStatus(hwndDlg, (fts->flags & PFTS_SENDING) ? LPGENW("Sending...") : LPGENW("Receiving..."), FTS_PROGRESS);
+ SetFilenameControls(hwndDlg, dat, fts);
+ firstTime = true;
+ }
+
+ const unsigned long lastPos = SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_GETPOS, 0, 0);
+ const unsigned long nextPos = fts->totalBytes ? (100ll * fts->totalProgress / fts->totalBytes) : 0;
+ if (lastPos != nextPos || firstTime) {
+ SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_SETPOS, nextPos, 0);
+ mir_snwprintf(str, L"%u%%", nextPos);
+ SetDlgItemText(hwndDlg, IDC_ALLPRECENTS, str);
+ }
+
+ int units;
+ GetSensiblyFormattedSize(fts->totalBytes, szSizeTotal, _countof(szSizeTotal), 0, 1, &units);
+ GetSensiblyFormattedSize(fts->totalProgress, szSizeDone, _countof(szSizeDone), units, 0, NULL);
+ mir_snwprintf(str, L"%s/%s", szSizeDone, szSizeTotal);
+ str2[0] = 0;
+ GetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str2, _countof(str2));
+ if (mir_wstrcmp(str, str2))
+ SetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str);
+ }
+ break;
+
+ case ACKRESULT_SUCCESS:
+ case ACKRESULT_FAILED:
+ case ACKRESULT_DENIED:
+ HideProgressControls(hwndDlg);
+ KillTimer(hwndDlg, 1);
+ if (!dat->send)
+ SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1);
+ SetDlgItemText(hwndDlg, IDCANCEL, TranslateT("Close"));
+ if (dat->hNotifyEvent)
+ UnhookEvent(dat->hNotifyEvent);
+ dat->hNotifyEvent = nullptr;
+
+ if (ack->result == ACKRESULT_DENIED) {
+ dat->fs = nullptr; /* protocol will free structure */
+ Skin_PlaySound("FileDenied");
+ SetFtStatus(hwndDlg, LPGENW("File transfer denied"), FTS_TEXT);
+ }
+ else if (ack->result == ACKRESULT_FAILED) {
+ dat->fs = nullptr; /* protocol will free structure */
+ Skin_PlaySound("FileFailed");
+ SetFtStatus(hwndDlg, LPGENW("File transfer failed"), FTS_TEXT);
+ }
+ else {
+ Skin_PlaySound("FileDone");
+ if (dat->send) {
+ dat->fs = nullptr; /* protocol will free structure */
+ SetFtStatus(hwndDlg, LPGENW("Transfer completed."), FTS_TEXT);
+
+ DBEVENTINFO dbei = {};
+ FillSendData(dat, dbei);
+ db_event_add(dat->hContact, &dbei);
+ if (dbei.pBlob)
+ mir_free(dbei.pBlob);
+ dat->files = nullptr; //protocol library frees this
+ }
+ else {
+ SetFtStatus(hwndDlg,
+ (dat->transferStatus.totalFiles == 1) ?
+ LPGENW("Transfer completed, open file.") :
+ LPGENW("Transfer completed, open folder."),
+ FTS_OPEN);
+
+ int useScanner = g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE);
+ if (useScanner != VIRUSSCAN_DISABLE) {
+ auto *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo));
+ vstsi->hwndReply = hwndDlg;
+ if (useScanner == VIRUSSCAN_DURINGDL) {
+ vstsi->returnCode = dat->transferStatus.currentFileNumber;
+ if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY) {
+ PostMessage(hwndDlg, M_VIRUSSCANDONE, vstsi->returnCode, 0);
+ mir_free(vstsi);
+ vstsi = nullptr;
+ }
+ else vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]);
+ }
+ else {
+ vstsi->szFile = mir_wstrdup(dat->transferStatus.szWorkingDir.w);
+ vstsi->returnCode = -1;
+ }
+ SetFtStatus(hwndDlg, LPGENW("Scanning for viruses..."), FTS_TEXT);
+ if (vstsi)
+ mir_forkThread<virusscanthreadstartinfo>(RunVirusScannerThread, vstsi);
+ }
+ else dat->fs = nullptr; /* protocol will free structure */
+
+ dat->transferStatus.currentFileNumber = dat->transferStatus.totalFiles;
+ }
+ }
+
+ PostMessage(GetParent(hwndDlg), WM_FT_COMPLETED, ack->result, (LPARAM)hwndDlg);
+ break;
+ }
+ }
+ break;
+
+ case M_VIRUSSCANDONE:
+ {
+ int done = 1;
+ if ((int)wParam == -1) {
+ for (int i = 0; i < dat->transferStatus.totalFiles; i++)
+ dat->fileVirusScanned[i] = 1;
+ }
+ else {
+ dat->fileVirusScanned[wParam] = 1;
+ for (int i = 0; i < dat->transferStatus.totalFiles; i++)
+ if (!dat->fileVirusScanned[i]) {
+ done = 0;
+ break;
+ }
+ }
+ if (done) {
+ dat->fs = nullptr; /* protocol will free structure */
+ SetFtStatus(hwndDlg, LPGENW("Transfer and virus scan complete"), FTS_TEXT);
+ }
+ }
+ break;
+
+ case WM_SIZE:
+ Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FILETRANSFERINFO), FileTransferDlgResizer, LPARAM(dat));
+
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_CONTACTNAME), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_STATUS), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE);
+ break;
+
+ case WM_DESTROY:
+ KillTimer(hwndDlg, 1);
+
+ HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0);
+ DeleteObject(hFont);
+
+ Button_FreeIcon_IcoLib(hwndDlg, IDC_CONTACT);
+ Button_FreeIcon_IcoLib(hwndDlg, IDC_OPENFILE);
+ Button_FreeIcon_IcoLib(hwndDlg, IDCANCEL);
+
+ delete dat;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break;
+ }
+ return FALSE;
+}
+
+FileDlgData::~FileDlgData()
+{
+ if (fs)
+ ProtoChainSend(hContact, PSS_FILECANCEL, (WPARAM)fs, 0);
+ if (hPreshutdownEvent)
+ UnhookEvent(hPreshutdownEvent);
+ if (hNotifyEvent)
+ UnhookEvent(hNotifyEvent);
+
+ FreeProtoFileTransferStatus(&transferStatus);
+ FreeFilesMatrix(&files);
+
+ mir_free(fileVirusScanned);
+ if (hIcon)
+ DestroyIcon(hIcon);
+ if (hIconFolder)
+ DestroyIcon(hIconFolder);
+}
diff --git a/src/core/stdfile/src/ftmanager.cpp b/src/core/stdfile/src/ftmanager.cpp index 20206908be..56b0a168a7 100644 --- a/src/core/stdfile/src/ftmanager.cpp +++ b/src/core/stdfile/src/ftmanager.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/main.cpp b/src/core/stdfile/src/main.cpp index 275c58cf44..3797ea1e49 100644 --- a/src/core/stdfile/src/main.cpp +++ b/src/core/stdfile/src/main.cpp @@ -2,7 +2,7 @@ Standard file transfers' plugin for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stdfile/src/stdafx.h b/src/core/stdfile/src/stdafx.h index b732c1278c..775d957d38 100644 --- a/src/core/stdfile/src/stdafx.h +++ b/src/core/stdfile/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdfile/src/version.h b/src/core/stdfile/src/version.h index 777678748d..2099819477 100644 --- a/src/core/stdfile/src/version.h +++ b/src/core/stdfile/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for sending/receiving files."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdFile"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdmsg/src/chat_manager.cpp b/src/core/stdmsg/src/chat_manager.cpp index b72f53acd7..b461b91703 100644 --- a/src/core/stdmsg/src/chat_manager.cpp +++ b/src/core/stdmsg/src/chat_manager.cpp @@ -1,243 +1,243 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -pfnDoTrayIcon oldDoTrayIcon; -pfnDoPopup oldDoPopup; - -SESSION_INFO* SM_GetPrevWindow(SESSION_INFO *si) -{ - int i = g_chatApi.arSessions.indexOf(si); - if (i == -1) - return nullptr; - - for (i--; i >= 0; i--) { - SESSION_INFO *p = g_chatApi.arSessions[i]; - if (p->pDlg) - return p; - } - - return nullptr; -} - -SESSION_INFO* SM_GetNextWindow(SESSION_INFO *si) -{ - int i = g_chatApi.arSessions.indexOf(si); - if (i == -1) - return nullptr; - - for (i++; i < g_chatApi.arSessions.getCount(); i++) { - SESSION_INFO *p = g_chatApi.arSessions[i]; - if (p->pDlg) - return p; - } - - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -HMENU g_hMenu = nullptr; - -GlobalLogSettings g_Settings; - -static MODULEINFO* MM_CreateModule() -{ - return new MODULEINFO(); -} - -static void OnReplaceSession(SESSION_INFO *si) -{ - if (si->pDlg) - RedrawWindow(GetDlgItem(si->pDlg->GetHwnd(), IDC_SRMM_NICKLIST), nullptr, nullptr, RDW_INVALIDATE); -} - -static void OnFlashHighlight(SESSION_INFO *si, int bInactive) -{ - if (!bInactive || si->pDlg == nullptr) - return; - - if (g_Settings.bTabsEnable) - g_pTabDialog->SetMessageHighlight(si->pDlg); - else if (Chat::bFlashWindowHighlight) - si->pDlg->StartFlash(); -} - -static void OnFlashWindow(SESSION_INFO *si, int bInactive) -{ - if (!bInactive || si->pDlg == nullptr) - return; - - if (g_Settings.bTabsEnable) - g_pTabDialog->SetTabHighlight(si->pDlg); - else if (Chat::bFlashWindow) - si->pDlg->StartFlash(); -} - -static BOOL DoTrayIcon(SESSION_INFO *si, GCEVENT *gce) -{ - if (gce->iType & g_Settings.dwTrayIconFlags) - return oldDoTrayIcon(si, gce); - return TRUE; -} - -static BOOL DoPopup(SESSION_INFO *si, GCEVENT *gce) -{ - if (gce->iType & g_Settings.dwPopupFlags) - return oldDoPopup(si, gce); - return TRUE; -} - -static void OnLoadSettings() -{ - g_Settings.iX = db_get_dw(0, CHAT_MODULE, "roomx", -1); - g_Settings.iY = db_get_dw(0, CHAT_MODULE, "roomy", -1); - - g_Settings.bTabsEnable = db_get_b(0, CHAT_MODULE, "Tabs", 1) != 0; - g_Settings.bTabsAtBottom = db_get_b(0, CHAT_MODULE, "TabBottom", 0) != 0; - g_Settings.bTabCloseOnDblClick = db_get_b(0, CHAT_MODULE, "TabCloseOnDblClick", 0) != 0; - g_Settings.bAddColonToAutoComplete = db_get_b(0, CHAT_MODULE, "AddColonToAutoComplete", 1) != 0; - - g_Settings.iSplitterX = db_get_w(0, CHAT_MODULE, "SplitterX", 105); - if (g_Settings.iSplitterX <= 50) - g_Settings.iSplitterX = 105; - g_Settings.iSplitterY = db_get_w(0, CHAT_MODULE, "SplitterY", 90); - if (g_Settings.iSplitterY <= 65) - g_Settings.iSplitterY = 90; -} - -static void RegisterFonts() -{ - ColourIDW colourid = {}; - strncpy(colourid.dbSettingsGroup, CHAT_MODULE, sizeof(colourid.dbSettingsGroup)); - wcsncpy(colourid.group, LPGENW("Message sessions") L"/" LPGENW("Chat module"), _countof(colourid.group)); - - strncpy(colourid.setting, "ColorLogBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Group chat log background"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_WINDOW); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorMessageBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Message background"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_WINDOW); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorNicklistBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Nick list background"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_WINDOW); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorNicklistLines", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Nick list lines"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_INACTIVEBORDER); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorNicklistSelectedBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Nick list background (selected)"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_HIGHLIGHT); - g_plugin.addColor(&colourid); -} - -static void ShowRoom(SESSION_INFO *si) -{ - if (!si) - return; - - // Do we need to create a window? - if (si->pDlg == nullptr) { - CTabbedWindow *pContainer = GetContainer(); - if (g_Settings.bTabsEnable) { - pContainer->AddPage(si); - PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0); - } - else { - CMsgDialog *pDlg = pContainer->m_pEmbed = new CMsgDialog(pContainer, si); - pContainer->Create(); - pDlg->SetParent(pContainer->GetHwnd()); - pDlg->Create(); - pContainer->Show(); - pContainer->FixTabIcons(pDlg); - PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0); - } - - if (si->iType != GCW_SERVER) - si->pDlg->UpdateNickList(); - else - si->pDlg->UpdateTitle(); - si->pDlg->UpdateStatusBar(); - } - else if (g_Settings.bTabsEnable && g_pTabDialog) - g_pTabDialog->m_tab.ActivatePage(g_pTabDialog->m_tab.GetDlgIndex(si->pDlg)); - - SetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE) | WS_EX_APPWINDOW); - - if (IsIconic(si->pDlg->GetHwnd())) - si->pDlg->Show(SW_NORMAL); - si->pDlg->Show(SW_SHOW); - SetForegroundWindow(si->pDlg->GetHwnd()); -} - -int OnCheckPlugins(WPARAM, LPARAM) -{ - g_plugin.bSmileyInstalled = ServiceExists(MS_SMILEYADD_REPLACESMILEYS); - return 0; -} - -void Load_ChatModule() -{ - AddIcons(); - RegisterFonts(); - - CHAT_MANAGER_INITDATA data = { &g_Settings, sizeof(MODULEINFO), sizeof(SESSION_INFO), LPGENW("Message sessions") L"/" LPGENW("Chat module"), FONTMODE_USE, &g_plugin }; - Chat_CustomizeApi(&data); - - g_chatApi.MM_CreateModule = MM_CreateModule; - g_chatApi.OnReplaceSession = OnReplaceSession; - - g_chatApi.OnLoadSettings = OnLoadSettings; - g_chatApi.OnFlashWindow = OnFlashWindow; - g_chatApi.OnFlashHighlight = OnFlashHighlight; - g_chatApi.ShowRoom = ShowRoom; - - Srmm_CreateHotkey(LPGEN("Messaging"), LPGEN("Send message")); - - oldDoPopup = g_chatApi.DoPopup; g_chatApi.DoPopup = DoPopup; - oldDoTrayIcon = g_chatApi.DoTrayIcon; g_chatApi.DoTrayIcon = DoTrayIcon; - g_chatApi.ReloadSettings(); - - g_hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_MENU)); - - HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins); -} - -void Unload_ChatModule() -{ - db_set_w(0, CHAT_MODULE, "SplitterX", (uint16_t)g_Settings.iSplitterX); - db_set_w(0, CHAT_MODULE, "SplitterY", (uint16_t)g_Settings.iSplitterY); - db_set_dw(0, CHAT_MODULE, "roomx", g_Settings.iX); - db_set_dw(0, CHAT_MODULE, "roomy", g_Settings.iY); - db_set_dw(0, CHAT_MODULE, "roomwidth", g_Settings.iWidth); - db_set_dw(0, CHAT_MODULE, "roomheight", g_Settings.iHeight); - - DestroyMenu(g_hMenu); -} +/*
+Chat module plugin for Miranda IM
+
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+pfnDoTrayIcon oldDoTrayIcon;
+pfnDoPopup oldDoPopup;
+
+SESSION_INFO* SM_GetPrevWindow(SESSION_INFO *si)
+{
+ int i = g_chatApi.arSessions.indexOf(si);
+ if (i == -1)
+ return nullptr;
+
+ for (i--; i >= 0; i--) {
+ SESSION_INFO *p = g_chatApi.arSessions[i];
+ if (p->pDlg)
+ return p;
+ }
+
+ return nullptr;
+}
+
+SESSION_INFO* SM_GetNextWindow(SESSION_INFO *si)
+{
+ int i = g_chatApi.arSessions.indexOf(si);
+ if (i == -1)
+ return nullptr;
+
+ for (i++; i < g_chatApi.arSessions.getCount(); i++) {
+ SESSION_INFO *p = g_chatApi.arSessions[i];
+ if (p->pDlg)
+ return p;
+ }
+
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HMENU g_hMenu = nullptr;
+
+GlobalLogSettings g_Settings;
+
+static MODULEINFO* MM_CreateModule()
+{
+ return new MODULEINFO();
+}
+
+static void OnReplaceSession(SESSION_INFO *si)
+{
+ if (si->pDlg)
+ RedrawWindow(GetDlgItem(si->pDlg->GetHwnd(), IDC_SRMM_NICKLIST), nullptr, nullptr, RDW_INVALIDATE);
+}
+
+static void OnFlashHighlight(SESSION_INFO *si, int bInactive)
+{
+ if (!bInactive || si->pDlg == nullptr)
+ return;
+
+ if (g_Settings.bTabsEnable)
+ g_pTabDialog->SetMessageHighlight(si->pDlg);
+ else if (Chat::bFlashWindowHighlight)
+ si->pDlg->StartFlash();
+}
+
+static void OnFlashWindow(SESSION_INFO *si, int bInactive)
+{
+ if (!bInactive || si->pDlg == nullptr)
+ return;
+
+ if (g_Settings.bTabsEnable)
+ g_pTabDialog->SetTabHighlight(si->pDlg);
+ else if (Chat::bFlashWindow)
+ si->pDlg->StartFlash();
+}
+
+static BOOL DoTrayIcon(SESSION_INFO *si, GCEVENT *gce)
+{
+ if (gce->iType & g_Settings.dwTrayIconFlags)
+ return oldDoTrayIcon(si, gce);
+ return TRUE;
+}
+
+static BOOL DoPopup(SESSION_INFO *si, GCEVENT *gce)
+{
+ if (gce->iType & g_Settings.dwPopupFlags)
+ return oldDoPopup(si, gce);
+ return TRUE;
+}
+
+static void OnLoadSettings()
+{
+ g_Settings.iX = db_get_dw(0, CHAT_MODULE, "roomx", -1);
+ g_Settings.iY = db_get_dw(0, CHAT_MODULE, "roomy", -1);
+
+ g_Settings.bTabsEnable = db_get_b(0, CHAT_MODULE, "Tabs", 1) != 0;
+ g_Settings.bTabsAtBottom = db_get_b(0, CHAT_MODULE, "TabBottom", 0) != 0;
+ g_Settings.bTabCloseOnDblClick = db_get_b(0, CHAT_MODULE, "TabCloseOnDblClick", 0) != 0;
+ g_Settings.bAddColonToAutoComplete = db_get_b(0, CHAT_MODULE, "AddColonToAutoComplete", 1) != 0;
+
+ g_Settings.iSplitterX = db_get_w(0, CHAT_MODULE, "SplitterX", 105);
+ if (g_Settings.iSplitterX <= 50)
+ g_Settings.iSplitterX = 105;
+ g_Settings.iSplitterY = db_get_w(0, CHAT_MODULE, "SplitterY", 90);
+ if (g_Settings.iSplitterY <= 65)
+ g_Settings.iSplitterY = 90;
+}
+
+static void RegisterFonts()
+{
+ ColourIDW colourid = {};
+ strncpy(colourid.dbSettingsGroup, CHAT_MODULE, sizeof(colourid.dbSettingsGroup));
+ wcsncpy(colourid.group, LPGENW("Message sessions") L"/" LPGENW("Chat module"), _countof(colourid.group));
+
+ strncpy(colourid.setting, "ColorLogBG", _countof(colourid.setting));
+ wcsncpy(colourid.name, LPGENW("Group chat log background"), _countof(colourid.name));
+ colourid.defcolour = GetSysColor(COLOR_WINDOW);
+ g_plugin.addColor(&colourid);
+
+ strncpy(colourid.setting, "ColorMessageBG", _countof(colourid.setting));
+ wcsncpy(colourid.name, LPGENW("Message background"), _countof(colourid.name));
+ colourid.defcolour = GetSysColor(COLOR_WINDOW);
+ g_plugin.addColor(&colourid);
+
+ strncpy(colourid.setting, "ColorNicklistBG", _countof(colourid.setting));
+ wcsncpy(colourid.name, LPGENW("Nick list background"), _countof(colourid.name));
+ colourid.defcolour = GetSysColor(COLOR_WINDOW);
+ g_plugin.addColor(&colourid);
+
+ strncpy(colourid.setting, "ColorNicklistLines", _countof(colourid.setting));
+ wcsncpy(colourid.name, LPGENW("Nick list lines"), _countof(colourid.name));
+ colourid.defcolour = GetSysColor(COLOR_INACTIVEBORDER);
+ g_plugin.addColor(&colourid);
+
+ strncpy(colourid.setting, "ColorNicklistSelectedBG", _countof(colourid.setting));
+ wcsncpy(colourid.name, LPGENW("Nick list background (selected)"), _countof(colourid.name));
+ colourid.defcolour = GetSysColor(COLOR_HIGHLIGHT);
+ g_plugin.addColor(&colourid);
+}
+
+static void ShowRoom(SESSION_INFO *si)
+{
+ if (!si)
+ return;
+
+ // Do we need to create a window?
+ if (si->pDlg == nullptr) {
+ CTabbedWindow *pContainer = GetContainer();
+ if (g_Settings.bTabsEnable) {
+ pContainer->AddPage(si);
+ PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0);
+ }
+ else {
+ CMsgDialog *pDlg = pContainer->m_pEmbed = new CMsgDialog(pContainer, si);
+ pContainer->Create();
+ pDlg->SetParent(pContainer->GetHwnd());
+ pDlg->Create();
+ pContainer->Show();
+ pContainer->FixTabIcons(pDlg);
+ PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0);
+ }
+
+ if (si->iType != GCW_SERVER)
+ si->pDlg->UpdateNickList();
+ else
+ si->pDlg->UpdateTitle();
+ si->pDlg->UpdateStatusBar();
+ }
+ else if (g_Settings.bTabsEnable && g_pTabDialog)
+ g_pTabDialog->m_tab.ActivatePage(g_pTabDialog->m_tab.GetDlgIndex(si->pDlg));
+
+ SetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE) | WS_EX_APPWINDOW);
+
+ if (IsIconic(si->pDlg->GetHwnd()))
+ si->pDlg->Show(SW_NORMAL);
+ si->pDlg->Show(SW_SHOW);
+ SetForegroundWindow(si->pDlg->GetHwnd());
+}
+
+int OnCheckPlugins(WPARAM, LPARAM)
+{
+ g_plugin.bSmileyInstalled = ServiceExists(MS_SMILEYADD_REPLACESMILEYS);
+ return 0;
+}
+
+void Load_ChatModule()
+{
+ AddIcons();
+ RegisterFonts();
+
+ CHAT_MANAGER_INITDATA data = { &g_Settings, sizeof(MODULEINFO), sizeof(SESSION_INFO), LPGENW("Message sessions") L"/" LPGENW("Chat module"), FONTMODE_USE, &g_plugin };
+ Chat_CustomizeApi(&data);
+
+ g_chatApi.MM_CreateModule = MM_CreateModule;
+ g_chatApi.OnReplaceSession = OnReplaceSession;
+
+ g_chatApi.OnLoadSettings = OnLoadSettings;
+ g_chatApi.OnFlashWindow = OnFlashWindow;
+ g_chatApi.OnFlashHighlight = OnFlashHighlight;
+ g_chatApi.ShowRoom = ShowRoom;
+
+ Srmm_CreateHotkey(LPGEN("Messaging"), LPGEN("Send message"));
+
+ oldDoPopup = g_chatApi.DoPopup; g_chatApi.DoPopup = DoPopup;
+ oldDoTrayIcon = g_chatApi.DoTrayIcon; g_chatApi.DoTrayIcon = DoTrayIcon;
+ g_chatApi.ReloadSettings();
+
+ g_hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_MENU));
+
+ HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins);
+}
+
+void Unload_ChatModule()
+{
+ db_set_w(0, CHAT_MODULE, "SplitterX", (uint16_t)g_Settings.iSplitterX);
+ db_set_w(0, CHAT_MODULE, "SplitterY", (uint16_t)g_Settings.iSplitterY);
+ db_set_dw(0, CHAT_MODULE, "roomx", g_Settings.iX);
+ db_set_dw(0, CHAT_MODULE, "roomy", g_Settings.iY);
+ db_set_dw(0, CHAT_MODULE, "roomwidth", g_Settings.iWidth);
+ db_set_dw(0, CHAT_MODULE, "roomheight", g_Settings.iHeight);
+
+ DestroyMenu(g_hMenu);
+}
diff --git a/src/core/stdmsg/src/chat_window.cpp b/src/core/stdmsg/src/chat_window.cpp index 52106ae755..8e1871dd4c 100644 --- a/src/core/stdmsg/src/chat_window.cpp +++ b/src/core/stdmsg/src/chat_window.cpp @@ -1,275 +1,275 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::LoadSettings() -{ - m_clrInputBG = db_get_dw(0, CHAT_MODULE, "ColorMessageBG", GetSysColor(COLOR_WINDOW)); - LoadMsgDlgFont(MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::ShowFilterMenu() -{ - HWND hwnd = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_hwnd, FilterWndProc, (LPARAM)this); - TranslateDialogDefault(hwnd); - - RECT rc; - GetWindowRect(m_btnFilter.GetHwnd(), &rc); - SetWindowPos(hwnd, HWND_TOP, rc.left - 85, (IsWindowVisible(m_btnFilter.GetHwnd()) || IsWindowVisible(m_btnBold.GetHwnd())) ? rc.top - 206 : rc.top - 186, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); -} - -void CMsgDialog::UpdateNickList() -{ - int i = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); - m_nickList.SendMsg(LB_SETCOUNT, m_si->getUserList().getCount(), 0); - m_nickList.SendMsg(LB_SETTOPINDEX, i, 0); - - UpdateTitle(); -} - -void CMsgDialog::UpdateOptions() -{ - m_btnNickList.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bNicklistEnabled ? IDI_NICKLIST2 : IDI_NICKLIST, FALSE)); - m_btnFilter.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bFilterEnabled ? IDI_FILTER2 : IDI_FILTER, FALSE)); - - HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT); - SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon); - DestroyIcon(hIcon); - - Window_SetIcon_IcoLib(m_pOwner->GetHwnd(), g_plugin.getIconHandle(IDI_CHANMGR)); - - m_pLog->UpdateOptions(); - - // nicklist - int ih = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListFont, FALSE); - int ih2 = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListHeadingsFont, FALSE); - int height = db_get_b(0, CHAT_MODULE, "NicklistRowDist", 12); - int font = ih > ih2 ? ih : ih2; - - // make sure we have space for icon! - if (g_Settings.bShowContactStatus) - font = font > 16 ? font : 16; - - m_nickList.SendMsg(LB_SETITEMHEIGHT, 0, height > font ? height : font); - InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE); - - CSuper::UpdateOptions(); -} - -void CMsgDialog::UpdateStatusBar() -{ - wchar_t *ptszDispName = m_si->pMI->ptszModDispName; - int x = 12; - x += Chat_GetTextPixelSize(ptszDispName, (HFONT)SendMessage(m_pOwner->m_hwndStatus, WM_GETFONT, 0, 0), TRUE); - x += GetSystemMetrics(SM_CXSMICON); - int iStatusbarParts[2] = { x, -1 }; - SendMessage(m_pOwner->m_hwndStatus, SB_SETPARTS, 2, (LPARAM)&iStatusbarParts); - - HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT); - SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon); - DestroyIcon(hIcon); - - SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)ptszDispName); - SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L"")); - SendMessage(m_pOwner->m_hwndStatus, SB_SETTIPTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L"")); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CLogWindow::LogEvents(LOGINFO *lin, bool bRedraw) -{ - auto *si = m_pDlg.m_si; - if (lin == nullptr || si == nullptr) - return; - - if (!bRedraw && si->iType == GCW_CHATROOM && m_pDlg.m_bFilterEnabled && (m_pDlg.m_iLogFilterFlags & lin->iType) == 0) - return; - - LOGSTREAMDATA streamData; - memset(&streamData, 0, sizeof(streamData)); - streamData.hwnd = m_rtf.GetHwnd(); - streamData.si = si; - streamData.lin = lin; - streamData.bStripFormat = FALSE; - - bool bFlag = false; - - EDITSTREAM stream = {}; - stream.pfnCallback = Srmm_LogStreamCallback; - stream.dwCookie = (DWORD_PTR)& streamData; - - SCROLLINFO scroll; - scroll.cbSize = sizeof(SCROLLINFO); - scroll.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; - GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &scroll); - - POINT point = {}; - m_rtf.SendMsg(EM_GETSCROLLPOS, 0, (LPARAM)&point); - - // do not scroll to bottom if there is a selection - CHARRANGE oldsel, sel; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&oldsel); - if (oldsel.cpMax != oldsel.cpMin) - m_rtf.SetDraw(false); - - //set the insertion point at the bottom - sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - - // fix for the indent... must be a M$ bug - if (sel.cpMax == 0) - bRedraw = TRUE; - - // should the event(s) be appended to the current log - WPARAM wp = bRedraw ? SF_RTF : SFF_SELECTION | SF_RTF; - - //get the number of pixels per logical inch - if (bRedraw) { - HDC hdc = GetDC(nullptr); - g_chatApi.logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY); - g_chatApi.logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX); - ReleaseDC(nullptr, hdc); - m_rtf.SetDraw(false); - bFlag = true; - } - - // stream in the event(s) - streamData.lin = lin; - streamData.bRedraw = bRedraw; - m_rtf.SendMsg(EM_STREAMIN, wp, (LPARAM)&stream); - - // do smileys - if (g_plugin.bSmileyInstalled && (bRedraw || (lin->ptszText && lin->iType != GC_EVENT_JOIN && lin->iType != GC_EVENT_NICK && lin->iType != GC_EVENT_ADDSTATUS && lin->iType != GC_EVENT_REMOVESTATUS))) { - CHARRANGE newsel; - newsel.cpMax = -1; - newsel.cpMin = sel.cpMin; - if (newsel.cpMin < 0) - newsel.cpMin = 0; - - SMADD_RICHEDIT3 sm = {}; - sm.cbSize = sizeof(sm); - sm.hwndRichEditControl = m_rtf.GetHwnd(); - sm.Protocolname = si->pszModule; - sm.rangeToReplace = bRedraw ? nullptr : &newsel; - sm.disableRedraw = TRUE; - sm.hContact = si->hContact; - CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&sm); - } - - // scroll log to bottom if the log was previously scrolled to bottom, else restore old position - if (bRedraw || (UINT)scroll.nPos >= (UINT)scroll.nMax - scroll.nPage - 5 || scroll.nMax - scroll.nMin - scroll.nPage < 50) - ScrollToBottom(); - else - m_rtf.SendMsg(EM_SETSCROLLPOS, 0, (LPARAM)&point); - - // do we need to restore the selection - if (oldsel.cpMax != oldsel.cpMin) { - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&oldsel); - m_rtf.SetDraw(true); - InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); - } - - // need to invalidate the window - if (bFlag) { - sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - m_rtf.SetDraw(true); - InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - static CMsgDialog *pDlg = nullptr; - switch (uMsg) { - case WM_INITDIALOG: - pDlg = (CMsgDialog*)lParam; - CheckDlgButton(hwndDlg, IDC_1, pDlg->m_iLogFilterFlags & GC_EVENT_ACTION ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_2, pDlg->m_iLogFilterFlags & GC_EVENT_MESSAGE ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_3, pDlg->m_iLogFilterFlags & GC_EVENT_NICK ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_4, pDlg->m_iLogFilterFlags & GC_EVENT_JOIN ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_5, pDlg->m_iLogFilterFlags & GC_EVENT_PART ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_6, pDlg->m_iLogFilterFlags & GC_EVENT_TOPIC ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_7, pDlg->m_iLogFilterFlags & GC_EVENT_ADDSTATUS ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_8, pDlg->m_iLogFilterFlags & GC_EVENT_INFORMATION ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_9, pDlg->m_iLogFilterFlags & GC_EVENT_QUIT ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_10, pDlg->m_iLogFilterFlags & GC_EVENT_KICK ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_11, pDlg->m_iLogFilterFlags & GC_EVENT_NOTICE ? BST_CHECKED : BST_UNCHECKED); - break; - - case WM_CTLCOLOREDIT: - case WM_CTLCOLORSTATIC: - SetTextColor((HDC)wParam, RGB(60, 60, 150)); - SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); - return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); - - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_INACTIVE) { - int iFlags = 0; - - if (IsDlgButtonChecked(hwndDlg, IDC_1) == BST_CHECKED) - iFlags |= GC_EVENT_ACTION; - if (IsDlgButtonChecked(hwndDlg, IDC_2) == BST_CHECKED) - iFlags |= GC_EVENT_MESSAGE; - if (IsDlgButtonChecked(hwndDlg, IDC_3) == BST_CHECKED) - iFlags |= GC_EVENT_NICK; - if (IsDlgButtonChecked(hwndDlg, IDC_4) == BST_CHECKED) - iFlags |= GC_EVENT_JOIN; - if (IsDlgButtonChecked(hwndDlg, IDC_5) == BST_CHECKED) - iFlags |= GC_EVENT_PART; - if (IsDlgButtonChecked(hwndDlg, IDC_6) == BST_CHECKED) - iFlags |= GC_EVENT_TOPIC; - if (IsDlgButtonChecked(hwndDlg, IDC_7) == BST_CHECKED) - iFlags |= GC_EVENT_ADDSTATUS; - if (IsDlgButtonChecked(hwndDlg, IDC_8) == BST_CHECKED) - iFlags |= GC_EVENT_INFORMATION; - if (IsDlgButtonChecked(hwndDlg, IDC_9) == BST_CHECKED) - iFlags |= GC_EVENT_QUIT; - if (IsDlgButtonChecked(hwndDlg, IDC_10) == BST_CHECKED) - iFlags |= GC_EVENT_KICK; - if (IsDlgButtonChecked(hwndDlg, IDC_11) == BST_CHECKED) - iFlags |= GC_EVENT_NOTICE; - - if (iFlags & GC_EVENT_ADDSTATUS) - iFlags |= GC_EVENT_REMOVESTATUS; - - pDlg->m_iLogFilterFlags = iFlags; - if (pDlg->m_bFilterEnabled) - pDlg->RedrawLog(); - PostMessage(hwndDlg, WM_CLOSE, 0, 0); - } - break; - - case WM_CLOSE: - DestroyWindow(hwndDlg); - break; - } - - return FALSE; -} +/*
+Chat module plugin for Miranda IM
+
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadSettings()
+{
+ m_clrInputBG = db_get_dw(0, CHAT_MODULE, "ColorMessageBG", GetSysColor(COLOR_WINDOW));
+ LoadMsgDlgFont(MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::ShowFilterMenu()
+{
+ HWND hwnd = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_hwnd, FilterWndProc, (LPARAM)this);
+ TranslateDialogDefault(hwnd);
+
+ RECT rc;
+ GetWindowRect(m_btnFilter.GetHwnd(), &rc);
+ SetWindowPos(hwnd, HWND_TOP, rc.left - 85, (IsWindowVisible(m_btnFilter.GetHwnd()) || IsWindowVisible(m_btnBold.GetHwnd())) ? rc.top - 206 : rc.top - 186, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
+}
+
+void CMsgDialog::UpdateNickList()
+{
+ int i = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0);
+ m_nickList.SendMsg(LB_SETCOUNT, m_si->getUserList().getCount(), 0);
+ m_nickList.SendMsg(LB_SETTOPINDEX, i, 0);
+
+ UpdateTitle();
+}
+
+void CMsgDialog::UpdateOptions()
+{
+ m_btnNickList.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bNicklistEnabled ? IDI_NICKLIST2 : IDI_NICKLIST, FALSE));
+ m_btnFilter.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bFilterEnabled ? IDI_FILTER2 : IDI_FILTER, FALSE));
+
+ HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT);
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon);
+ DestroyIcon(hIcon);
+
+ Window_SetIcon_IcoLib(m_pOwner->GetHwnd(), g_plugin.getIconHandle(IDI_CHANMGR));
+
+ m_pLog->UpdateOptions();
+
+ // nicklist
+ int ih = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListFont, FALSE);
+ int ih2 = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListHeadingsFont, FALSE);
+ int height = db_get_b(0, CHAT_MODULE, "NicklistRowDist", 12);
+ int font = ih > ih2 ? ih : ih2;
+
+ // make sure we have space for icon!
+ if (g_Settings.bShowContactStatus)
+ font = font > 16 ? font : 16;
+
+ m_nickList.SendMsg(LB_SETITEMHEIGHT, 0, height > font ? height : font);
+ InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE);
+
+ CSuper::UpdateOptions();
+}
+
+void CMsgDialog::UpdateStatusBar()
+{
+ wchar_t *ptszDispName = m_si->pMI->ptszModDispName;
+ int x = 12;
+ x += Chat_GetTextPixelSize(ptszDispName, (HFONT)SendMessage(m_pOwner->m_hwndStatus, WM_GETFONT, 0, 0), TRUE);
+ x += GetSystemMetrics(SM_CXSMICON);
+ int iStatusbarParts[2] = { x, -1 };
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETPARTS, 2, (LPARAM)&iStatusbarParts);
+
+ HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT);
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon);
+ DestroyIcon(hIcon);
+
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)ptszDispName);
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L""));
+ SendMessage(m_pOwner->m_hwndStatus, SB_SETTIPTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L""));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CLogWindow::LogEvents(LOGINFO *lin, bool bRedraw)
+{
+ auto *si = m_pDlg.m_si;
+ if (lin == nullptr || si == nullptr)
+ return;
+
+ if (!bRedraw && si->iType == GCW_CHATROOM && m_pDlg.m_bFilterEnabled && (m_pDlg.m_iLogFilterFlags & lin->iType) == 0)
+ return;
+
+ LOGSTREAMDATA streamData;
+ memset(&streamData, 0, sizeof(streamData));
+ streamData.hwnd = m_rtf.GetHwnd();
+ streamData.si = si;
+ streamData.lin = lin;
+ streamData.bStripFormat = FALSE;
+
+ bool bFlag = false;
+
+ EDITSTREAM stream = {};
+ stream.pfnCallback = Srmm_LogStreamCallback;
+ stream.dwCookie = (DWORD_PTR)& streamData;
+
+ SCROLLINFO scroll;
+ scroll.cbSize = sizeof(SCROLLINFO);
+ scroll.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
+ GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &scroll);
+
+ POINT point = {};
+ m_rtf.SendMsg(EM_GETSCROLLPOS, 0, (LPARAM)&point);
+
+ // do not scroll to bottom if there is a selection
+ CHARRANGE oldsel, sel;
+ m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&oldsel);
+ if (oldsel.cpMax != oldsel.cpMin)
+ m_rtf.SetDraw(false);
+
+ //set the insertion point at the bottom
+ sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength();
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel);
+
+ // fix for the indent... must be a M$ bug
+ if (sel.cpMax == 0)
+ bRedraw = TRUE;
+
+ // should the event(s) be appended to the current log
+ WPARAM wp = bRedraw ? SF_RTF : SFF_SELECTION | SF_RTF;
+
+ //get the number of pixels per logical inch
+ if (bRedraw) {
+ HDC hdc = GetDC(nullptr);
+ g_chatApi.logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY);
+ g_chatApi.logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX);
+ ReleaseDC(nullptr, hdc);
+ m_rtf.SetDraw(false);
+ bFlag = true;
+ }
+
+ // stream in the event(s)
+ streamData.lin = lin;
+ streamData.bRedraw = bRedraw;
+ m_rtf.SendMsg(EM_STREAMIN, wp, (LPARAM)&stream);
+
+ // do smileys
+ if (g_plugin.bSmileyInstalled && (bRedraw || (lin->ptszText && lin->iType != GC_EVENT_JOIN && lin->iType != GC_EVENT_NICK && lin->iType != GC_EVENT_ADDSTATUS && lin->iType != GC_EVENT_REMOVESTATUS))) {
+ CHARRANGE newsel;
+ newsel.cpMax = -1;
+ newsel.cpMin = sel.cpMin;
+ if (newsel.cpMin < 0)
+ newsel.cpMin = 0;
+
+ SMADD_RICHEDIT3 sm = {};
+ sm.cbSize = sizeof(sm);
+ sm.hwndRichEditControl = m_rtf.GetHwnd();
+ sm.Protocolname = si->pszModule;
+ sm.rangeToReplace = bRedraw ? nullptr : &newsel;
+ sm.disableRedraw = TRUE;
+ sm.hContact = si->hContact;
+ CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&sm);
+ }
+
+ // scroll log to bottom if the log was previously scrolled to bottom, else restore old position
+ if (bRedraw || (UINT)scroll.nPos >= (UINT)scroll.nMax - scroll.nPage - 5 || scroll.nMax - scroll.nMin - scroll.nPage < 50)
+ ScrollToBottom();
+ else
+ m_rtf.SendMsg(EM_SETSCROLLPOS, 0, (LPARAM)&point);
+
+ // do we need to restore the selection
+ if (oldsel.cpMax != oldsel.cpMin) {
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&oldsel);
+ m_rtf.SetDraw(true);
+ InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE);
+ }
+
+ // need to invalidate the window
+ if (bFlag) {
+ sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength();
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel);
+ m_rtf.SetDraw(true);
+ InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static CMsgDialog *pDlg = nullptr;
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ pDlg = (CMsgDialog*)lParam;
+ CheckDlgButton(hwndDlg, IDC_1, pDlg->m_iLogFilterFlags & GC_EVENT_ACTION ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_2, pDlg->m_iLogFilterFlags & GC_EVENT_MESSAGE ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_3, pDlg->m_iLogFilterFlags & GC_EVENT_NICK ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_4, pDlg->m_iLogFilterFlags & GC_EVENT_JOIN ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_5, pDlg->m_iLogFilterFlags & GC_EVENT_PART ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_6, pDlg->m_iLogFilterFlags & GC_EVENT_TOPIC ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_7, pDlg->m_iLogFilterFlags & GC_EVENT_ADDSTATUS ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_8, pDlg->m_iLogFilterFlags & GC_EVENT_INFORMATION ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_9, pDlg->m_iLogFilterFlags & GC_EVENT_QUIT ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_10, pDlg->m_iLogFilterFlags & GC_EVENT_KICK ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_11, pDlg->m_iLogFilterFlags & GC_EVENT_NOTICE ? BST_CHECKED : BST_UNCHECKED);
+ break;
+
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORSTATIC:
+ SetTextColor((HDC)wParam, RGB(60, 60, 150));
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
+
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) == WA_INACTIVE) {
+ int iFlags = 0;
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_1) == BST_CHECKED)
+ iFlags |= GC_EVENT_ACTION;
+ if (IsDlgButtonChecked(hwndDlg, IDC_2) == BST_CHECKED)
+ iFlags |= GC_EVENT_MESSAGE;
+ if (IsDlgButtonChecked(hwndDlg, IDC_3) == BST_CHECKED)
+ iFlags |= GC_EVENT_NICK;
+ if (IsDlgButtonChecked(hwndDlg, IDC_4) == BST_CHECKED)
+ iFlags |= GC_EVENT_JOIN;
+ if (IsDlgButtonChecked(hwndDlg, IDC_5) == BST_CHECKED)
+ iFlags |= GC_EVENT_PART;
+ if (IsDlgButtonChecked(hwndDlg, IDC_6) == BST_CHECKED)
+ iFlags |= GC_EVENT_TOPIC;
+ if (IsDlgButtonChecked(hwndDlg, IDC_7) == BST_CHECKED)
+ iFlags |= GC_EVENT_ADDSTATUS;
+ if (IsDlgButtonChecked(hwndDlg, IDC_8) == BST_CHECKED)
+ iFlags |= GC_EVENT_INFORMATION;
+ if (IsDlgButtonChecked(hwndDlg, IDC_9) == BST_CHECKED)
+ iFlags |= GC_EVENT_QUIT;
+ if (IsDlgButtonChecked(hwndDlg, IDC_10) == BST_CHECKED)
+ iFlags |= GC_EVENT_KICK;
+ if (IsDlgButtonChecked(hwndDlg, IDC_11) == BST_CHECKED)
+ iFlags |= GC_EVENT_NOTICE;
+
+ if (iFlags & GC_EVENT_ADDSTATUS)
+ iFlags |= GC_EVENT_REMOVESTATUS;
+
+ pDlg->m_iLogFilterFlags = iFlags;
+ if (pDlg->m_bFilterEnabled)
+ pDlg->RedrawLog();
+ PostMessage(hwndDlg, WM_CLOSE, 0, 0);
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+ }
+
+ return FALSE;
+}
diff --git a/src/core/stdmsg/src/cmdlist.cpp b/src/core/stdmsg/src/cmdlist.cpp index beb669dcf5..1ace5b0545 100644 --- a/src/core/stdmsg/src/cmdlist.cpp +++ b/src/core/stdmsg/src/cmdlist.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/cmdlist.h b/src/core/stdmsg/src/cmdlist.h index bbe8c9b11b..fda5562689 100644 --- a/src/core/stdmsg/src/cmdlist.h +++ b/src/core/stdmsg/src/cmdlist.h @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/globals.cpp b/src/core/stdmsg/src/globals.cpp index de2ca11c6d..5d2b95cc46 100644 --- a/src/core/stdmsg/src/globals.cpp +++ b/src/core/stdmsg/src/globals.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msgdialog.cpp b/src/core/stdmsg/src/msgdialog.cpp index 8e0c4ff3ed..359b308022 100644 --- a/src/core/stdmsg/src/msgdialog.cpp +++ b/src/core/stdmsg/src/msgdialog.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msglog.cpp b/src/core/stdmsg/src/msglog.cpp index 8fb1f5edbd..791cd1b71e 100644 --- a/src/core/stdmsg/src/msglog.cpp +++ b/src/core/stdmsg/src/msglog.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msgoptions.cpp b/src/core/stdmsg/src/msgoptions.cpp index 590afa2478..34dc8e044f 100644 --- a/src/core/stdmsg/src/msgoptions.cpp +++ b/src/core/stdmsg/src/msgoptions.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msgs.cpp b/src/core/stdmsg/src/msgs.cpp index 95b209fd02..9f5be7be28 100644 --- a/src/core/stdmsg/src/msgs.cpp +++ b/src/core/stdmsg/src/msgs.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msgs.h b/src/core/stdmsg/src/msgs.h index f21110e641..0e9f38dead 100644 --- a/src/core/stdmsg/src/msgs.h +++ b/src/core/stdmsg/src/msgs.h @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/msgtimedout.cpp b/src/core/stdmsg/src/msgtimedout.cpp index 19744fe695..828924580e 100644 --- a/src/core/stdmsg/src/msgtimedout.cpp +++ b/src/core/stdmsg/src/msgtimedout.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/srmm.cpp b/src/core/stdmsg/src/srmm.cpp index 69c350aad7..4485d40bb3 100644 --- a/src/core/stdmsg/src/srmm.cpp +++ b/src/core/stdmsg/src/srmm.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/statusicon.cpp b/src/core/stdmsg/src/statusicon.cpp index 4244f9037c..6255dc3e1e 100644 --- a/src/core/stdmsg/src/statusicon.cpp +++ b/src/core/stdmsg/src/statusicon.cpp @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/stdafx.cxx b/src/core/stdmsg/src/stdafx.cxx index 52ec2d6817..e23069a5b8 100644 --- a/src/core/stdmsg/src/stdafx.cxx +++ b/src/core/stdmsg/src/stdafx.cxx @@ -1,6 +1,6 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/core/stdmsg/src/stdafx.h b/src/core/stdmsg/src/stdafx.h index a42feafac9..33d557a6a9 100644 --- a/src/core/stdmsg/src/stdafx.h +++ b/src/core/stdmsg/src/stdafx.h @@ -1,6 +1,6 @@ /*
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stdmsg/src/tabs.cpp b/src/core/stdmsg/src/tabs.cpp index d60bec7634..402d1f2f0f 100644 --- a/src/core/stdmsg/src/tabs.cpp +++ b/src/core/stdmsg/src/tabs.cpp @@ -1,668 +1,668 @@ -/* - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "statusicon.h" - -CTabbedWindow *g_pTabDialog = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// - -CTabbedWindow* GetContainer() -{ - if (g_Settings.bTabsEnable) { - if (g_pTabDialog == nullptr) { - g_pTabDialog = new CTabbedWindow(); - g_pTabDialog->Show(); - } - return g_pTabDialog; - } - - return new CTabbedWindow(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK TabSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CTabbedWindow *pOwner = (CTabbedWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - TCHITTESTINFO tci = {}; - - static bool bDragging = false; - static int iBeginIndex = 0; - switch (msg) { - case WM_LBUTTONDOWN: - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - if (DragDetect(hwnd, tci.pt) && TabCtrl_GetItemCount(hwnd) > 1) { - tci.flags = TCHT_ONITEM; - ScreenToClient(hwnd, &tci.pt); - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1) { - CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); - if (pDlg) { - bDragging = true; - iBeginIndex = idx; - ImageList_BeginDrag(Clist_GetImageList(), pDlg->GetImageId(), 8, 8); - ImageList_DragEnter(hwnd, tci.pt.x, tci.pt.y); - SetCapture(hwnd); - } - return TRUE; - } - } - else pOwner->TabClicked(); - break; - - case WM_CAPTURECHANGED: - bDragging = false; - ImageList_DragLeave(hwnd); - ImageList_EndDrag(); - break; - - case WM_MOUSEMOVE: - if (bDragging) { - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(hwnd, &tci.pt); - ImageList_DragMove(tci.pt.x, tci.pt.y); - } - break; - - case WM_LBUTTONUP: - if (bDragging && ReleaseCapture()) { - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - tci.flags = TCHT_ONITEM; - bDragging = false; - ImageList_DragLeave(hwnd); - ImageList_EndDrag(); - - ScreenToClient(hwnd, &tci.pt); - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1 && idx != iBeginIndex) - pOwner->DropTab(idx, iBeginIndex); - } - break; - - case WM_LBUTTONDBLCLK: - if (g_Settings.bTabCloseOnDblClick) { - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(hwnd, &tci.pt); - - tci.flags = TCHT_ONITEM; - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1) { - CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); - if (pDlg) - pDlg->CloseTab(); - } - } - break; - - case WM_MBUTTONUP: - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - tci.flags = TCHT_ONITEM; - - ScreenToClient(hwnd, &tci.pt); - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1) { - CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); - if (pDlg) - pDlg->CloseTab(); - } - break; - } - - return mir_callNextSubclass(hwnd, TabSubclassProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CTabbedWindow::CTabbedWindow() : - CDlgBase(g_plugin, IDD_CONTAINER), - m_tab(this, IDC_TAB) -{ - SetMinSize(450, 350); -} - -bool CTabbedWindow::OnInitDialog() -{ - SetWindowLongPtr(m_tab.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - mir_subclassWindow(m_tab.GetHwnd(), ::TabSubclassProc); - - m_hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, nullptr, WS_CHILD | WS_VISIBLE | SBT_TOOLTIPS | SBARS_SIZEGRIP, 0, 0, 0, 0, m_hwnd, nullptr, g_plugin.getInst(), nullptr); - SendMessage(m_hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON), 0); - - RECT rc; - GetWindowRect(m_hwndStatus, &rc); - m_statusHeight = rc.bottom - rc.top; - - SetWindowPosition(); - - if (!g_Settings.bTabsEnable) { - m_tab.Hide(); - return false; - } - - LONG_PTR mask = GetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE); - if (g_Settings.bTabsAtBottom) - mask |= TCS_BOTTOM; - else - mask &= ~TCS_BOTTOM; - SetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE, mask); - - TabCtrl_SetMinTabWidth(m_tab.GetHwnd(), 80); - TabCtrl_SetImageList(m_tab.GetHwnd(), Clist_GetImageList()); - return true; -} - -void CTabbedWindow::OnDestroy() -{ - DestroyWindow(m_hwndStatus); m_hwndStatus = nullptr; - - SaveWindowPosition(true); - - Utils_SaveWindowPosition(m_hwnd, g_plugin.bSavePerContact ? ((m_pEmbed == nullptr) ? 0 : m_pEmbed->m_hContact) : 0, CHAT_MODULE, "room"); - - if (m_pEmbed == nullptr) - g_pTabDialog = nullptr; -} - -void CTabbedWindow::OnResize() -{ - CDlgBase::OnResize(); - - SendMessage(m_hwndStatus, WM_SIZE, 0, 0); - - if (m_pEmbed) { - RECT rc; - GetClientRect(m_tab.GetHwnd(), &rc); - MoveWindow(m_pEmbed->GetHwnd(), 0, 0, rc.right - rc.left, rc.bottom - rc.top, FALSE); - } - - SaveWindowPosition(false); -} - -int CTabbedWindow::Resizer(UTILRESIZECONTROL *urc) -{ - if (urc->wId == IDC_TAB) { - urc->rcItem.top = 1; - urc->rcItem.bottom = urc->dlgNewSize.cy - m_statusHeight - 1; - return RD_ANCHORX_WIDTH | RD_ANCHORY_CUSTOM; - } - - return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; // status bar -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CTabbedWindow* CTabbedWindow::AddPage(MCONTACT hContact, wchar_t *pwszText, int iNoActivate) -{ - CMsgDialog *pDlg = new CMsgDialog(this, hContact); - pDlg->m_wszInitialText = pwszText; - if (iNoActivate != -1) - pDlg->m_bNoActivate = iNoActivate != 0; - - if (g_Settings.bTabsEnable) { - m_tab.AddPage(Clist_GetContactDisplayName(hContact), nullptr, pDlg); - FixTabIcons(pDlg); - - m_tab.ActivatePage(m_tab.GetCount() - 1); - } - else { - m_pEmbed = pDlg; - Create(); - pDlg->SetParent(m_hwnd); - pDlg->Create(); - Show(); - } - - PostMessage(m_hwnd, WM_SIZE, 0, 0); - return this; -} - -void CTabbedWindow::AddPage(SESSION_INFO *si, int insertAt) -{ - // does the tab already exist? - int indexfound = (si->pDlg) ? m_tab.GetDlgIndex(si->pDlg) : -1; - if (indexfound == -1) { // create a new tab - wchar_t szTemp[30]; - mir_wstrncpy(szTemp, si->ptszName, 21); - if (mir_wstrlen(si->ptszName) > 20) - mir_wstrncpy(szTemp + 20, L"...", 4); - - if (!IsWindowVisible(m_hwnd)) - Show(SW_SHOW); - - CMsgDialog *pDlg = new CMsgDialog(this, si); - pDlg->SetParent(m_hwnd); - m_tab.AddPage(szTemp, nullptr, pDlg); - m_tab.ActivatePage(m_tab.GetCount() - 1); - FixTabIcons(pDlg); - } - else if (insertAt == -1) - m_tab.ActivatePage(indexfound); -} - -CMsgDialog* CTabbedWindow::CurrPage() const -{ - return (m_pEmbed != nullptr) ? m_pEmbed : (CMsgDialog*)m_tab.GetActivePage(); -} - -void CTabbedWindow::DropTab(int begin, int end) -{ - if (begin == end) - return; - - m_tab.SwapPages(begin, end); - - CMsgDialog *pDlg = (CMsgDialog *)m_tab.GetNthPage(end); - if (pDlg) { - FixTabIcons(pDlg); - m_tab.ActivatePage(end); - } - - // fix the "fixed" positions - int tabCount = m_tab.GetCount(); - for (int i = 0; i < tabCount; i++) { - pDlg = (CMsgDialog *)m_tab.GetNthPage(i); - if (pDlg == nullptr) - continue; - - SESSION_INFO *si = pDlg->m_si; - if (si && si->hContact && db_get_w(si->hContact, si->pszModule, "TabPosition", 0) != 0) - db_set_w(si->hContact, si->pszModule, "TabPosition", i + 1); - } -} - -void CTabbedWindow::FixTabIcons(CMsgDialog *pDlg) -{ - if (pDlg == nullptr) - return; - - int image = pDlg->GetImageId(); - - // if tabs are turned off, simply change the window's icon, otherwise set the tab's icon first - if (m_pEmbed == nullptr) { - int idx = m_tab.GetDlgIndex(pDlg); - if (idx == -1) - return; - - TCITEM tci = {}; - tci.mask = TCIF_IMAGE; - TabCtrl_GetItem(m_tab.GetHwnd(), idx, &tci); - if (tci.iImage != image) { - tci.iImage = image; - TabCtrl_SetItem(m_tab.GetHwnd(), idx, &tci); - } - } - - // set the container's icon only if we're processing the current page - if (pDlg == CurrPage()) { - Window_FreeIcon_IcoLib(m_hwnd); - if (g_plugin.bUseStatusWinIcon) - Window_SetProtoIcon_IcoLib(m_hwnd, pDlg->m_szProto, pDlg->m_wStatus); - else if (pDlg->isChat()) - Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_CHANMGR)); - else - Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_EVENT_MESSAGE); - } -} - -void CTabbedWindow::RemoveTab(CMsgDialog *pDlg) -{ - int idx = m_tab.GetDlgIndex(pDlg); - if (idx == -1) - return; - - m_tab.RemovePage(idx); - if (m_tab.GetCount() == 0) - PostMessage(m_hwnd, WM_CLOSE, 0, 0); - else { - if (m_tab.GetNthPage(idx) == nullptr) - idx--; - m_tab.ActivatePage(idx); - } -} - -void CTabbedWindow::SaveWindowPosition(bool bUpdateSession) -{ - WINDOWPLACEMENT wp = {}; - wp.length = sizeof(wp); - GetWindowPlacement(m_hwnd, &wp); - - g_Settings.iX = wp.rcNormalPosition.left; - g_Settings.iY = wp.rcNormalPosition.top; - g_Settings.iWidth = wp.rcNormalPosition.right - wp.rcNormalPosition.left; - g_Settings.iHeight = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; - - if (bUpdateSession) { - iX = g_Settings.iX; - iY = g_Settings.iY; - iWidth = g_Settings.iWidth; - iHeight = g_Settings.iHeight; - } -} - -void CTabbedWindow::SetMessageHighlight(CMsgDialog *pDlg) -{ - if (m_tab.GetDlgIndex(pDlg) == -1) - return; - - pDlg->m_si->wState |= GC_EVENT_HIGHLIGHT; - FixTabIcons(pDlg); - if (Chat::bFlashWindowHighlight && pDlg != m_tab.GetActivePage()) - pDlg->StartFlash(); -} - -void CTabbedWindow::SetTabHighlight(CMsgDialog *pDlg) -{ - if (m_tab.GetDlgIndex(pDlg) == -1) - return; - - FixTabIcons(pDlg); - if (Chat::bFlashWindow && pDlg != m_tab.GetActivePage()) - pDlg->StartFlash(); -} - -void CTabbedWindow::SetWindowPosition() -{ - if (m_pEmbed == nullptr) { - Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room"); - return; - } - - if (iX) { - WINDOWPLACEMENT wp; - wp.length = sizeof(wp); - GetWindowPlacement(m_hwnd, &wp); - - wp.rcNormalPosition.left = iX; - wp.rcNormalPosition.top = iY; - wp.rcNormalPosition.right = wp.rcNormalPosition.left + iWidth; - wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + iHeight; - wp.showCmd = SW_HIDE; - SetWindowPlacement(m_hwnd, &wp); - return; - } - - int flag = m_pEmbed->m_bNoActivate ? RWPF_HIDDEN : 0; - if (Utils_RestoreWindowPosition(m_hwnd, g_plugin.bSavePerContact ? m_pEmbed->m_hContact : 0, CHAT_MODULE, "room", flag)) { - if (g_plugin.bSavePerContact) { - if (Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room", flag | RWPF_NOMOVE)) - SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW); - } - else SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW); - } - - if (!g_plugin.bSavePerContact && g_plugin.bCascade) { - RECT rc, rcMax = {}; - CTabbedWindow *pMaxTab = nullptr; - - for (auto &it : g_arDialogs) { - auto *pTab = it->m_pOwner; - if (pTab == this) - continue; - - GetWindowRect(pTab->GetHwnd(), &rc); - if (rc.left > rcMax.left && rc.top > rcMax.top) { - pMaxTab = pTab; - rcMax = rc; - } - } - - if (pMaxTab) { - m_windowWasCascaded = 1; - int offset = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME); - SetWindowPos(m_hwnd, nullptr, rcMax.left + offset, rcMax.top + offset, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); - } - } -} - -void CTabbedWindow::SwitchNextTab() -{ - int total = m_tab.GetCount(); - int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (i != -1 && total > 1) { - if (i < total - 1) - i++; - else - i = 0; - m_tab.ActivatePage(i); - TabClicked(); - } -} - -void CTabbedWindow::SwitchPrevTab() -{ - int total = m_tab.GetCount(); - int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (i != -1 && total >= 1) { - if (i > 0) - i--; - else - i = total - 1; - m_tab.ActivatePage(i); - TabClicked(); - } -} - -void CTabbedWindow::SwitchTab(int iNewTab) -{ - int total = m_tab.GetCount(); - int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (i != -1 && total != -1 && total != 1 && i != iNewTab && total > iNewTab) { - m_tab.ActivatePage(iNewTab); - TabClicked(); - } -} - -void CTabbedWindow::TabClicked() -{ - CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetActivePage(); - if (pDlg == nullptr) - return; - - SetFocus(pDlg->m_message.GetHwnd()); - - SESSION_INFO *si = pDlg->m_si; - if (si) { - if (si->wState & STATE_TALK) { - si->wState &= ~STATE_TALK; - db_set_w(si->hContact, si->pszModule, "ApparentMode", 0); - } - - if (si->wState & GC_EVENT_HIGHLIGHT) { - si->wState &= ~GC_EVENT_HIGHLIGHT; - - if (g_clistApi.pfnGetEvent(si->hContact, 0)) - g_clistApi.pfnRemoveEvent(si->hContact, GC_FAKE_EVENT); - } - - if (!si->pDlg) { - g_chatApi.ShowRoom(si); - SendMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); - } - } - - FixTabIcons(pDlg); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CTabbedWindow::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - RECT rc; - int idx; - - switch (msg) { - case WM_MOVE: - SaveWindowPosition(false); - break; - - case WM_ENTERSIZEMOVE: - GetClientRect(m_hwnd, &rc); - oldSizeX = rc.right - rc.left; - oldSizeY = rc.bottom - rc.top; - break; - - case WM_EXITSIZEMOVE: - GetClientRect(m_hwnd, &rc); - if (!((rc.right - rc.left) == oldSizeX && (rc.bottom - rc.top) == oldSizeY)) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) { - pDlg->m_pLog->ScrollToBottom(); - pDlg->Resize(); - } - } - break; - - case WM_DRAWITEM: - { - LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam; - if (dis->hwndItem == m_hwndStatus) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) - DrawStatusIcons(pDlg->m_hContact, dis->hDC, dis->rcItem, 2); - return TRUE; - } - } - break; - - case WM_COMMAND: - if (LOWORD(wParam) == IDOK) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) { - pDlg->m_btnOk.Click(); - return TRUE; - } - } - break; - - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_INACTIVE) - break; - - if (!m_pEmbed) { - idx = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (idx != -1) - m_tab.ActivatePage(idx); - } - else m_pEmbed->OnActivate(); - break; - - case WM_NOTIFY: - if (((LPNMHDR)lParam)->hwndFrom == m_hwndStatus) { - if (((LPNMHDR)lParam)->code == NM_CLICK || ((LPNMHDR)lParam)->code == NM_RCLICK) { - NMMOUSE *nm = (NMMOUSE *)lParam; - SendMessage(m_hwndStatus, SB_GETRECT, SendMessage(m_hwndStatus, SB_GETPARTS, 0, 0) - 1, (LPARAM)&rc); - if (nm->pt.x >= rc.left) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) - CheckStatusIconClick(pDlg->m_hContact, m_hwndStatus, nm->pt, rc, 2, ((LPNMHDR)lParam)->code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0); - } - return TRUE; - } - } - - if (((LPNMHDR)lParam)->idFrom == IDC_TAB) { - switch (((LPNMHDR)lParam)->code) { - case TCN_SELCHANGE: - m_tab.ActivatePage(TabCtrl_GetCurSel(m_tab.GetHwnd())); - break; - - case NM_RCLICK: - int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom); - if (i == -1) - break; - - TCHITTESTINFO tci = {}; - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - tci.flags = TCHT_ONITEM; - ScreenToClient(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt); - if ((i = TabCtrl_HitTest(((LPNMHDR)lParam)->hwndFrom, &tci)) == -1) - break; - - CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetNthPage(i); - SESSION_INFO *si = pDlg->m_si; - - ClientToScreen(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt); - HMENU hSubMenu = GetSubMenu(g_hMenu, 1); - TranslateMenu(hSubMenu); - - if (si != nullptr) { - uint16_t w = db_get_w(si->hContact, si->pszModule, "TabPosition", 0); - if (w == 0) - CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED); - else - CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_CHECKED); - } - else CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED); - - switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, tci.pt.x, tci.pt.y, 0, m_hwnd, nullptr)) { - case ID_CLOSE: - pDlg->CloseTab(); - break; - - case ID_LOCKPOSITION: - if (si != nullptr) { - if (!(GetMenuState(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND)&MF_CHECKED)) { - if (si->hContact) - db_set_w(si->hContact, si->pszModule, "TabPosition", (uint16_t)(i + 1)); - } - else db_unset(si->hContact, si->pszModule, "TabPosition"); - } - break; - - case ID_CLOSEOTHER: - int tabCount = m_tab.GetCount(); - if (tabCount > 1) { - while (tabCount--) { - if (tabCount == i) - continue; - - if (pDlg = (CMsgDialog*)m_tab.GetNthPage(tabCount)) - pDlg->CloseTab(); - } - m_tab.ActivatePage(0); - } - break; - } - } - } - break; - } - - return CDlgBase::DlgProc(msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void UninitTabs() -{ - if (g_pTabDialog != nullptr) { - g_pTabDialog->Close(); - g_pTabDialog = nullptr; - } -} +/*
+
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "statusicon.h"
+
+CTabbedWindow *g_pTabDialog = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CTabbedWindow* GetContainer()
+{
+ if (g_Settings.bTabsEnable) {
+ if (g_pTabDialog == nullptr) {
+ g_pTabDialog = new CTabbedWindow();
+ g_pTabDialog->Show();
+ }
+ return g_pTabDialog;
+ }
+
+ return new CTabbedWindow();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK TabSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CTabbedWindow *pOwner = (CTabbedWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ TCHITTESTINFO tci = {};
+
+ static bool bDragging = false;
+ static int iBeginIndex = 0;
+ switch (msg) {
+ case WM_LBUTTONDOWN:
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ if (DragDetect(hwnd, tci.pt) && TabCtrl_GetItemCount(hwnd) > 1) {
+ tci.flags = TCHT_ONITEM;
+ ScreenToClient(hwnd, &tci.pt);
+ int idx = TabCtrl_HitTest(hwnd, &tci);
+ if (idx != -1) {
+ CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx);
+ if (pDlg) {
+ bDragging = true;
+ iBeginIndex = idx;
+ ImageList_BeginDrag(Clist_GetImageList(), pDlg->GetImageId(), 8, 8);
+ ImageList_DragEnter(hwnd, tci.pt.x, tci.pt.y);
+ SetCapture(hwnd);
+ }
+ return TRUE;
+ }
+ }
+ else pOwner->TabClicked();
+ break;
+
+ case WM_CAPTURECHANGED:
+ bDragging = false;
+ ImageList_DragLeave(hwnd);
+ ImageList_EndDrag();
+ break;
+
+ case WM_MOUSEMOVE:
+ if (bDragging) {
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(hwnd, &tci.pt);
+ ImageList_DragMove(tci.pt.x, tci.pt.y);
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ if (bDragging && ReleaseCapture()) {
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ tci.flags = TCHT_ONITEM;
+ bDragging = false;
+ ImageList_DragLeave(hwnd);
+ ImageList_EndDrag();
+
+ ScreenToClient(hwnd, &tci.pt);
+ int idx = TabCtrl_HitTest(hwnd, &tci);
+ if (idx != -1 && idx != iBeginIndex)
+ pOwner->DropTab(idx, iBeginIndex);
+ }
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ if (g_Settings.bTabCloseOnDblClick) {
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(hwnd, &tci.pt);
+
+ tci.flags = TCHT_ONITEM;
+ int idx = TabCtrl_HitTest(hwnd, &tci);
+ if (idx != -1) {
+ CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx);
+ if (pDlg)
+ pDlg->CloseTab();
+ }
+ }
+ break;
+
+ case WM_MBUTTONUP:
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ tci.flags = TCHT_ONITEM;
+
+ ScreenToClient(hwnd, &tci.pt);
+ int idx = TabCtrl_HitTest(hwnd, &tci);
+ if (idx != -1) {
+ CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx);
+ if (pDlg)
+ pDlg->CloseTab();
+ }
+ break;
+ }
+
+ return mir_callNextSubclass(hwnd, TabSubclassProc, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CTabbedWindow::CTabbedWindow() :
+ CDlgBase(g_plugin, IDD_CONTAINER),
+ m_tab(this, IDC_TAB)
+{
+ SetMinSize(450, 350);
+}
+
+bool CTabbedWindow::OnInitDialog()
+{
+ SetWindowLongPtr(m_tab.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_tab.GetHwnd(), ::TabSubclassProc);
+
+ m_hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, nullptr, WS_CHILD | WS_VISIBLE | SBT_TOOLTIPS | SBARS_SIZEGRIP, 0, 0, 0, 0, m_hwnd, nullptr, g_plugin.getInst(), nullptr);
+ SendMessage(m_hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON), 0);
+
+ RECT rc;
+ GetWindowRect(m_hwndStatus, &rc);
+ m_statusHeight = rc.bottom - rc.top;
+
+ SetWindowPosition();
+
+ if (!g_Settings.bTabsEnable) {
+ m_tab.Hide();
+ return false;
+ }
+
+ LONG_PTR mask = GetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE);
+ if (g_Settings.bTabsAtBottom)
+ mask |= TCS_BOTTOM;
+ else
+ mask &= ~TCS_BOTTOM;
+ SetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE, mask);
+
+ TabCtrl_SetMinTabWidth(m_tab.GetHwnd(), 80);
+ TabCtrl_SetImageList(m_tab.GetHwnd(), Clist_GetImageList());
+ return true;
+}
+
+void CTabbedWindow::OnDestroy()
+{
+ DestroyWindow(m_hwndStatus); m_hwndStatus = nullptr;
+
+ SaveWindowPosition(true);
+
+ Utils_SaveWindowPosition(m_hwnd, g_plugin.bSavePerContact ? ((m_pEmbed == nullptr) ? 0 : m_pEmbed->m_hContact) : 0, CHAT_MODULE, "room");
+
+ if (m_pEmbed == nullptr)
+ g_pTabDialog = nullptr;
+}
+
+void CTabbedWindow::OnResize()
+{
+ CDlgBase::OnResize();
+
+ SendMessage(m_hwndStatus, WM_SIZE, 0, 0);
+
+ if (m_pEmbed) {
+ RECT rc;
+ GetClientRect(m_tab.GetHwnd(), &rc);
+ MoveWindow(m_pEmbed->GetHwnd(), 0, 0, rc.right - rc.left, rc.bottom - rc.top, FALSE);
+ }
+
+ SaveWindowPosition(false);
+}
+
+int CTabbedWindow::Resizer(UTILRESIZECONTROL *urc)
+{
+ if (urc->wId == IDC_TAB) {
+ urc->rcItem.top = 1;
+ urc->rcItem.bottom = urc->dlgNewSize.cy - m_statusHeight - 1;
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_CUSTOM;
+ }
+
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; // status bar
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CTabbedWindow* CTabbedWindow::AddPage(MCONTACT hContact, wchar_t *pwszText, int iNoActivate)
+{
+ CMsgDialog *pDlg = new CMsgDialog(this, hContact);
+ pDlg->m_wszInitialText = pwszText;
+ if (iNoActivate != -1)
+ pDlg->m_bNoActivate = iNoActivate != 0;
+
+ if (g_Settings.bTabsEnable) {
+ m_tab.AddPage(Clist_GetContactDisplayName(hContact), nullptr, pDlg);
+ FixTabIcons(pDlg);
+
+ m_tab.ActivatePage(m_tab.GetCount() - 1);
+ }
+ else {
+ m_pEmbed = pDlg;
+ Create();
+ pDlg->SetParent(m_hwnd);
+ pDlg->Create();
+ Show();
+ }
+
+ PostMessage(m_hwnd, WM_SIZE, 0, 0);
+ return this;
+}
+
+void CTabbedWindow::AddPage(SESSION_INFO *si, int insertAt)
+{
+ // does the tab already exist?
+ int indexfound = (si->pDlg) ? m_tab.GetDlgIndex(si->pDlg) : -1;
+ if (indexfound == -1) { // create a new tab
+ wchar_t szTemp[30];
+ mir_wstrncpy(szTemp, si->ptszName, 21);
+ if (mir_wstrlen(si->ptszName) > 20)
+ mir_wstrncpy(szTemp + 20, L"...", 4);
+
+ if (!IsWindowVisible(m_hwnd))
+ Show(SW_SHOW);
+
+ CMsgDialog *pDlg = new CMsgDialog(this, si);
+ pDlg->SetParent(m_hwnd);
+ m_tab.AddPage(szTemp, nullptr, pDlg);
+ m_tab.ActivatePage(m_tab.GetCount() - 1);
+ FixTabIcons(pDlg);
+ }
+ else if (insertAt == -1)
+ m_tab.ActivatePage(indexfound);
+}
+
+CMsgDialog* CTabbedWindow::CurrPage() const
+{
+ return (m_pEmbed != nullptr) ? m_pEmbed : (CMsgDialog*)m_tab.GetActivePage();
+}
+
+void CTabbedWindow::DropTab(int begin, int end)
+{
+ if (begin == end)
+ return;
+
+ m_tab.SwapPages(begin, end);
+
+ CMsgDialog *pDlg = (CMsgDialog *)m_tab.GetNthPage(end);
+ if (pDlg) {
+ FixTabIcons(pDlg);
+ m_tab.ActivatePage(end);
+ }
+
+ // fix the "fixed" positions
+ int tabCount = m_tab.GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ pDlg = (CMsgDialog *)m_tab.GetNthPage(i);
+ if (pDlg == nullptr)
+ continue;
+
+ SESSION_INFO *si = pDlg->m_si;
+ if (si && si->hContact && db_get_w(si->hContact, si->pszModule, "TabPosition", 0) != 0)
+ db_set_w(si->hContact, si->pszModule, "TabPosition", i + 1);
+ }
+}
+
+void CTabbedWindow::FixTabIcons(CMsgDialog *pDlg)
+{
+ if (pDlg == nullptr)
+ return;
+
+ int image = pDlg->GetImageId();
+
+ // if tabs are turned off, simply change the window's icon, otherwise set the tab's icon first
+ if (m_pEmbed == nullptr) {
+ int idx = m_tab.GetDlgIndex(pDlg);
+ if (idx == -1)
+ return;
+
+ TCITEM tci = {};
+ tci.mask = TCIF_IMAGE;
+ TabCtrl_GetItem(m_tab.GetHwnd(), idx, &tci);
+ if (tci.iImage != image) {
+ tci.iImage = image;
+ TabCtrl_SetItem(m_tab.GetHwnd(), idx, &tci);
+ }
+ }
+
+ // set the container's icon only if we're processing the current page
+ if (pDlg == CurrPage()) {
+ Window_FreeIcon_IcoLib(m_hwnd);
+ if (g_plugin.bUseStatusWinIcon)
+ Window_SetProtoIcon_IcoLib(m_hwnd, pDlg->m_szProto, pDlg->m_wStatus);
+ else if (pDlg->isChat())
+ Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_CHANMGR));
+ else
+ Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_EVENT_MESSAGE);
+ }
+}
+
+void CTabbedWindow::RemoveTab(CMsgDialog *pDlg)
+{
+ int idx = m_tab.GetDlgIndex(pDlg);
+ if (idx == -1)
+ return;
+
+ m_tab.RemovePage(idx);
+ if (m_tab.GetCount() == 0)
+ PostMessage(m_hwnd, WM_CLOSE, 0, 0);
+ else {
+ if (m_tab.GetNthPage(idx) == nullptr)
+ idx--;
+ m_tab.ActivatePage(idx);
+ }
+}
+
+void CTabbedWindow::SaveWindowPosition(bool bUpdateSession)
+{
+ WINDOWPLACEMENT wp = {};
+ wp.length = sizeof(wp);
+ GetWindowPlacement(m_hwnd, &wp);
+
+ g_Settings.iX = wp.rcNormalPosition.left;
+ g_Settings.iY = wp.rcNormalPosition.top;
+ g_Settings.iWidth = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+ g_Settings.iHeight = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+
+ if (bUpdateSession) {
+ iX = g_Settings.iX;
+ iY = g_Settings.iY;
+ iWidth = g_Settings.iWidth;
+ iHeight = g_Settings.iHeight;
+ }
+}
+
+void CTabbedWindow::SetMessageHighlight(CMsgDialog *pDlg)
+{
+ if (m_tab.GetDlgIndex(pDlg) == -1)
+ return;
+
+ pDlg->m_si->wState |= GC_EVENT_HIGHLIGHT;
+ FixTabIcons(pDlg);
+ if (Chat::bFlashWindowHighlight && pDlg != m_tab.GetActivePage())
+ pDlg->StartFlash();
+}
+
+void CTabbedWindow::SetTabHighlight(CMsgDialog *pDlg)
+{
+ if (m_tab.GetDlgIndex(pDlg) == -1)
+ return;
+
+ FixTabIcons(pDlg);
+ if (Chat::bFlashWindow && pDlg != m_tab.GetActivePage())
+ pDlg->StartFlash();
+}
+
+void CTabbedWindow::SetWindowPosition()
+{
+ if (m_pEmbed == nullptr) {
+ Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room");
+ return;
+ }
+
+ if (iX) {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ GetWindowPlacement(m_hwnd, &wp);
+
+ wp.rcNormalPosition.left = iX;
+ wp.rcNormalPosition.top = iY;
+ wp.rcNormalPosition.right = wp.rcNormalPosition.left + iWidth;
+ wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + iHeight;
+ wp.showCmd = SW_HIDE;
+ SetWindowPlacement(m_hwnd, &wp);
+ return;
+ }
+
+ int flag = m_pEmbed->m_bNoActivate ? RWPF_HIDDEN : 0;
+ if (Utils_RestoreWindowPosition(m_hwnd, g_plugin.bSavePerContact ? m_pEmbed->m_hContact : 0, CHAT_MODULE, "room", flag)) {
+ if (g_plugin.bSavePerContact) {
+ if (Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room", flag | RWPF_NOMOVE))
+ SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW);
+ }
+ else SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW);
+ }
+
+ if (!g_plugin.bSavePerContact && g_plugin.bCascade) {
+ RECT rc, rcMax = {};
+ CTabbedWindow *pMaxTab = nullptr;
+
+ for (auto &it : g_arDialogs) {
+ auto *pTab = it->m_pOwner;
+ if (pTab == this)
+ continue;
+
+ GetWindowRect(pTab->GetHwnd(), &rc);
+ if (rc.left > rcMax.left && rc.top > rcMax.top) {
+ pMaxTab = pTab;
+ rcMax = rc;
+ }
+ }
+
+ if (pMaxTab) {
+ m_windowWasCascaded = 1;
+ int offset = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME);
+ SetWindowPos(m_hwnd, nullptr, rcMax.left + offset, rcMax.top + offset, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
+ }
+ }
+}
+
+void CTabbedWindow::SwitchNextTab()
+{
+ int total = m_tab.GetCount();
+ int i = TabCtrl_GetCurSel(m_tab.GetHwnd());
+ if (i != -1 && total > 1) {
+ if (i < total - 1)
+ i++;
+ else
+ i = 0;
+ m_tab.ActivatePage(i);
+ TabClicked();
+ }
+}
+
+void CTabbedWindow::SwitchPrevTab()
+{
+ int total = m_tab.GetCount();
+ int i = TabCtrl_GetCurSel(m_tab.GetHwnd());
+ if (i != -1 && total >= 1) {
+ if (i > 0)
+ i--;
+ else
+ i = total - 1;
+ m_tab.ActivatePage(i);
+ TabClicked();
+ }
+}
+
+void CTabbedWindow::SwitchTab(int iNewTab)
+{
+ int total = m_tab.GetCount();
+ int i = TabCtrl_GetCurSel(m_tab.GetHwnd());
+ if (i != -1 && total != -1 && total != 1 && i != iNewTab && total > iNewTab) {
+ m_tab.ActivatePage(iNewTab);
+ TabClicked();
+ }
+}
+
+void CTabbedWindow::TabClicked()
+{
+ CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetActivePage();
+ if (pDlg == nullptr)
+ return;
+
+ SetFocus(pDlg->m_message.GetHwnd());
+
+ SESSION_INFO *si = pDlg->m_si;
+ if (si) {
+ if (si->wState & STATE_TALK) {
+ si->wState &= ~STATE_TALK;
+ db_set_w(si->hContact, si->pszModule, "ApparentMode", 0);
+ }
+
+ if (si->wState & GC_EVENT_HIGHLIGHT) {
+ si->wState &= ~GC_EVENT_HIGHLIGHT;
+
+ if (g_clistApi.pfnGetEvent(si->hContact, 0))
+ g_clistApi.pfnRemoveEvent(si->hContact, GC_FAKE_EVENT);
+ }
+
+ if (!si->pDlg) {
+ g_chatApi.ShowRoom(si);
+ SendMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ }
+ }
+
+ FixTabIcons(pDlg);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CTabbedWindow::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc;
+ int idx;
+
+ switch (msg) {
+ case WM_MOVE:
+ SaveWindowPosition(false);
+ break;
+
+ case WM_ENTERSIZEMOVE:
+ GetClientRect(m_hwnd, &rc);
+ oldSizeX = rc.right - rc.left;
+ oldSizeY = rc.bottom - rc.top;
+ break;
+
+ case WM_EXITSIZEMOVE:
+ GetClientRect(m_hwnd, &rc);
+ if (!((rc.right - rc.left) == oldSizeX && (rc.bottom - rc.top) == oldSizeY)) {
+ CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed;
+ if (pDlg != nullptr) {
+ pDlg->m_pLog->ScrollToBottom();
+ pDlg->Resize();
+ }
+ }
+ break;
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
+ if (dis->hwndItem == m_hwndStatus) {
+ CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed;
+ if (pDlg != nullptr)
+ DrawStatusIcons(pDlg->m_hContact, dis->hDC, dis->rcItem, 2);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDOK) {
+ CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed;
+ if (pDlg != nullptr) {
+ pDlg->m_btnOk.Click();
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) == WA_INACTIVE)
+ break;
+
+ if (!m_pEmbed) {
+ idx = TabCtrl_GetCurSel(m_tab.GetHwnd());
+ if (idx != -1)
+ m_tab.ActivatePage(idx);
+ }
+ else m_pEmbed->OnActivate();
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->hwndFrom == m_hwndStatus) {
+ if (((LPNMHDR)lParam)->code == NM_CLICK || ((LPNMHDR)lParam)->code == NM_RCLICK) {
+ NMMOUSE *nm = (NMMOUSE *)lParam;
+ SendMessage(m_hwndStatus, SB_GETRECT, SendMessage(m_hwndStatus, SB_GETPARTS, 0, 0) - 1, (LPARAM)&rc);
+ if (nm->pt.x >= rc.left) {
+ CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed;
+ if (pDlg != nullptr)
+ CheckStatusIconClick(pDlg->m_hContact, m_hwndStatus, nm->pt, rc, 2, ((LPNMHDR)lParam)->code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0);
+ }
+ return TRUE;
+ }
+ }
+
+ if (((LPNMHDR)lParam)->idFrom == IDC_TAB) {
+ switch (((LPNMHDR)lParam)->code) {
+ case TCN_SELCHANGE:
+ m_tab.ActivatePage(TabCtrl_GetCurSel(m_tab.GetHwnd()));
+ break;
+
+ case NM_RCLICK:
+ int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom);
+ if (i == -1)
+ break;
+
+ TCHITTESTINFO tci = {};
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ tci.flags = TCHT_ONITEM;
+ ScreenToClient(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt);
+ if ((i = TabCtrl_HitTest(((LPNMHDR)lParam)->hwndFrom, &tci)) == -1)
+ break;
+
+ CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetNthPage(i);
+ SESSION_INFO *si = pDlg->m_si;
+
+ ClientToScreen(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt);
+ HMENU hSubMenu = GetSubMenu(g_hMenu, 1);
+ TranslateMenu(hSubMenu);
+
+ if (si != nullptr) {
+ uint16_t w = db_get_w(si->hContact, si->pszModule, "TabPosition", 0);
+ if (w == 0)
+ CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED);
+ else
+ CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_CHECKED);
+ }
+ else CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED);
+
+ switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, tci.pt.x, tci.pt.y, 0, m_hwnd, nullptr)) {
+ case ID_CLOSE:
+ pDlg->CloseTab();
+ break;
+
+ case ID_LOCKPOSITION:
+ if (si != nullptr) {
+ if (!(GetMenuState(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND)&MF_CHECKED)) {
+ if (si->hContact)
+ db_set_w(si->hContact, si->pszModule, "TabPosition", (uint16_t)(i + 1));
+ }
+ else db_unset(si->hContact, si->pszModule, "TabPosition");
+ }
+ break;
+
+ case ID_CLOSEOTHER:
+ int tabCount = m_tab.GetCount();
+ if (tabCount > 1) {
+ while (tabCount--) {
+ if (tabCount == i)
+ continue;
+
+ if (pDlg = (CMsgDialog*)m_tab.GetNthPage(tabCount))
+ pDlg->CloseTab();
+ }
+ m_tab.ActivatePage(0);
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void UninitTabs()
+{
+ if (g_pTabDialog != nullptr) {
+ g_pTabDialog->Close();
+ g_pTabDialog = nullptr;
+ }
+}
diff --git a/src/core/stdmsg/src/version.h b/src/core/stdmsg/src/version.h index 80b240e521..c2573ed98b 100644 --- a/src/core/stdmsg/src/version.h +++ b/src/core/stdmsg/src/version.h @@ -9,4 +9,4 @@ #define __DESCRIPTION "Core module for send/receive instant messages."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdMsg"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stdpopup/src/stdafx.cxx b/src/core/stdpopup/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/src/core/stdpopup/src/stdafx.cxx +++ b/src/core/stdpopup/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "stdafx.h"
\ No newline at end of file diff --git a/src/core/stdpopup/src/version.h b/src/core/stdpopup/src/version.h index 2ffc660f71..16d5b005cd 100644 --- a/src/core/stdpopup/src/version.h +++ b/src/core/stdpopup/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for popups."
#define __AUTHOR "Scott Ellis, Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdPopup"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stduihist/src/history.cpp b/src/core/stduihist/src/history.cpp index 56c3aba8a5..ae92875fab 100644 --- a/src/core/stduihist/src/history.cpp +++ b/src/core/stduihist/src/history.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduihist/src/main.cpp b/src/core/stduihist/src/main.cpp index 1911bd9792..1be11d7ac3 100644 --- a/src/core/stduihist/src/main.cpp +++ b/src/core/stduihist/src/main.cpp @@ -2,7 +2,7 @@ Standard ugly history viewer for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stduihist/src/stdafx.h b/src/core/stduihist/src/stdafx.h index 9817742ad3..aae29d451d 100644 --- a/src/core/stduihist/src/stdafx.h +++ b/src/core/stduihist/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduihist/src/version.h b/src/core/stduihist/src/version.h index 62eda51681..ee7cceff2a 100644 --- a/src/core/stduihist/src/version.h +++ b/src/core/stduihist/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for built-in history viewer."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdUIHist"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stduserinfo/src/contactinfo.cpp b/src/core/stduserinfo/src/contactinfo.cpp index a19783e4df..7adec4c09b 100644 --- a/src/core/stduserinfo/src/contactinfo.cpp +++ b/src/core/stduserinfo/src/contactinfo.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduserinfo/src/main.cpp b/src/core/stduserinfo/src/main.cpp index 5334c9727f..9bd9b6f7be 100644 --- a/src/core/stduserinfo/src/main.cpp +++ b/src/core/stduserinfo/src/main.cpp @@ -2,7 +2,7 @@ Standard User Info plugin for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stduserinfo/src/stdafx.h b/src/core/stduserinfo/src/stdafx.h index 76b456e716..ca5412f200 100644 --- a/src/core/stduserinfo/src/stdafx.h +++ b/src/core/stduserinfo/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduserinfo/src/stdinfo.cpp b/src/core/stduserinfo/src/stdinfo.cpp index 5acec2d245..ca3b2edd20 100644 --- a/src/core/stduserinfo/src/stdinfo.cpp +++ b/src/core/stduserinfo/src/stdinfo.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduserinfo/src/userinfo.cpp b/src/core/stduserinfo/src/userinfo.cpp index 9448d5c96f..30792d9244 100644 --- a/src/core/stduserinfo/src/userinfo.cpp +++ b/src/core/stduserinfo/src/userinfo.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduserinfo/src/utils.h b/src/core/stduserinfo/src/utils.h index cb766c47ae..9ceb33b61d 100644 --- a/src/core/stduserinfo/src/utils.h +++ b/src/core/stduserinfo/src/utils.h @@ -1,214 +1,214 @@ -/* - -Standard User Info plugin for Miranda NG - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#define SVS_NORMAL 0 -#define SVS_GENDER 1 -#define SVS_ZEROISUNSPEC 2 -#define SVS_IP 3 -#define SVS_COUNTRY 4 -#define SVS_MONTH 5 -#define SVS_SIGNED 6 -#define SVS_TIMEZONE 7 -#define SVS_MARITAL 8 - -///////////////////////////////////////////////////////////////////////////////////////// - -struct DataItem -{ - const char *szSetting; - int idCtrl, special; -}; - -template <size_t _Size> -bool IsEmptyValue(MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr) -{ - if (szModule == nullptr) - szModule = Proto_GetBaseAccountName(hContact); - - DBVARIANT dbv; - for (auto &it : buffer) { - if (db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0) - continue; - - db_free(&dbv); - return false; - } - - return true; -} - -template <size_t _Size> -void SetValue(HWND hwndDlg, MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr) -{ - if (szModule == nullptr) - szModule = Proto_GetBaseAccountName(hContact); - - for (auto &it : buffer) { - char *pstr = nullptr; - wchar_t *pwstr = nullptr, wstr[80]; - - DBVARIANT dbv = { DBVT_DELETED }; - - bool unspecified; - if (szModule == nullptr) - unspecified = true; - else - unspecified = db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0; - - if (!unspecified) { - switch (dbv.type) { - case DBVT_BYTE: - if (it.special == SVS_GENDER) { - if (dbv.cVal == 'M') pwstr = TranslateT("Male"); - else if (dbv.cVal == 'F') pwstr = TranslateT("Female"); - else unspecified = 1; - } - else if (it.special == SVS_MONTH) { - if (dbv.bVal > 0 && dbv.bVal <= 12) { - pwstr = wstr; - GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1 - 1 + dbv.bVal, wstr, _countof(wstr)); - } - else unspecified = 1; - } - else if (it.special == SVS_TIMEZONE) { - if (dbv.cVal == -100) unspecified = 1; - else { - pwstr = wstr; - mir_snwprintf(wstr, dbv.cVal ? L"UTC%+d:%02d" : L"UTC", -dbv.cVal / 2, (dbv.cVal & 1) * 30); - } - } - else if (it.special == SVS_MARITAL) { - switch (dbv.cVal) { - case 0: - pwstr = TranslateT("<not specified>"); - break; - case 10: - pwstr = TranslateT("Single"); - break; - case 11: - pwstr = TranslateT("Close relationships"); - break; - case 12: - pwstr = TranslateT("Engaged"); - break; - case 20: - pwstr = TranslateT("Married"); - break; - case 30: - pwstr = TranslateT("Divorced"); - break; - case 31: - pwstr = TranslateT("Separated"); - break; - case 40: - pwstr = TranslateT("Widowed"); - break; - case 50: - pwstr = TranslateT("Actively searching"); - break; - case 60: - pwstr = TranslateT("In love"); - break; - case 70: - pwstr = TranslateT("It's complicated"); - break; - case 80: - pwstr = TranslateT("In a civil union"); - break; - default: - unspecified = 1; - } - } - else { - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.bVal == 0); - pwstr = _itow(it.special == SVS_SIGNED ? dbv.cVal : dbv.bVal, wstr, 10); - } - break; - - case DBVT_WORD: - if (it.special == SVS_COUNTRY) { - uint16_t wSave = dbv.wVal; - if (wSave == (uint16_t)-1) { - char szSettingName[100]; - mir_snprintf(szSettingName, "%sName", it.szSetting); - if (!db_get_ws(hContact, szModule, szSettingName, &dbv)) { - pwstr = dbv.pwszVal; - unspecified = false; - break; - } - } - - pwstr = TranslateW(_A2T((char *)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, wSave, 0))); - unspecified = pwstr == nullptr; - } - else { - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.wVal == 0); - pwstr = _itow(it.special == SVS_SIGNED ? dbv.sVal : dbv.wVal, wstr, 10); - } - break; - - case DBVT_DWORD: - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.dVal == 0); - if (it.special == SVS_IP) { - struct in_addr ia; - ia.S_un.S_addr = htonl(dbv.dVal); - mir_wstrncpy(wstr, _A2T(inet_ntoa(ia)), _countof(wstr)); - pwstr = wstr; - if (dbv.dVal == 0) - unspecified = 1; - } - else pwstr = _itow(it.special == SVS_SIGNED ? dbv.lVal : dbv.dVal, wstr, 10); - break; - - case DBVT_ASCIIZ: - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); - pstr = dbv.pszVal; - break; - - case DBVT_UTF8: - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); - if (!unspecified) { - SetDlgItemTextW(hwndDlg, it.idCtrl, TranslateW(ptrW(mir_utf8decodeW(dbv.pszVal)))); - goto LBL_Exit; - } - - mir_utf8decode(dbv.pszVal, &pwstr); - break; - - default: - pwstr = wstr; - mir_wstrcpy(wstr, L"???"); - break; - } - } - - if (unspecified) - SetDlgItemText(hwndDlg, it.idCtrl, TranslateT("<not specified>")); - else if (pwstr != nullptr) - SetDlgItemText(hwndDlg, it.idCtrl, pwstr); - else - SetDlgItemTextA(hwndDlg, it.idCtrl, pstr); - -LBL_Exit: - EnableWindow(GetDlgItem(hwndDlg, it.idCtrl), !unspecified); - db_free(&dbv); - } -} +/*
+
+Standard User Info plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#define SVS_NORMAL 0
+#define SVS_GENDER 1
+#define SVS_ZEROISUNSPEC 2
+#define SVS_IP 3
+#define SVS_COUNTRY 4
+#define SVS_MONTH 5
+#define SVS_SIGNED 6
+#define SVS_TIMEZONE 7
+#define SVS_MARITAL 8
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct DataItem
+{
+ const char *szSetting;
+ int idCtrl, special;
+};
+
+template <size_t _Size>
+bool IsEmptyValue(MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr)
+{
+ if (szModule == nullptr)
+ szModule = Proto_GetBaseAccountName(hContact);
+
+ DBVARIANT dbv;
+ for (auto &it : buffer) {
+ if (db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0)
+ continue;
+
+ db_free(&dbv);
+ return false;
+ }
+
+ return true;
+}
+
+template <size_t _Size>
+void SetValue(HWND hwndDlg, MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr)
+{
+ if (szModule == nullptr)
+ szModule = Proto_GetBaseAccountName(hContact);
+
+ for (auto &it : buffer) {
+ char *pstr = nullptr;
+ wchar_t *pwstr = nullptr, wstr[80];
+
+ DBVARIANT dbv = { DBVT_DELETED };
+
+ bool unspecified;
+ if (szModule == nullptr)
+ unspecified = true;
+ else
+ unspecified = db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0;
+
+ if (!unspecified) {
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ if (it.special == SVS_GENDER) {
+ if (dbv.cVal == 'M') pwstr = TranslateT("Male");
+ else if (dbv.cVal == 'F') pwstr = TranslateT("Female");
+ else unspecified = 1;
+ }
+ else if (it.special == SVS_MONTH) {
+ if (dbv.bVal > 0 && dbv.bVal <= 12) {
+ pwstr = wstr;
+ GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1 - 1 + dbv.bVal, wstr, _countof(wstr));
+ }
+ else unspecified = 1;
+ }
+ else if (it.special == SVS_TIMEZONE) {
+ if (dbv.cVal == -100) unspecified = 1;
+ else {
+ pwstr = wstr;
+ mir_snwprintf(wstr, dbv.cVal ? L"UTC%+d:%02d" : L"UTC", -dbv.cVal / 2, (dbv.cVal & 1) * 30);
+ }
+ }
+ else if (it.special == SVS_MARITAL) {
+ switch (dbv.cVal) {
+ case 0:
+ pwstr = TranslateT("<not specified>");
+ break;
+ case 10:
+ pwstr = TranslateT("Single");
+ break;
+ case 11:
+ pwstr = TranslateT("Close relationships");
+ break;
+ case 12:
+ pwstr = TranslateT("Engaged");
+ break;
+ case 20:
+ pwstr = TranslateT("Married");
+ break;
+ case 30:
+ pwstr = TranslateT("Divorced");
+ break;
+ case 31:
+ pwstr = TranslateT("Separated");
+ break;
+ case 40:
+ pwstr = TranslateT("Widowed");
+ break;
+ case 50:
+ pwstr = TranslateT("Actively searching");
+ break;
+ case 60:
+ pwstr = TranslateT("In love");
+ break;
+ case 70:
+ pwstr = TranslateT("It's complicated");
+ break;
+ case 80:
+ pwstr = TranslateT("In a civil union");
+ break;
+ default:
+ unspecified = 1;
+ }
+ }
+ else {
+ unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.bVal == 0);
+ pwstr = _itow(it.special == SVS_SIGNED ? dbv.cVal : dbv.bVal, wstr, 10);
+ }
+ break;
+
+ case DBVT_WORD:
+ if (it.special == SVS_COUNTRY) {
+ uint16_t wSave = dbv.wVal;
+ if (wSave == (uint16_t)-1) {
+ char szSettingName[100];
+ mir_snprintf(szSettingName, "%sName", it.szSetting);
+ if (!db_get_ws(hContact, szModule, szSettingName, &dbv)) {
+ pwstr = dbv.pwszVal;
+ unspecified = false;
+ break;
+ }
+ }
+
+ pwstr = TranslateW(_A2T((char *)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, wSave, 0)));
+ unspecified = pwstr == nullptr;
+ }
+ else {
+ unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.wVal == 0);
+ pwstr = _itow(it.special == SVS_SIGNED ? dbv.sVal : dbv.wVal, wstr, 10);
+ }
+ break;
+
+ case DBVT_DWORD:
+ unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.dVal == 0);
+ if (it.special == SVS_IP) {
+ struct in_addr ia;
+ ia.S_un.S_addr = htonl(dbv.dVal);
+ mir_wstrncpy(wstr, _A2T(inet_ntoa(ia)), _countof(wstr));
+ pwstr = wstr;
+ if (dbv.dVal == 0)
+ unspecified = 1;
+ }
+ else pwstr = _itow(it.special == SVS_SIGNED ? dbv.lVal : dbv.dVal, wstr, 10);
+ break;
+
+ case DBVT_ASCIIZ:
+ unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0');
+ pstr = dbv.pszVal;
+ break;
+
+ case DBVT_UTF8:
+ unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0');
+ if (!unspecified) {
+ SetDlgItemTextW(hwndDlg, it.idCtrl, TranslateW(ptrW(mir_utf8decodeW(dbv.pszVal))));
+ goto LBL_Exit;
+ }
+
+ mir_utf8decode(dbv.pszVal, &pwstr);
+ break;
+
+ default:
+ pwstr = wstr;
+ mir_wstrcpy(wstr, L"???");
+ break;
+ }
+ }
+
+ if (unspecified)
+ SetDlgItemText(hwndDlg, it.idCtrl, TranslateT("<not specified>"));
+ else if (pwstr != nullptr)
+ SetDlgItemText(hwndDlg, it.idCtrl, pwstr);
+ else
+ SetDlgItemTextA(hwndDlg, it.idCtrl, pstr);
+
+LBL_Exit:
+ EnableWindow(GetDlgItem(hwndDlg, it.idCtrl), !unspecified);
+ db_free(&dbv);
+ }
+}
diff --git a/src/core/stduserinfo/src/version.h b/src/core/stduserinfo/src/version.h index 1a68b614db..441fd0cdef 100644 --- a/src/core/stduserinfo/src/version.h +++ b/src/core/stduserinfo/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for providing user information."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdUserInfo"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/core/stduseronline/src/main.cpp b/src/core/stduseronline/src/main.cpp index 255a954e35..da3e0c5cd8 100644 --- a/src/core/stduseronline/src/main.cpp +++ b/src/core/stduseronline/src/main.cpp @@ -2,7 +2,7 @@ Standard user online monitor for Miranda NG
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/core/stduseronline/src/stdafx.h b/src/core/stduseronline/src/stdafx.h index a34a26fd31..1f069e243c 100644 --- a/src/core/stduseronline/src/stdafx.h +++ b/src/core/stduseronline/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduseronline/src/useronline.cpp b/src/core/stduseronline/src/useronline.cpp index 3b1520a0a6..be50bf3165 100644 --- a/src/core/stduseronline/src/useronline.cpp +++ b/src/core/stduseronline/src/useronline.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/core/stduseronline/src/version.h b/src/core/stduseronline/src/version.h index 3edec197fb..ec2b47a6f8 100644 --- a/src/core/stduseronline/src/version.h +++ b/src/core/stduseronline/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for user-is-online event processing."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/StdUserOnline"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/src/mir_app/src/CMPluginBase.cpp b/src/mir_app/src/CMPluginBase.cpp index bc768335a2..92c444e46c 100644 --- a/src/mir_app/src/CMPluginBase.cpp +++ b/src/mir_app/src/CMPluginBase.cpp @@ -1,343 +1,343 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "plugins.h" -#include "IcoLib.h" - -static int sttFakeID = -100; - -static int Compare(const CMPluginBase *p1, const CMPluginBase *p2) -{ - return INT_PTR(p1->getInst()) - INT_PTR(p2->getInst()); -} - -static LIST<CMPluginBase> g_arPlugins(10, Compare); - -void RegisterModule(CMPluginBase *pPlugin) -{ - g_arPlugins.insert(pPlugin); -} - -MIR_APP_DLL(HINSTANCE) GetInstByAddress(void *codePtr) -{ - if (g_arPlugins.getCount() == 0) - return nullptr; - - int idx; - void *boo[2] = { 0, codePtr }; - List_GetIndex((SortedList*)&g_arPlugins, (CMPluginBase*)&boo, &idx); - if (idx > 0) - idx--; - - HINSTANCE result = g_arPlugins[idx]->getInst(); - if (result < g_plugin.getInst() && codePtr > g_plugin.getInst()) - return g_plugin.getInst(); - - if (idx == 0 && codePtr < (void*)result) - return nullptr; - - return result; -} - -MIR_APP_DLL(int) GetPluginLangId(const MUUID &uuid, int _hLang) -{ - if (uuid == miid_last) - return --sttFakeID; - - for (auto &it : g_arPlugins) - if (it->getInfo().uuid == uuid) - return (_hLang) ? _hLang : --sttFakeID; - - return 0; -} - -MIR_APP_DLL(int) IsPluginLoaded(const MUUID &uuid) -{ - for (auto &it : g_arPlugins) - if (it->getInfo().uuid == uuid) - return it->getInst() != nullptr; - - return false; -} - -const char* GetPluginNameByInstance(HINSTANCE hInst) -{ - HINSTANCE boo[2] = { 0, hInst }; - CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo); - return (pPlugin == nullptr) ? nullptr : pPlugin->getInfo().shortName; -} - -MIR_APP_DLL(CMPluginBase&) GetPluginByInstance(HINSTANCE hInst) -{ - HINSTANCE boo[2] = { 0, hInst }; - CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo); - return (pPlugin == nullptr) ? g_plugin : *pPlugin; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// stubs for pascal plugins - -static void wipePluginData(CMPluginBase *pPlugin) -{ - if (Miranda_IsTerminated()) - return; - - KillModuleMenus(pPlugin); - KillModuleFonts(pPlugin); - KillModuleIcons(pPlugin); - KillModuleHotkeys(pPlugin); - KillModulePopups(pPlugin); - KillModuleSounds(pPlugin); - KillModuleExtraIcons(pPlugin); - KillModuleSrmmIcons(pPlugin); - KillModuleToolbarIcons(pPlugin); - KillModuleOptions(pPlugin); -} - -// emulates the call of CMPluginBase::CMPluginBase for Pascal plugins -EXTERN_C MIR_APP_DLL(void) RegisterPlugin(CMPluginBase *pPlugin) -{ - if (pPlugin->getInst() != nullptr) - g_arPlugins.insert(pPlugin); -} - -// emulates the call of CMPluginBase::~CMPluginBase for Pascal plugins -EXTERN_C MIR_APP_DLL(void) UnregisterPlugin(CMPluginBase *pPlugin) -{ - wipePluginData(pPlugin); - g_arPlugins.remove(pPlugin); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int CompareIcons(const IcolibItem *p1, const IcolibItem *p2) -{ - return p1->default_indx - p2->default_indx; -} - -CMPluginBase::CMPluginBase(const char *moduleName, const PLUGININFOEX &pInfo) : - m_szModuleName(moduleName), - m_pInfo(pInfo), - m_arIcons(10, CompareIcons) -{ - if (m_hInst != nullptr) - g_arPlugins.insert(this); -} - -CMPluginBase::~CMPluginBase() -{ - wipePluginData(this); - - if (m_hLogger) { - mir_closeLog(m_hLogger); - m_hLogger = nullptr; - } - - g_arPlugins.remove(this); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::Load() -{ - return 0; -} - -int CMPluginBase::Unload() -{ - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMPluginBase::tryOpenLog() -{ - wchar_t path[MAX_PATH]; - mir_snwprintf(path, L"%s\\%S.txt", VARSW(L"%miranda_logpath%").get(), m_szModuleName); - m_hLogger = mir_createLog(m_szModuleName, nullptr, path, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addOptions(WPARAM wParam, OPTIONSDIALOGPAGE *odp) -{ - return ::Options_AddPage(wParam, odp, this); -} - -void CMPluginBase::openOptions(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab) -{ - ::Options_Open(pszGroup, pszPage, pszTab, this); -} - -void CMPluginBase::openOptionsPage(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab) -{ - ::Options_OpenPage(pszGroup, pszPage, pszTab, this); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addFont(FontID *pFont) -{ - return Font_Register(pFont, this); -} - -int CMPluginBase::addFont(FontIDW *pFont) -{ - return Font_RegisterW(pFont, this); -} - -int CMPluginBase::addColor(ColourID *pColor) -{ - return Colour_Register(pColor, this); -} - -int CMPluginBase::addColor(ColourIDW *pColor) -{ - return Colour_RegisterW(pColor, this); -} - -int CMPluginBase::addEffect(EffectID *pEffect) -{ - return Effect_Register(pEffect, this); -} - -int CMPluginBase::addEffect(EffectIDW *pEffect) -{ - return Effect_RegisterW(pEffect, this); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addFrame(const CLISTFrame *F) -{ - return (int)CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)F, (LPARAM)this); -} - -int CMPluginBase::addHotkey(const HOTKEYDESC *hk) -{ - return Hotkey_Register(hk, this); -} - -HANDLE CMPluginBase::addIcon(const SKINICONDESC *sid) -{ - return IcoLib_AddIcon(sid, this); -} - -HGENMENU CMPluginBase::addRootMenu(int hMenuObject, LPCWSTR ptszName, int position, HANDLE hIcoLib) -{ - return Menu_CreateRoot(hMenuObject, ptszName, position, hIcoLib, this); -} - -HANDLE CMPluginBase::addTTB(const struct TTBButton *pButton) -{ - return (HANDLE)CallService(MS_TTB_ADDBUTTON, (WPARAM)pButton, (LPARAM)this); -} - -int CMPluginBase::addUserInfo(WPARAM wParam, USERINFOPAGE *uip) -{ - uip->pPlugin = this; - return CallService("UserInfo/AddPage", wParam, (LPARAM)uip); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMPluginBase::debugLogA(LPCSTR szFormat, ...) -{ - if (m_hLogger == nullptr) - tryOpenLog(); - - va_list args; - va_start(args, szFormat); - mir_writeLogVA(m_hLogger, szFormat, args); - va_end(args); -} - -void CMPluginBase::debugLogW(LPCWSTR wszFormat, ...) -{ - if (m_hLogger == nullptr) - tryOpenLog(); - - va_list args; - va_start(args, wszFormat); - mir_writeLogVW(m_hLogger, wszFormat, args); - va_end(args); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -#ifdef _WINDOWS -int CMPluginBase::addImgListIcon(HIMAGELIST himl, int iconId) -{ - HICON hIcon = getIcon(iconId); - int ret = ::ImageList_AddIcon(himl, hIcon); - IcoLib_ReleaseIcon(hIcon); - return ret; -} -#endif - -HICON CMPluginBase::getIcon(int iconId, bool big) -{ - return IcoLib_GetIconByHandle(getIconHandle(iconId), big); -} - -HANDLE CMPluginBase::getIconHandle(int iconId) -{ - IcolibItem *p = (IcolibItem*)alloca(sizeof(IcolibItem)); - p->default_indx = -iconId; - return m_arIcons.find(p); -} - -void CMPluginBase::releaseIcon(int iconId, bool big) -{ - IcoLib_ReleaseIcon(getIcon(iconId), big); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMPluginBase::RegisterProtocol(int type, pfnInitProto fnInit, pfnUninitProto fnUninit) -{ - if (isPluginBanned(m_pInfo.uuid)) - return; - - if (type == PROTOTYPE_PROTOCOL && fnInit != nullptr) - type = PROTOTYPE_PROTOWITHACCS; - - MBaseProto *pd = (MBaseProto*)Proto_RegisterModule(type, m_szModuleName); - if (pd) { - pd->fnInit = fnInit; - pd->fnUninit = fnUninit; - pd->hInst = m_hInst; - } -} - -void CMPluginBase::SetUniqueId(const char *pszUniqueId) -{ - if (pszUniqueId == nullptr) - return; - - MBaseProto *pd = g_arProtos.find((MBaseProto*)&m_szModuleName); - if (pd != nullptr) - pd->szUniqueId = mir_strdup(pszUniqueId); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "plugins.h"
+#include "IcoLib.h"
+
+static int sttFakeID = -100;
+
+static int Compare(const CMPluginBase *p1, const CMPluginBase *p2)
+{
+ return INT_PTR(p1->getInst()) - INT_PTR(p2->getInst());
+}
+
+static LIST<CMPluginBase> g_arPlugins(10, Compare);
+
+void RegisterModule(CMPluginBase *pPlugin)
+{
+ g_arPlugins.insert(pPlugin);
+}
+
+MIR_APP_DLL(HINSTANCE) GetInstByAddress(void *codePtr)
+{
+ if (g_arPlugins.getCount() == 0)
+ return nullptr;
+
+ int idx;
+ void *boo[2] = { 0, codePtr };
+ List_GetIndex((SortedList*)&g_arPlugins, (CMPluginBase*)&boo, &idx);
+ if (idx > 0)
+ idx--;
+
+ HINSTANCE result = g_arPlugins[idx]->getInst();
+ if (result < g_plugin.getInst() && codePtr > g_plugin.getInst())
+ return g_plugin.getInst();
+
+ if (idx == 0 && codePtr < (void*)result)
+ return nullptr;
+
+ return result;
+}
+
+MIR_APP_DLL(int) GetPluginLangId(const MUUID &uuid, int _hLang)
+{
+ if (uuid == miid_last)
+ return --sttFakeID;
+
+ for (auto &it : g_arPlugins)
+ if (it->getInfo().uuid == uuid)
+ return (_hLang) ? _hLang : --sttFakeID;
+
+ return 0;
+}
+
+MIR_APP_DLL(int) IsPluginLoaded(const MUUID &uuid)
+{
+ for (auto &it : g_arPlugins)
+ if (it->getInfo().uuid == uuid)
+ return it->getInst() != nullptr;
+
+ return false;
+}
+
+const char* GetPluginNameByInstance(HINSTANCE hInst)
+{
+ HINSTANCE boo[2] = { 0, hInst };
+ CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo);
+ return (pPlugin == nullptr) ? nullptr : pPlugin->getInfo().shortName;
+}
+
+MIR_APP_DLL(CMPluginBase&) GetPluginByInstance(HINSTANCE hInst)
+{
+ HINSTANCE boo[2] = { 0, hInst };
+ CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo);
+ return (pPlugin == nullptr) ? g_plugin : *pPlugin;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// stubs for pascal plugins
+
+static void wipePluginData(CMPluginBase *pPlugin)
+{
+ if (Miranda_IsTerminated())
+ return;
+
+ KillModuleMenus(pPlugin);
+ KillModuleFonts(pPlugin);
+ KillModuleIcons(pPlugin);
+ KillModuleHotkeys(pPlugin);
+ KillModulePopups(pPlugin);
+ KillModuleSounds(pPlugin);
+ KillModuleExtraIcons(pPlugin);
+ KillModuleSrmmIcons(pPlugin);
+ KillModuleToolbarIcons(pPlugin);
+ KillModuleOptions(pPlugin);
+}
+
+// emulates the call of CMPluginBase::CMPluginBase for Pascal plugins
+EXTERN_C MIR_APP_DLL(void) RegisterPlugin(CMPluginBase *pPlugin)
+{
+ if (pPlugin->getInst() != nullptr)
+ g_arPlugins.insert(pPlugin);
+}
+
+// emulates the call of CMPluginBase::~CMPluginBase for Pascal plugins
+EXTERN_C MIR_APP_DLL(void) UnregisterPlugin(CMPluginBase *pPlugin)
+{
+ wipePluginData(pPlugin);
+ g_arPlugins.remove(pPlugin);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int CompareIcons(const IcolibItem *p1, const IcolibItem *p2)
+{
+ return p1->default_indx - p2->default_indx;
+}
+
+CMPluginBase::CMPluginBase(const char *moduleName, const PLUGININFOEX &pInfo) :
+ m_szModuleName(moduleName),
+ m_pInfo(pInfo),
+ m_arIcons(10, CompareIcons)
+{
+ if (m_hInst != nullptr)
+ g_arPlugins.insert(this);
+}
+
+CMPluginBase::~CMPluginBase()
+{
+ wipePluginData(this);
+
+ if (m_hLogger) {
+ mir_closeLog(m_hLogger);
+ m_hLogger = nullptr;
+ }
+
+ g_arPlugins.remove(this);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPluginBase::Load()
+{
+ return 0;
+}
+
+int CMPluginBase::Unload()
+{
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMPluginBase::tryOpenLog()
+{
+ wchar_t path[MAX_PATH];
+ mir_snwprintf(path, L"%s\\%S.txt", VARSW(L"%miranda_logpath%").get(), m_szModuleName);
+ m_hLogger = mir_createLog(m_szModuleName, nullptr, path, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPluginBase::addOptions(WPARAM wParam, OPTIONSDIALOGPAGE *odp)
+{
+ return ::Options_AddPage(wParam, odp, this);
+}
+
+void CMPluginBase::openOptions(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab)
+{
+ ::Options_Open(pszGroup, pszPage, pszTab, this);
+}
+
+void CMPluginBase::openOptionsPage(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab)
+{
+ ::Options_OpenPage(pszGroup, pszPage, pszTab, this);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPluginBase::addFont(FontID *pFont)
+{
+ return Font_Register(pFont, this);
+}
+
+int CMPluginBase::addFont(FontIDW *pFont)
+{
+ return Font_RegisterW(pFont, this);
+}
+
+int CMPluginBase::addColor(ColourID *pColor)
+{
+ return Colour_Register(pColor, this);
+}
+
+int CMPluginBase::addColor(ColourIDW *pColor)
+{
+ return Colour_RegisterW(pColor, this);
+}
+
+int CMPluginBase::addEffect(EffectID *pEffect)
+{
+ return Effect_Register(pEffect, this);
+}
+
+int CMPluginBase::addEffect(EffectIDW *pEffect)
+{
+ return Effect_RegisterW(pEffect, this);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPluginBase::addFrame(const CLISTFrame *F)
+{
+ return (int)CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)F, (LPARAM)this);
+}
+
+int CMPluginBase::addHotkey(const HOTKEYDESC *hk)
+{
+ return Hotkey_Register(hk, this);
+}
+
+HANDLE CMPluginBase::addIcon(const SKINICONDESC *sid)
+{
+ return IcoLib_AddIcon(sid, this);
+}
+
+HGENMENU CMPluginBase::addRootMenu(int hMenuObject, LPCWSTR ptszName, int position, HANDLE hIcoLib)
+{
+ return Menu_CreateRoot(hMenuObject, ptszName, position, hIcoLib, this);
+}
+
+HANDLE CMPluginBase::addTTB(const struct TTBButton *pButton)
+{
+ return (HANDLE)CallService(MS_TTB_ADDBUTTON, (WPARAM)pButton, (LPARAM)this);
+}
+
+int CMPluginBase::addUserInfo(WPARAM wParam, USERINFOPAGE *uip)
+{
+ uip->pPlugin = this;
+ return CallService("UserInfo/AddPage", wParam, (LPARAM)uip);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMPluginBase::debugLogA(LPCSTR szFormat, ...)
+{
+ if (m_hLogger == nullptr)
+ tryOpenLog();
+
+ va_list args;
+ va_start(args, szFormat);
+ mir_writeLogVA(m_hLogger, szFormat, args);
+ va_end(args);
+}
+
+void CMPluginBase::debugLogW(LPCWSTR wszFormat, ...)
+{
+ if (m_hLogger == nullptr)
+ tryOpenLog();
+
+ va_list args;
+ va_start(args, wszFormat);
+ mir_writeLogVW(m_hLogger, wszFormat, args);
+ va_end(args);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef _WINDOWS
+int CMPluginBase::addImgListIcon(HIMAGELIST himl, int iconId)
+{
+ HICON hIcon = getIcon(iconId);
+ int ret = ::ImageList_AddIcon(himl, hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+ return ret;
+}
+#endif
+
+HICON CMPluginBase::getIcon(int iconId, bool big)
+{
+ return IcoLib_GetIconByHandle(getIconHandle(iconId), big);
+}
+
+HANDLE CMPluginBase::getIconHandle(int iconId)
+{
+ IcolibItem *p = (IcolibItem*)alloca(sizeof(IcolibItem));
+ p->default_indx = -iconId;
+ return m_arIcons.find(p);
+}
+
+void CMPluginBase::releaseIcon(int iconId, bool big)
+{
+ IcoLib_ReleaseIcon(getIcon(iconId), big);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMPluginBase::RegisterProtocol(int type, pfnInitProto fnInit, pfnUninitProto fnUninit)
+{
+ if (isPluginBanned(m_pInfo.uuid))
+ return;
+
+ if (type == PROTOTYPE_PROTOCOL && fnInit != nullptr)
+ type = PROTOTYPE_PROTOWITHACCS;
+
+ MBaseProto *pd = (MBaseProto*)Proto_RegisterModule(type, m_szModuleName);
+ if (pd) {
+ pd->fnInit = fnInit;
+ pd->fnUninit = fnUninit;
+ pd->hInst = m_hInst;
+ }
+}
+
+void CMPluginBase::SetUniqueId(const char *pszUniqueId)
+{
+ if (pszUniqueId == nullptr)
+ return;
+
+ MBaseProto *pd = g_arProtos.find((MBaseProto*)&m_szModuleName);
+ if (pd != nullptr)
+ pd->szUniqueId = mir_strdup(pszUniqueId);
+}
diff --git a/src/mir_app/src/Docking.cpp b/src/mir_app/src/Docking.cpp index 9ea5cd9f58..c8dad68b95 100644 --- a/src/mir_app/src/Docking.cpp +++ b/src/mir_app/src/Docking.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/FontOptions.cpp b/src/mir_app/src/FontOptions.cpp index 071da22914..5a8fd4aff8 100644 --- a/src/mir_app/src/FontOptions.cpp +++ b/src/mir_app/src/FontOptions.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/FontService.cpp b/src/mir_app/src/FontService.cpp index 2f4f300ff2..27f16e2ce2 100644 --- a/src/mir_app/src/FontService.cpp +++ b/src/mir_app/src/FontService.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/FontService.h b/src/mir_app/src/FontService.h index e86799ab35..2e562bd185 100644 --- a/src/mir_app/src/FontService.h +++ b/src/mir_app/src/FontService.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/IcoLib.h b/src/mir_app/src/IcoLib.h index 385d1acfc4..78dffe4cf8 100644 --- a/src/mir_app/src/IcoLib.h +++ b/src/mir_app/src/IcoLib.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/MDatabaseCommon.cpp b/src/mir_app/src/MDatabaseCommon.cpp index 033cbea2b9..323954a516 100644 --- a/src/mir_app/src/MDatabaseCommon.cpp +++ b/src/mir_app/src/MDatabaseCommon.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team, +Copyright (C) 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/MDatabaseCommonCrypt.cpp b/src/mir_app/src/MDatabaseCommonCrypt.cpp index bbfd18f399..88ec00a1cd 100644 --- a/src/mir_app/src/MDatabaseCommonCrypt.cpp +++ b/src/mir_app/src/MDatabaseCommonCrypt.cpp @@ -1,449 +1,449 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "database.h" -#include "encrypt.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// Provider selection dialog - -class CSelectCryptoDialog : public CDlgBase -{ - CCtrlCombo m_combo; - CCtrlData m_descr; - CRYPTO_PROVIDER *m_selected = nullptr; - - CRYPTO_PROVIDER *getCurrent() - { - return (CRYPTO_PROVIDER*)m_combo.GetCurData(); - } - - bool OnInitDialog() override - { - for (auto &p : arCryptoProviders) - m_combo.AddStringA(p->pszName, LPARAM(p)); - - m_combo.SetCurSel(0); - m_descr.SetText(arCryptoProviders[0].szDescr.w); - return true; - } - - bool OnApply() override - { - m_selected = getCurrent(); - return true; - } - - void OnComboChanged(CCtrlCombo*) - { - m_descr.SetText(getCurrent()->szDescr.w); - } - -public: - CSelectCryptoDialog() : - CDlgBase(g_plugin, IDD_SELECT_CRYPTOPROVIDER), - m_combo(this, IDC_SELECTCRYPT_COMBO), - m_descr(this, IDC_CRYPTOPROVIDER_DESCR) - { - m_combo.OnChange = Callback(this, &CSelectCryptoDialog::OnComboChanged); - } - - inline CRYPTO_PROVIDER* GetSelected() - { return m_selected; - } -}; - -CRYPTO_PROVIDER* MDatabaseCommon::SelectProvider() -{ - if (arCryptoProviders.getCount() == 0) - return nullptr; - - CRYPTO_PROVIDER *pProv; - if (arCryptoProviders.getCount() > 1) { - CSelectCryptoDialog dlg; - dlg.DoModal(); - pProv = dlg.GetSelected(); - } - else pProv = &arCryptoProviders[0]; - - return (StoreProvider(pProv)) ? pProv : nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL MDatabaseCommon::IsSettingEncrypted(LPCSTR szModule, LPCSTR szSetting) -{ - if (!_strnicmp(szSetting, "password", 8)) return true; - if (!mir_strcmp(szSetting, "NLProxyAuthPassword")) return true; - if (!mir_strcmp(szSetting, "LNPassword")) return true; - if (!mir_strcmp(szSetting, "FileProxyPassword")) return true; - if (!mir_strcmp(szSetting, "TokenSecret")) return true; - - if (!mir_strcmp(szModule, "SecureIM")) { - if (!mir_strcmp(szSetting, "pgp")) return true; - if (!mir_strcmp(szSetting, "pgpPrivKey")) return true; - } - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static HGENMENU hSetPwdMenu; - -__forceinline wchar_t *GetMenuTitle(bool bUsesPassword) -{ - return bUsesPassword ? LPGENW("Change/remove password") : LPGENW("Set password"); -} - -void MDatabaseCommon::SetPassword(const wchar_t *ptszPassword) -{ - if (mir_wstrlen(ptszPassword) == 0) { - m_bUsesPassword = false; - m_crypto->setPassword(nullptr); - } - else { - m_bUsesPassword = true; - m_crypto->setPassword(pass_ptrA(mir_utf8encodeW(ptszPassword))); - } - - Menu_ModifyItem(hSetPwdMenu, GetMenuTitle(m_bUsesPassword), Skin_GetIconHandle(SKINICON_OTHER_KEYS)); -} - -static UINT oldLangID; -void LanguageChanged(HWND hwndDlg) -{ - UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0); - char Lang[3] = { 0 }; - if (LangID != oldLangID) { - oldLangID = LangID; - GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2); - Lang[0] = toupper(Lang[0]); - Lang[1] = tolower(Lang[1]); - SetDlgItemTextA(hwndDlg, IDC_LANG, Lang); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static bool CheckOldPassword(HWND hwndDlg, MDatabaseCommon *db) -{ - if (db->usesPassword()) { - wchar_t buf[100]; - GetDlgItemText(hwndDlg, IDC_OLDPASS, buf, _countof(buf)); - pass_ptrA oldPass(mir_utf8encodeW(buf)); - if (!db->getCrypt()->checkPassword(oldPass)) { - SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Wrong old password entered!")); - return false; - } - } - return true; -} - -static INT_PTR CALLBACK sttChangePassword(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - MDatabaseCommon *db = (MDatabaseCommon *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (uMsg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true)); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); - - oldLangID = 0; - SetTimer(hwndDlg, 1, 200, nullptr); - LanguageChanged(hwndDlg); - return TRUE; - - case WM_CTLCOLORSTATIC: - if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_LANG)) { - SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT); - } - return FALSE; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - - case IDREMOVE: - if (!CheckOldPassword(hwndDlg, db)) { -LBL_Error: - SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_NCPAINT, 0, 0); - SetDlgItemTextA(hwndDlg, IDC_USERPASS1, ""); - SetDlgItemTextA(hwndDlg, IDC_USERPASS2, ""); - } - else { - db->SetPassword(nullptr); - db->StoreCryptoKey(); - EndDialog(hwndDlg, IDREMOVE); - } - break; - - case IDOK: - wchar_t buf2[100]; - GetDlgItemText(hwndDlg, IDC_USERPASS1, buf2, _countof(buf2)); - if (wcslen(buf2) < 3) { - SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Password is too short!")); - goto LBL_Error; - } - - wchar_t buf[100]; - GetDlgItemText(hwndDlg, IDC_USERPASS2, buf, _countof(buf)); - if (wcscmp(buf2, buf)) { - SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Passwords do not match!")); - goto LBL_Error; - } - - if (!CheckOldPassword(hwndDlg, db)) - goto LBL_Error; - - db->SetPassword(buf2); - db->StoreCryptoKey(); - SecureZeroMemory(buf, sizeof(buf)); - SecureZeroMemory(buf2, sizeof(buf2)); - EndDialog(hwndDlg, IDOK); - } - break; - - case WM_TIMER: - LanguageChanged(hwndDlg); - return FALSE; - - case WM_DESTROY: - KillTimer(hwndDlg, 1); - Window_FreeIcon_IcoLib(GetDlgItem(hwndDlg, IDC_HEADERBAR)); - } - - return FALSE; -} - -static INT_PTR ChangePassword(void* obj, WPARAM, LPARAM) -{ - MDatabaseCommon *db = (MDatabaseCommon*)obj; - DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(db->usesPassword() ? IDD_CHANGEPASS : IDD_NEWPASS), nullptr, sttChangePassword, (LPARAM)db); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Options - -class CDatabaseOptionsDialog : public CDlgBase -{ - CCtrlCheck m_chkStandart, m_chkTotal; - CCtrlButton m_btnChangePass; - MDatabaseCommon *m_db; - -public: - CDatabaseOptionsDialog(MDatabaseCommon *db) : - CDlgBase(g_plugin, IDD_OPT_DATABASE), - m_db(db), - m_chkTotal(this, IDC_TOTAL), - m_chkStandart(this, IDC_STANDARD), - m_btnChangePass(this, IDC_USERPASS1) - { - m_btnChangePass.OnClick = Callback(this, &CDatabaseOptionsDialog::onClick_ChangePass); - } - - bool OnInitDialog() override - { - m_chkStandart.SetState(!m_db->isEncrypted()); - m_chkTotal.SetState(m_db->isEncrypted()); - return true; - } - - bool OnApply() override - { - SetCursor(LoadCursor(nullptr, IDC_WAIT)); - m_db->EnableEncryption(m_chkTotal.GetState() != 0); - SetCursor(LoadCursor(nullptr, IDC_ARROW)); - m_chkStandart.SetState(!m_db->isEncrypted()); - m_chkTotal.SetState(m_db->isEncrypted()); - return true; - } - - void onClick_ChangePass(CCtrlButton *) - { - ChangePassword(m_db, 0, 0); - } -}; - -static int OnOptionsInit(PVOID obj, WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = { sizeof(odp) }; - odp.position = -790000000; - odp.flags = ODPF_BOLDGROUPS; - odp.szTitle.a = LPGEN("Database"); - odp.pDialog = new CDatabaseOptionsDialog((MDatabaseCommon*)obj); - g_plugin.addOptions(wParam, &odp); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void InitCryptMenuItem(CMenuItem &mi) -{ - auto *pDb = db_get_current(); - - HookEventObj(ME_OPT_INITIALISE, OnOptionsInit, pDb); - - SET_UID(mi, 0x50321866, 0xba1, 0x46dd, 0xb3, 0xa6, 0xc3, 0xcc, 0x55, 0xf2, 0x42, 0x9e); - mi.flags = CMIF_UNICODE; - mi.position = 1000000001; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_KEYS); - mi.name.w = GetMenuTitle(pDb->usesPassword()); - mi.pszService = "DB/UI/ChangePassword"; - hSetPwdMenu = Menu_AddMainMenuItem(&mi); - CreateServiceFunctionObj(mi.pszService, ChangePassword, pDb); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CEnterPasswordDialog : public CDlgBase -{ - friend class MDatabaseCommon; - - CTimer m_timer; - CCtrlData m_header; - CCtrlData m_language; - CCtrlEdit m_passwordEdit; - - int m_wrongPass = 0; - wchar_t m_newPass[100]; - MDatabaseCommon *m_db; - - void OnTimer(CTimer*) - { - UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0); - char Lang[3] = { 0 }; - GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2); - Lang[0] = toupper(Lang[0]); - Lang[1] = tolower(Lang[1]); - m_language.SetTextA(Lang); - } - - INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override - { - if (msg == WM_CTLCOLORSTATIC) { - if ((HWND)lParam == m_language.GetHwnd()) { - SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT); - } - } - return CDlgBase::DlgProc(msg, wParam, lParam); - } - -public: - CEnterPasswordDialog(MDatabaseCommon *db) : - CDlgBase(g_plugin, IDD_LOGIN), - m_timer(this, 1), - m_header(this, IDC_HEADERBAR), - m_language(this, IDC_LANG), - m_passwordEdit(this, IDC_USERPASS), - m_db(db) - { - m_newPass[0] = 0; - m_timer.OnEvent = Callback(this, &CEnterPasswordDialog::OnTimer); - } - - ~CEnterPasswordDialog() - { - SecureZeroMemory(m_newPass, sizeof(m_newPass)); - } - - bool OnInitDialog() override - { - m_header.SendMsg(WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true)); - - if (m_wrongPass) { - if (m_wrongPass > 2) { - m_passwordEdit.Disable(); - EnableWindow(GetDlgItem(m_hwnd, IDOK), false); - m_header.SetText(TranslateT("Too many errors!")); - } - else m_header.SetText(TranslateT("Password is not correct!")); - } - else m_header.SetText(TranslateT("Please type in your password")); - - m_timer.Start(200); - return true; - } - - bool OnApply() override - { - m_passwordEdit.GetText(m_newPass, _countof(m_newPass)); - return true; - } - - void OnDestroy() override - { - Window_FreeIcon_IcoLib(m_header.GetHwnd()); - } -}; - -int MDatabaseCommon::InitCrypt() -{ - if (m_crypto != nullptr) - return 0; - - CRYPTO_PROVIDER *pProvider = ReadProvider(); - if (pProvider == nullptr) - pProvider = SelectProvider(); - if (pProvider == nullptr) - return 1; - - if ((m_crypto = pProvider->pFactory()) == nullptr) - return 3; - - MBinBuffer key; - BOOL bSuccess = ReadCryptoKey(key); - if (bSuccess && (key.length() == m_crypto->getKeyLength())) { - // first try to set a key without password - if (!m_crypto->setKey(nullptr, (const uint8_t*)key.data(), key.length())) { - CEnterPasswordDialog dlg(this); - while (true) { - if (!dlg.DoModal()) - return 4; - - pass_ptrA szPassword(mir_utf8encodeW(dlg.m_newPass)); - if (m_crypto->setKey(szPassword, (const uint8_t*)key.data(), key.length())) { - m_bUsesPassword = true; - break; - } - dlg.m_wrongPass++; - } - } - } - else { - if (!m_crypto->generateKey()) - return 6; - StoreCryptoKey(); - } - - m_bEncrypted = ReadEncryption(); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "database.h"
+#include "encrypt.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Provider selection dialog
+
+class CSelectCryptoDialog : public CDlgBase
+{
+ CCtrlCombo m_combo;
+ CCtrlData m_descr;
+ CRYPTO_PROVIDER *m_selected = nullptr;
+
+ CRYPTO_PROVIDER *getCurrent()
+ {
+ return (CRYPTO_PROVIDER*)m_combo.GetCurData();
+ }
+
+ bool OnInitDialog() override
+ {
+ for (auto &p : arCryptoProviders)
+ m_combo.AddStringA(p->pszName, LPARAM(p));
+
+ m_combo.SetCurSel(0);
+ m_descr.SetText(arCryptoProviders[0].szDescr.w);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ m_selected = getCurrent();
+ return true;
+ }
+
+ void OnComboChanged(CCtrlCombo*)
+ {
+ m_descr.SetText(getCurrent()->szDescr.w);
+ }
+
+public:
+ CSelectCryptoDialog() :
+ CDlgBase(g_plugin, IDD_SELECT_CRYPTOPROVIDER),
+ m_combo(this, IDC_SELECTCRYPT_COMBO),
+ m_descr(this, IDC_CRYPTOPROVIDER_DESCR)
+ {
+ m_combo.OnChange = Callback(this, &CSelectCryptoDialog::OnComboChanged);
+ }
+
+ inline CRYPTO_PROVIDER* GetSelected()
+ { return m_selected;
+ }
+};
+
+CRYPTO_PROVIDER* MDatabaseCommon::SelectProvider()
+{
+ if (arCryptoProviders.getCount() == 0)
+ return nullptr;
+
+ CRYPTO_PROVIDER *pProv;
+ if (arCryptoProviders.getCount() > 1) {
+ CSelectCryptoDialog dlg;
+ dlg.DoModal();
+ pProv = dlg.GetSelected();
+ }
+ else pProv = &arCryptoProviders[0];
+
+ return (StoreProvider(pProv)) ? pProv : nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+BOOL MDatabaseCommon::IsSettingEncrypted(LPCSTR szModule, LPCSTR szSetting)
+{
+ if (!_strnicmp(szSetting, "password", 8)) return true;
+ if (!mir_strcmp(szSetting, "NLProxyAuthPassword")) return true;
+ if (!mir_strcmp(szSetting, "LNPassword")) return true;
+ if (!mir_strcmp(szSetting, "FileProxyPassword")) return true;
+ if (!mir_strcmp(szSetting, "TokenSecret")) return true;
+
+ if (!mir_strcmp(szModule, "SecureIM")) {
+ if (!mir_strcmp(szSetting, "pgp")) return true;
+ if (!mir_strcmp(szSetting, "pgpPrivKey")) return true;
+ }
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static HGENMENU hSetPwdMenu;
+
+__forceinline wchar_t *GetMenuTitle(bool bUsesPassword)
+{
+ return bUsesPassword ? LPGENW("Change/remove password") : LPGENW("Set password");
+}
+
+void MDatabaseCommon::SetPassword(const wchar_t *ptszPassword)
+{
+ if (mir_wstrlen(ptszPassword) == 0) {
+ m_bUsesPassword = false;
+ m_crypto->setPassword(nullptr);
+ }
+ else {
+ m_bUsesPassword = true;
+ m_crypto->setPassword(pass_ptrA(mir_utf8encodeW(ptszPassword)));
+ }
+
+ Menu_ModifyItem(hSetPwdMenu, GetMenuTitle(m_bUsesPassword), Skin_GetIconHandle(SKINICON_OTHER_KEYS));
+}
+
+static UINT oldLangID;
+void LanguageChanged(HWND hwndDlg)
+{
+ UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0);
+ char Lang[3] = { 0 };
+ if (LangID != oldLangID) {
+ oldLangID = LangID;
+ GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2);
+ Lang[0] = toupper(Lang[0]);
+ Lang[1] = tolower(Lang[1]);
+ SetDlgItemTextA(hwndDlg, IDC_LANG, Lang);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static bool CheckOldPassword(HWND hwndDlg, MDatabaseCommon *db)
+{
+ if (db->usesPassword()) {
+ wchar_t buf[100];
+ GetDlgItemText(hwndDlg, IDC_OLDPASS, buf, _countof(buf));
+ pass_ptrA oldPass(mir_utf8encodeW(buf));
+ if (!db->getCrypt()->checkPassword(oldPass)) {
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Wrong old password entered!"));
+ return false;
+ }
+ }
+ return true;
+}
+
+static INT_PTR CALLBACK sttChangePassword(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ MDatabaseCommon *db = (MDatabaseCommon *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+
+ oldLangID = 0;
+ SetTimer(hwndDlg, 1, 200, nullptr);
+ LanguageChanged(hwndDlg);
+ return TRUE;
+
+ case WM_CTLCOLORSTATIC:
+ if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_LANG)) {
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ SetBkMode((HDC)wParam, TRANSPARENT);
+ return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT);
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ EndDialog(hwndDlg, IDCANCEL);
+ break;
+
+ case IDREMOVE:
+ if (!CheckOldPassword(hwndDlg, db)) {
+LBL_Error:
+ SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_NCPAINT, 0, 0);
+ SetDlgItemTextA(hwndDlg, IDC_USERPASS1, "");
+ SetDlgItemTextA(hwndDlg, IDC_USERPASS2, "");
+ }
+ else {
+ db->SetPassword(nullptr);
+ db->StoreCryptoKey();
+ EndDialog(hwndDlg, IDREMOVE);
+ }
+ break;
+
+ case IDOK:
+ wchar_t buf2[100];
+ GetDlgItemText(hwndDlg, IDC_USERPASS1, buf2, _countof(buf2));
+ if (wcslen(buf2) < 3) {
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Password is too short!"));
+ goto LBL_Error;
+ }
+
+ wchar_t buf[100];
+ GetDlgItemText(hwndDlg, IDC_USERPASS2, buf, _countof(buf));
+ if (wcscmp(buf2, buf)) {
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Passwords do not match!"));
+ goto LBL_Error;
+ }
+
+ if (!CheckOldPassword(hwndDlg, db))
+ goto LBL_Error;
+
+ db->SetPassword(buf2);
+ db->StoreCryptoKey();
+ SecureZeroMemory(buf, sizeof(buf));
+ SecureZeroMemory(buf2, sizeof(buf2));
+ EndDialog(hwndDlg, IDOK);
+ }
+ break;
+
+ case WM_TIMER:
+ LanguageChanged(hwndDlg);
+ return FALSE;
+
+ case WM_DESTROY:
+ KillTimer(hwndDlg, 1);
+ Window_FreeIcon_IcoLib(GetDlgItem(hwndDlg, IDC_HEADERBAR));
+ }
+
+ return FALSE;
+}
+
+static INT_PTR ChangePassword(void* obj, WPARAM, LPARAM)
+{
+ MDatabaseCommon *db = (MDatabaseCommon*)obj;
+ DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(db->usesPassword() ? IDD_CHANGEPASS : IDD_NEWPASS), nullptr, sttChangePassword, (LPARAM)db);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Options
+
+class CDatabaseOptionsDialog : public CDlgBase
+{
+ CCtrlCheck m_chkStandart, m_chkTotal;
+ CCtrlButton m_btnChangePass;
+ MDatabaseCommon *m_db;
+
+public:
+ CDatabaseOptionsDialog(MDatabaseCommon *db) :
+ CDlgBase(g_plugin, IDD_OPT_DATABASE),
+ m_db(db),
+ m_chkTotal(this, IDC_TOTAL),
+ m_chkStandart(this, IDC_STANDARD),
+ m_btnChangePass(this, IDC_USERPASS1)
+ {
+ m_btnChangePass.OnClick = Callback(this, &CDatabaseOptionsDialog::onClick_ChangePass);
+ }
+
+ bool OnInitDialog() override
+ {
+ m_chkStandart.SetState(!m_db->isEncrypted());
+ m_chkTotal.SetState(m_db->isEncrypted());
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ SetCursor(LoadCursor(nullptr, IDC_WAIT));
+ m_db->EnableEncryption(m_chkTotal.GetState() != 0);
+ SetCursor(LoadCursor(nullptr, IDC_ARROW));
+ m_chkStandart.SetState(!m_db->isEncrypted());
+ m_chkTotal.SetState(m_db->isEncrypted());
+ return true;
+ }
+
+ void onClick_ChangePass(CCtrlButton *)
+ {
+ ChangePassword(m_db, 0, 0);
+ }
+};
+
+static int OnOptionsInit(PVOID obj, WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = { sizeof(odp) };
+ odp.position = -790000000;
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.szTitle.a = LPGEN("Database");
+ odp.pDialog = new CDatabaseOptionsDialog((MDatabaseCommon*)obj);
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void InitCryptMenuItem(CMenuItem &mi)
+{
+ auto *pDb = db_get_current();
+
+ HookEventObj(ME_OPT_INITIALISE, OnOptionsInit, pDb);
+
+ SET_UID(mi, 0x50321866, 0xba1, 0x46dd, 0xb3, 0xa6, 0xc3, 0xcc, 0x55, 0xf2, 0x42, 0x9e);
+ mi.flags = CMIF_UNICODE;
+ mi.position = 1000000001;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_KEYS);
+ mi.name.w = GetMenuTitle(pDb->usesPassword());
+ mi.pszService = "DB/UI/ChangePassword";
+ hSetPwdMenu = Menu_AddMainMenuItem(&mi);
+ CreateServiceFunctionObj(mi.pszService, ChangePassword, pDb);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CEnterPasswordDialog : public CDlgBase
+{
+ friend class MDatabaseCommon;
+
+ CTimer m_timer;
+ CCtrlData m_header;
+ CCtrlData m_language;
+ CCtrlEdit m_passwordEdit;
+
+ int m_wrongPass = 0;
+ wchar_t m_newPass[100];
+ MDatabaseCommon *m_db;
+
+ void OnTimer(CTimer*)
+ {
+ UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0);
+ char Lang[3] = { 0 };
+ GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2);
+ Lang[0] = toupper(Lang[0]);
+ Lang[1] = tolower(Lang[1]);
+ m_language.SetTextA(Lang);
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
+ {
+ if (msg == WM_CTLCOLORSTATIC) {
+ if ((HWND)lParam == m_language.GetHwnd()) {
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ SetBkMode((HDC)wParam, TRANSPARENT);
+ return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT);
+ }
+ }
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+ }
+
+public:
+ CEnterPasswordDialog(MDatabaseCommon *db) :
+ CDlgBase(g_plugin, IDD_LOGIN),
+ m_timer(this, 1),
+ m_header(this, IDC_HEADERBAR),
+ m_language(this, IDC_LANG),
+ m_passwordEdit(this, IDC_USERPASS),
+ m_db(db)
+ {
+ m_newPass[0] = 0;
+ m_timer.OnEvent = Callback(this, &CEnterPasswordDialog::OnTimer);
+ }
+
+ ~CEnterPasswordDialog()
+ {
+ SecureZeroMemory(m_newPass, sizeof(m_newPass));
+ }
+
+ bool OnInitDialog() override
+ {
+ m_header.SendMsg(WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true));
+
+ if (m_wrongPass) {
+ if (m_wrongPass > 2) {
+ m_passwordEdit.Disable();
+ EnableWindow(GetDlgItem(m_hwnd, IDOK), false);
+ m_header.SetText(TranslateT("Too many errors!"));
+ }
+ else m_header.SetText(TranslateT("Password is not correct!"));
+ }
+ else m_header.SetText(TranslateT("Please type in your password"));
+
+ m_timer.Start(200);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ m_passwordEdit.GetText(m_newPass, _countof(m_newPass));
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Window_FreeIcon_IcoLib(m_header.GetHwnd());
+ }
+};
+
+int MDatabaseCommon::InitCrypt()
+{
+ if (m_crypto != nullptr)
+ return 0;
+
+ CRYPTO_PROVIDER *pProvider = ReadProvider();
+ if (pProvider == nullptr)
+ pProvider = SelectProvider();
+ if (pProvider == nullptr)
+ return 1;
+
+ if ((m_crypto = pProvider->pFactory()) == nullptr)
+ return 3;
+
+ MBinBuffer key;
+ BOOL bSuccess = ReadCryptoKey(key);
+ if (bSuccess && (key.length() == m_crypto->getKeyLength())) {
+ // first try to set a key without password
+ if (!m_crypto->setKey(nullptr, (const uint8_t*)key.data(), key.length())) {
+ CEnterPasswordDialog dlg(this);
+ while (true) {
+ if (!dlg.DoModal())
+ return 4;
+
+ pass_ptrA szPassword(mir_utf8encodeW(dlg.m_newPass));
+ if (m_crypto->setKey(szPassword, (const uint8_t*)key.data(), key.length())) {
+ m_bUsesPassword = true;
+ break;
+ }
+ dlg.m_wrongPass++;
+ }
+ }
+ }
+ else {
+ if (!m_crypto->generateKey())
+ return 6;
+ StoreCryptoKey();
+ }
+
+ m_bEncrypted = ReadEncryption();
+ return 0;
+}
diff --git a/src/mir_app/src/MDatabaseReadonly.cpp b/src/mir_app/src/MDatabaseReadonly.cpp index f3ed3da7ef..f022d5e5fb 100644 --- a/src/mir_app/src/MDatabaseReadonly.cpp +++ b/src/mir_app/src/MDatabaseReadonly.cpp @@ -1,180 +1,180 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "database.h" - -MDatabaseReadonly::MDatabaseReadonly() -{ -} - -BOOL MDatabaseReadonly::IsRelational(void) -{ - return FALSE; -} - -void MDatabaseReadonly::SetCacheSafetyMode(BOOL) -{ -} - -BOOL MDatabaseReadonly::EnumModuleNames(DBMODULEENUMPROC, void*) -{ - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadCryptoKey(MBinBuffer&) -{ - return FALSE; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreCryptoKey() -{ - return FALSE; -} - -STDMETHODIMP_(CRYPTO_PROVIDER*) MDatabaseReadonly::ReadProvider() -{ - return nullptr; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreProvider(CRYPTO_PROVIDER *) -{ - return FALSE; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::EnableEncryption(BOOL) -{ - return FALSE; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadEncryption() -{ - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MCONTACT MDatabaseReadonly::AddContact(void) -{ - return 0; -} - -int MDatabaseReadonly::DeleteContact(MCONTACT) -{ - return 1; -} - -BOOL MDatabaseReadonly::IsDbContact(MCONTACT contactID) -{ - return contactID == 1; -} - -int MDatabaseReadonly::GetContactSize(void) -{ - return sizeof(DBCachedContact); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MEVENT MDatabaseReadonly::AddEvent(MCONTACT, const DBEVENTINFO*) -{ - return 0; -} - -BOOL MDatabaseReadonly::DeleteEvent(MEVENT) -{ - return 1; -} - -BOOL MDatabaseReadonly::EditEvent(MCONTACT, MEVENT, const DBEVENTINFO*) -{ - return 1; -} - -int MDatabaseReadonly::GetBlobSize(MEVENT) -{ - return 0; -} - -MEVENT MDatabaseReadonly::FindFirstUnreadEvent(MCONTACT) -{ - return 0; -} - -BOOL MDatabaseReadonly::MarkEventRead(MCONTACT, MEVENT) -{ - return 1; -} - -MCONTACT MDatabaseReadonly::GetEventContact(MEVENT) -{ - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL MDatabaseReadonly::GetContactSettingWorker(MCONTACT, LPCSTR, LPCSTR, DBVARIANT*, int) -{ - return 1; -} - -BOOL MDatabaseReadonly::WriteContactSettingWorker(MCONTACT, DBCONTACTWRITESETTING&) -{ - return 1; -} - -BOOL MDatabaseReadonly::DeleteContactSetting(MCONTACT, LPCSTR, LPCSTR) -{ - return 1; -} - -BOOL MDatabaseReadonly::EnumContactSettings(MCONTACT, DBSETTINGENUMPROC, const char*, void*) -{ - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL MDatabaseReadonly::MetaMergeHistory(DBCachedContact*, DBCachedContact*) -{ - return 1; -} - -BOOL MDatabaseReadonly::MetaSplitHistory(DBCachedContact*, DBCachedContact*) -{ - return 1; -} - -BOOL MDatabaseReadonly::MetaRemoveSubHistory(DBCachedContact*) -{ - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MEVENT MDatabaseReadonly::GetEventById(LPCSTR, LPCSTR) -{ - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "database.h"
+
+MDatabaseReadonly::MDatabaseReadonly()
+{
+}
+
+BOOL MDatabaseReadonly::IsRelational(void)
+{
+ return FALSE;
+}
+
+void MDatabaseReadonly::SetCacheSafetyMode(BOOL)
+{
+}
+
+BOOL MDatabaseReadonly::EnumModuleNames(DBMODULEENUMPROC, void*)
+{
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadCryptoKey(MBinBuffer&)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreCryptoKey()
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(CRYPTO_PROVIDER*) MDatabaseReadonly::ReadProvider()
+{
+ return nullptr;
+}
+
+STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreProvider(CRYPTO_PROVIDER *)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(BOOL) MDatabaseReadonly::EnableEncryption(BOOL)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadEncryption()
+{
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MCONTACT MDatabaseReadonly::AddContact(void)
+{
+ return 0;
+}
+
+int MDatabaseReadonly::DeleteContact(MCONTACT)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::IsDbContact(MCONTACT contactID)
+{
+ return contactID == 1;
+}
+
+int MDatabaseReadonly::GetContactSize(void)
+{
+ return sizeof(DBCachedContact);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MEVENT MDatabaseReadonly::AddEvent(MCONTACT, const DBEVENTINFO*)
+{
+ return 0;
+}
+
+BOOL MDatabaseReadonly::DeleteEvent(MEVENT)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::EditEvent(MCONTACT, MEVENT, const DBEVENTINFO*)
+{
+ return 1;
+}
+
+int MDatabaseReadonly::GetBlobSize(MEVENT)
+{
+ return 0;
+}
+
+MEVENT MDatabaseReadonly::FindFirstUnreadEvent(MCONTACT)
+{
+ return 0;
+}
+
+BOOL MDatabaseReadonly::MarkEventRead(MCONTACT, MEVENT)
+{
+ return 1;
+}
+
+MCONTACT MDatabaseReadonly::GetEventContact(MEVENT)
+{
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+BOOL MDatabaseReadonly::GetContactSettingWorker(MCONTACT, LPCSTR, LPCSTR, DBVARIANT*, int)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::WriteContactSettingWorker(MCONTACT, DBCONTACTWRITESETTING&)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::DeleteContactSetting(MCONTACT, LPCSTR, LPCSTR)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::EnumContactSettings(MCONTACT, DBSETTINGENUMPROC, const char*, void*)
+{
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+BOOL MDatabaseReadonly::MetaMergeHistory(DBCachedContact*, DBCachedContact*)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::MetaSplitHistory(DBCachedContact*, DBCachedContact*)
+{
+ return 1;
+}
+
+BOOL MDatabaseReadonly::MetaRemoveSubHistory(DBCachedContact*)
+{
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MEVENT MDatabaseReadonly::GetEventById(LPCSTR, LPCSTR)
+{
+ return 0;
+}
diff --git a/src/mir_app/src/MHttpRequest.cpp b/src/mir_app/src/MHttpRequest.cpp index cefe4d551c..7935974463 100644 --- a/src/mir_app/src/MHttpRequest.cpp +++ b/src/mir_app/src/MHttpRequest.cpp @@ -1,93 +1,93 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -MHttpRequest::MHttpRequest() -{ - cbSize = sizeof(NETLIBHTTPREQUEST); - requestType = REQUEST_GET; -} - -MHttpRequest::~MHttpRequest() -{ - for (int i = 0; i < headersCount; i++) { - mir_free(headers[i].szName); - mir_free(headers[i].szValue); - } - mir_free(headers); - mir_free(pData); -} - -void MHttpRequest::AddHeader(LPCSTR szName, LPCSTR szValue) -{ - for (int i = 0; i < headersCount; i++) - if (!mir_strcmp(headers[i].szName, szName)) { - replaceStr(headers[i].szValue, szValue); - return; - } - - headers = (NETLIBHTTPHEADER*)mir_realloc(headers, sizeof(NETLIBHTTPHEADER)*(headersCount + 1)); - headers[headersCount].szName = mir_strdup(szName); - headers[headersCount].szValue = mir_strdup(szValue); - headersCount++; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT_PARAM ¶m) -{ - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%ld", param.szName, param.iValue); - return pReq; -} - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT64_PARAM ¶m) -{ - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%lld", param.szName, param.iValue); - return pReq; -} - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const CHAR_PARAM ¶m) -{ - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%s", param.szName, mir_urlEncode(param.szValue).c_str()); - return pReq; -} - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const WCHAR_PARAM ¶m) -{ - T2Utf szValue(param.wszValue); - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%s", param.szName, mir_urlEncode(szValue).c_str()); - return pReq; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+MHttpRequest::MHttpRequest()
+{
+ cbSize = sizeof(NETLIBHTTPREQUEST);
+ requestType = REQUEST_GET;
+}
+
+MHttpRequest::~MHttpRequest()
+{
+ for (int i = 0; i < headersCount; i++) {
+ mir_free(headers[i].szName);
+ mir_free(headers[i].szValue);
+ }
+ mir_free(headers);
+ mir_free(pData);
+}
+
+void MHttpRequest::AddHeader(LPCSTR szName, LPCSTR szValue)
+{
+ for (int i = 0; i < headersCount; i++)
+ if (!mir_strcmp(headers[i].szName, szName)) {
+ replaceStr(headers[i].szValue, szValue);
+ return;
+ }
+
+ headers = (NETLIBHTTPHEADER*)mir_realloc(headers, sizeof(NETLIBHTTPHEADER)*(headersCount + 1));
+ headers[headersCount].szName = mir_strdup(szName);
+ headers[headersCount].szValue = mir_strdup(szValue);
+ headersCount++;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT_PARAM ¶m)
+{
+ CMStringA &s = pReq->m_szParam;
+ if (!s.IsEmpty())
+ s.AppendChar('&');
+ s.AppendFormat("%s=%ld", param.szName, param.iValue);
+ return pReq;
+}
+
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT64_PARAM ¶m)
+{
+ CMStringA &s = pReq->m_szParam;
+ if (!s.IsEmpty())
+ s.AppendChar('&');
+ s.AppendFormat("%s=%lld", param.szName, param.iValue);
+ return pReq;
+}
+
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const CHAR_PARAM ¶m)
+{
+ CMStringA &s = pReq->m_szParam;
+ if (!s.IsEmpty())
+ s.AppendChar('&');
+ s.AppendFormat("%s=%s", param.szName, mir_urlEncode(param.szValue).c_str());
+ return pReq;
+}
+
+MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const WCHAR_PARAM ¶m)
+{
+ T2Utf szValue(param.wszValue);
+ CMStringA &s = pReq->m_szParam;
+ if (!s.IsEmpty())
+ s.AppendChar('&');
+ s.AppendFormat("%s=%s", param.szName, mir_urlEncode(szValue).c_str());
+ return pReq;
+}
diff --git a/src/mir_app/src/addcontact.cpp b/src/mir_app/src/addcontact.cpp index e065bf94df..2eda1400ca 100644 --- a/src/mir_app/src/addcontact.cpp +++ b/src/mir_app/src/addcontact.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/auth.cpp b/src/mir_app/src/auth.cpp index c40053819f..0520c20a11 100644 --- a/src/mir_app/src/auth.cpp +++ b/src/mir_app/src/auth.cpp @@ -1,367 +1,367 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// Auth Request dialog - -class CAuthReqDlg : public CDlgBase -{ - MEVENT m_hDbEvent; - MCONTACT m_hContact; - const char *m_szProto; - - CCtrlBase fldHeader, fldReason; - CCtrlEdit edtReason; - CCtrlCheck chkAdd; - CCtrlButton btnAdd, btnDetails, btnLater; - -public: - CAuthReqDlg(MEVENT hEvent) : - CDlgBase(g_plugin, IDD_AUTHREQ), - m_hDbEvent(hEvent), - fldHeader(this, IDC_HEADERBAR), - fldReason(this, IDC_REASON), - edtReason(this, IDC_DENYREASON), - btnAdd(this, IDC_ADD), - btnLater(this, IDC_DECIDELATER), - btnDetails(this, IDC_DETAILS), - chkAdd(this, IDC_ADDCHECK) - { - btnLater.OnClick = Callback(this, &CAuthReqDlg::onClick_Later); - btnDetails.OnClick = Callback(this, &CAuthReqDlg::onClick_Details); - } - - bool OnInitDialog() override - { - Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details")); - Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list")); - - // blob is: uin(uint32_t), hcontact(uint32_t), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ) - DBEVENTINFO dbei = {}; - dbei.cbBlob = -1; - if (db_event_get(m_hDbEvent, &dbei)) - return false; - - m_szProto = dbei.szModule; - PROTOACCOUNT *acc = Proto_GetAccount(dbei.szModule); - - uint32_t uin = *(uint32_t*)dbei.pBlob; - m_hContact = DbGetAuthEventContact(&dbei); - char *nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2; - char *first = nick + mir_strlen(nick) + 1; - char *last = first + mir_strlen(first) + 1; - char *email = last + mir_strlen(last) + 1; - char *reason = email + mir_strlen(email) + 1; - - #ifdef _WINDOWS - SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0)); - SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0)); - #endif - - ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last)); - ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first)); - ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick)); - ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email)); - ptrW reasonT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(reason) : mir_a2u(reason)); - - CMStringW wszHeader; - if (firstT[0] && lastT[0]) - wszHeader.Format(L"%s %s", (wchar_t*)firstT, (wchar_t*)lastT); - else if (firstT[0]) - wszHeader = firstT.get(); - else if (lastT[0]) - wszHeader = lastT.get(); - - if (mir_wstrlen(nickT)) { - if (wszHeader.IsEmpty()) - wszHeader = nickT.get(); - else - wszHeader.AppendFormat(L" %s", nickT.get()); - } - if (wszHeader.IsEmpty()) - wszHeader = TranslateT("<Unknown>"); - - if (uin && emailT[0]) - wszHeader.AppendFormat(TranslateT(" requested authorization\n%u (%s) on %s"), uin, emailT.get(), acc->tszAccountName); - else if (uin) - wszHeader.AppendFormat(TranslateT(" requested authorization\n%u on %s"), uin, acc->tszAccountName); - else - wszHeader.AppendFormat(TranslateT(" requested authorization\n%s on %s"), emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName); - fldHeader.SetText(wszHeader); - - fldReason.SetText(reasonT); - - if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) - btnAdd.Hide(); - - edtReason.SetMaxLength(255); - if (CallProtoService(dbei.szModule, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_NOAUTHDENYREASON) { - edtReason.Disable(); - edtReason.SetText(TranslateT("Feature is not supported by protocol")); - } - - if (Contact::OnList(m_hContact)) { - chkAdd.Disable(); - chkAdd.SetState(false); - } - else chkAdd.SetState(true); - return true; - } - - bool OnApply() override - { - CallProtoService(m_szProto, PS_AUTHALLOW, m_hDbEvent, 0); - - if (chkAdd.GetState()) - Contact::AddByEvent(m_hDbEvent, m_hwnd); - return true; - } - - void OnDestroy() override - { - if (!m_bSucceeded) { - if (edtReason.Enabled()) - CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, (LPARAM)ptrW(edtReason.GetText())); - else - CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, 0); - } - - Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD); - Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS); - - #ifdef _WINDOWS - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); - #endif - } - - void onClick_Later(CCtrlButton*) - { - m_bSucceeded = true; - Close(); - } - - void onClick_Details(CCtrlButton*) - { - CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0); - } -}; - -static INT_PTR ShowReqWindow(WPARAM, LPARAM lParam) -{ - (new CAuthReqDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show(); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CAddedDlg : public CDlgBase -{ - MEVENT m_hDbEvent; - MCONTACT m_hContact; - - CCtrlBase fldHeader; - CCtrlButton btnDetails, btnAdd; - -public: - CAddedDlg(MEVENT hEvent) : - CDlgBase(g_plugin, IDD_ADDED), - m_hDbEvent(hEvent), - btnAdd(this, IDC_ADD), - btnDetails(this, IDC_DETAILS), - fldHeader(this, IDC_HEADERBAR) - { - btnAdd.OnClick = Callback(this, &CAddedDlg::onClick_Add); - btnDetails.OnClick = Callback(this, &CAddedDlg::onClick_Details); - } - - bool OnInitDialog() override - { - Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details")); - Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list")); - - // blob is: uin(uint32_t), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ) - DB::EventInfo dbei; - dbei.cbBlob = -1; - db_event_get(m_hDbEvent, &dbei); - - m_hContact = DbGetAuthEventContact(&dbei); - - uint32_t uin = *(uint32_t*)dbei.pBlob; - char* nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2; - char* first = nick + mir_strlen(nick) + 1; - char* last = first + mir_strlen(first) + 1; - char* email = last + mir_strlen(last) + 1; - - #ifdef _WINDOWS - SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0)); - SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0)); - #endif - - PROTOACCOUNT* acc = Proto_GetAccount(dbei.szModule); - - ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last)); - ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first)); - ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick)); - ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email)); - - wchar_t name[128] = L""; - int off = 0; - if (firstT[0] && lastT[0]) - off = mir_snwprintf(name, L"%s %s", firstT.get(), lastT.get()); - else if (firstT[0]) - off = mir_snwprintf(name, L"%s", firstT.get()); - else if (lastT[0]) - off = mir_snwprintf(name, L"%s", lastT.get()); - if (nickT[0]) { - if (off) - mir_snwprintf(name + off, _countof(name) - off, L" (%s)", nickT.get()); - else - wcsncpy_s(name, nickT, _TRUNCATE); - } - if (!name[0]) - wcsncpy_s(name, TranslateT("<Unknown>"), _TRUNCATE); - - wchar_t hdr[256]; - if (uin && emailT[0]) - mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u (%s) on %s"), name, uin, emailT.get(), acc->tszAccountName); - else if (uin) - mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u on %s"), name, uin, acc->tszAccountName); - else - mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%s on %s"), name, emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName); - fldHeader.SetText(hdr); - - if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) - btnAdd.Hide(); - return true; - } - - bool OnApply() - { - Contact::AddByEvent(m_hDbEvent, m_hwnd); - return true; - } - - void OnDestroy() override - { - Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD); - Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS); - - #ifdef _WINDOWS - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); - #endif - } - - void onClick_Add(CCtrlButton*) - { - Contact::AddByEvent(m_hDbEvent, m_hwnd); - - if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) - btnAdd.Hide(); - } - - void onClick_Details(CCtrlButton*) - { - CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0); - } -}; - -static INT_PTR ShowAddedWindow(WPARAM, LPARAM lParam) -{ - (new CAddedDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show(); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -#define MS_AUTH_SHOWADDED "Auth/ShowAdded" -#define MS_AUTH_SHOWREQUEST "Auth/ShowRequest" - -static int AuthEventAdded(WPARAM, LPARAM lParam) -{ - wchar_t szTooltip[256]; - MEVENT hDbEvent = (MEVENT)lParam; - - DB::EventInfo dbei; - db_event_get(lParam, &dbei); - if (dbei.flags & (DBEF_SENT | DBEF_READ) || (dbei.eventType != EVENTTYPE_AUTHREQUEST && dbei.eventType != EVENTTYPE_ADDED)) - return 0; - - dbei.cbBlob = -1; - db_event_get(hDbEvent, &dbei); - - MCONTACT hContact = DbGetAuthEventContact(&dbei); - - CLISTEVENT cle = {}; - cle.hContact = hContact; - cle.szTooltip.w = szTooltip; - cle.flags = CLEF_UNICODE; - cle.lParam = lParam; - cle.hDbEvent = hDbEvent; - - ptrW szUid(Contact::GetInfo(CNF_UNIQUEID, hContact)); - - if (dbei.eventType == EVENTTYPE_AUTHREQUEST) { - Skin_PlaySound("AuthRequest"); - if (szUid) - mir_snwprintf(szTooltip, TranslateT("%s requests authorization"), szUid.get()); - else - mir_snwprintf(szTooltip, TranslateT("%u requests authorization"), *(uint32_t*)dbei.pBlob); - - cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_REQUEST); - cle.pszService = MS_AUTH_SHOWREQUEST; - g_clistApi.pfnAddEvent(&cle); - } - else if (dbei.eventType == EVENTTYPE_ADDED) { - Skin_PlaySound("AddedEvent"); - if (szUid) - mir_snwprintf(szTooltip, TranslateT("%s added you to their contact list"), szUid.get()); - else - mir_snwprintf(szTooltip, TranslateT("%u added you to their contact list"), *(uint32_t*)dbei.pBlob); - - cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_ADD); - cle.pszService = MS_AUTH_SHOWADDED; - g_clistApi.pfnAddEvent(&cle); - } - return 0; -} - -static void CALLBACK LaunchAuth() -{ - HookEvent(ME_DB_EVENT_ADDED, AuthEventAdded); -} - -int LoadSendRecvAuthModule(void) -{ - CreateServiceFunction(MS_AUTH_SHOWREQUEST, ShowReqWindow); - CreateServiceFunction(MS_AUTH_SHOWADDED, ShowAddedWindow); - Miranda_WaitOnHandle(LaunchAuth); - - g_plugin.addSound("AuthRequest", LPGENW("Alerts"), LPGENW("Authorization request")); - g_plugin.addSound("AddedEvent", LPGENW("Alerts"), LPGENW("Added event")); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Auth Request dialog
+
+class CAuthReqDlg : public CDlgBase
+{
+ MEVENT m_hDbEvent;
+ MCONTACT m_hContact;
+ const char *m_szProto;
+
+ CCtrlBase fldHeader, fldReason;
+ CCtrlEdit edtReason;
+ CCtrlCheck chkAdd;
+ CCtrlButton btnAdd, btnDetails, btnLater;
+
+public:
+ CAuthReqDlg(MEVENT hEvent) :
+ CDlgBase(g_plugin, IDD_AUTHREQ),
+ m_hDbEvent(hEvent),
+ fldHeader(this, IDC_HEADERBAR),
+ fldReason(this, IDC_REASON),
+ edtReason(this, IDC_DENYREASON),
+ btnAdd(this, IDC_ADD),
+ btnLater(this, IDC_DECIDELATER),
+ btnDetails(this, IDC_DETAILS),
+ chkAdd(this, IDC_ADDCHECK)
+ {
+ btnLater.OnClick = Callback(this, &CAuthReqDlg::onClick_Later);
+ btnDetails.OnClick = Callback(this, &CAuthReqDlg::onClick_Details);
+ }
+
+ bool OnInitDialog() override
+ {
+ Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details"));
+ Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list"));
+
+ // blob is: uin(uint32_t), hcontact(uint32_t), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ)
+ DBEVENTINFO dbei = {};
+ dbei.cbBlob = -1;
+ if (db_event_get(m_hDbEvent, &dbei))
+ return false;
+
+ m_szProto = dbei.szModule;
+ PROTOACCOUNT *acc = Proto_GetAccount(dbei.szModule);
+
+ uint32_t uin = *(uint32_t*)dbei.pBlob;
+ m_hContact = DbGetAuthEventContact(&dbei);
+ char *nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2;
+ char *first = nick + mir_strlen(nick) + 1;
+ char *last = first + mir_strlen(first) + 1;
+ char *email = last + mir_strlen(last) + 1;
+ char *reason = email + mir_strlen(email) + 1;
+
+ #ifdef _WINDOWS
+ SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0));
+ SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0));
+ #endif
+
+ ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last));
+ ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first));
+ ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick));
+ ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email));
+ ptrW reasonT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(reason) : mir_a2u(reason));
+
+ CMStringW wszHeader;
+ if (firstT[0] && lastT[0])
+ wszHeader.Format(L"%s %s", (wchar_t*)firstT, (wchar_t*)lastT);
+ else if (firstT[0])
+ wszHeader = firstT.get();
+ else if (lastT[0])
+ wszHeader = lastT.get();
+
+ if (mir_wstrlen(nickT)) {
+ if (wszHeader.IsEmpty())
+ wszHeader = nickT.get();
+ else
+ wszHeader.AppendFormat(L" %s", nickT.get());
+ }
+ if (wszHeader.IsEmpty())
+ wszHeader = TranslateT("<Unknown>");
+
+ if (uin && emailT[0])
+ wszHeader.AppendFormat(TranslateT(" requested authorization\n%u (%s) on %s"), uin, emailT.get(), acc->tszAccountName);
+ else if (uin)
+ wszHeader.AppendFormat(TranslateT(" requested authorization\n%u on %s"), uin, acc->tszAccountName);
+ else
+ wszHeader.AppendFormat(TranslateT(" requested authorization\n%s on %s"), emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName);
+ fldHeader.SetText(wszHeader);
+
+ fldReason.SetText(reasonT);
+
+ if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact))
+ btnAdd.Hide();
+
+ edtReason.SetMaxLength(255);
+ if (CallProtoService(dbei.szModule, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_NOAUTHDENYREASON) {
+ edtReason.Disable();
+ edtReason.SetText(TranslateT("Feature is not supported by protocol"));
+ }
+
+ if (Contact::OnList(m_hContact)) {
+ chkAdd.Disable();
+ chkAdd.SetState(false);
+ }
+ else chkAdd.SetState(true);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ CallProtoService(m_szProto, PS_AUTHALLOW, m_hDbEvent, 0);
+
+ if (chkAdd.GetState())
+ Contact::AddByEvent(m_hDbEvent, m_hwnd);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ if (!m_bSucceeded) {
+ if (edtReason.Enabled())
+ CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, (LPARAM)ptrW(edtReason.GetText()));
+ else
+ CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, 0);
+ }
+
+ Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD);
+ Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS);
+
+ #ifdef _WINDOWS
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0));
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0));
+ #endif
+ }
+
+ void onClick_Later(CCtrlButton*)
+ {
+ m_bSucceeded = true;
+ Close();
+ }
+
+ void onClick_Details(CCtrlButton*)
+ {
+ CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0);
+ }
+};
+
+static INT_PTR ShowReqWindow(WPARAM, LPARAM lParam)
+{
+ (new CAuthReqDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CAddedDlg : public CDlgBase
+{
+ MEVENT m_hDbEvent;
+ MCONTACT m_hContact;
+
+ CCtrlBase fldHeader;
+ CCtrlButton btnDetails, btnAdd;
+
+public:
+ CAddedDlg(MEVENT hEvent) :
+ CDlgBase(g_plugin, IDD_ADDED),
+ m_hDbEvent(hEvent),
+ btnAdd(this, IDC_ADD),
+ btnDetails(this, IDC_DETAILS),
+ fldHeader(this, IDC_HEADERBAR)
+ {
+ btnAdd.OnClick = Callback(this, &CAddedDlg::onClick_Add);
+ btnDetails.OnClick = Callback(this, &CAddedDlg::onClick_Details);
+ }
+
+ bool OnInitDialog() override
+ {
+ Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details"));
+ Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list"));
+
+ // blob is: uin(uint32_t), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ)
+ DB::EventInfo dbei;
+ dbei.cbBlob = -1;
+ db_event_get(m_hDbEvent, &dbei);
+
+ m_hContact = DbGetAuthEventContact(&dbei);
+
+ uint32_t uin = *(uint32_t*)dbei.pBlob;
+ char* nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2;
+ char* first = nick + mir_strlen(nick) + 1;
+ char* last = first + mir_strlen(first) + 1;
+ char* email = last + mir_strlen(last) + 1;
+
+ #ifdef _WINDOWS
+ SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0));
+ SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0));
+ #endif
+
+ PROTOACCOUNT* acc = Proto_GetAccount(dbei.szModule);
+
+ ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last));
+ ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first));
+ ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick));
+ ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email));
+
+ wchar_t name[128] = L"";
+ int off = 0;
+ if (firstT[0] && lastT[0])
+ off = mir_snwprintf(name, L"%s %s", firstT.get(), lastT.get());
+ else if (firstT[0])
+ off = mir_snwprintf(name, L"%s", firstT.get());
+ else if (lastT[0])
+ off = mir_snwprintf(name, L"%s", lastT.get());
+ if (nickT[0]) {
+ if (off)
+ mir_snwprintf(name + off, _countof(name) - off, L" (%s)", nickT.get());
+ else
+ wcsncpy_s(name, nickT, _TRUNCATE);
+ }
+ if (!name[0])
+ wcsncpy_s(name, TranslateT("<Unknown>"), _TRUNCATE);
+
+ wchar_t hdr[256];
+ if (uin && emailT[0])
+ mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u (%s) on %s"), name, uin, emailT.get(), acc->tszAccountName);
+ else if (uin)
+ mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u on %s"), name, uin, acc->tszAccountName);
+ else
+ mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%s on %s"), name, emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName);
+ fldHeader.SetText(hdr);
+
+ if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact))
+ btnAdd.Hide();
+ return true;
+ }
+
+ bool OnApply()
+ {
+ Contact::AddByEvent(m_hDbEvent, m_hwnd);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD);
+ Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS);
+
+ #ifdef _WINDOWS
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0));
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0));
+ #endif
+ }
+
+ void onClick_Add(CCtrlButton*)
+ {
+ Contact::AddByEvent(m_hDbEvent, m_hwnd);
+
+ if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact))
+ btnAdd.Hide();
+ }
+
+ void onClick_Details(CCtrlButton*)
+ {
+ CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0);
+ }
+};
+
+static INT_PTR ShowAddedWindow(WPARAM, LPARAM lParam)
+{
+ (new CAddedDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define MS_AUTH_SHOWADDED "Auth/ShowAdded"
+#define MS_AUTH_SHOWREQUEST "Auth/ShowRequest"
+
+static int AuthEventAdded(WPARAM, LPARAM lParam)
+{
+ wchar_t szTooltip[256];
+ MEVENT hDbEvent = (MEVENT)lParam;
+
+ DB::EventInfo dbei;
+ db_event_get(lParam, &dbei);
+ if (dbei.flags & (DBEF_SENT | DBEF_READ) || (dbei.eventType != EVENTTYPE_AUTHREQUEST && dbei.eventType != EVENTTYPE_ADDED))
+ return 0;
+
+ dbei.cbBlob = -1;
+ db_event_get(hDbEvent, &dbei);
+
+ MCONTACT hContact = DbGetAuthEventContact(&dbei);
+
+ CLISTEVENT cle = {};
+ cle.hContact = hContact;
+ cle.szTooltip.w = szTooltip;
+ cle.flags = CLEF_UNICODE;
+ cle.lParam = lParam;
+ cle.hDbEvent = hDbEvent;
+
+ ptrW szUid(Contact::GetInfo(CNF_UNIQUEID, hContact));
+
+ if (dbei.eventType == EVENTTYPE_AUTHREQUEST) {
+ Skin_PlaySound("AuthRequest");
+ if (szUid)
+ mir_snwprintf(szTooltip, TranslateT("%s requests authorization"), szUid.get());
+ else
+ mir_snwprintf(szTooltip, TranslateT("%u requests authorization"), *(uint32_t*)dbei.pBlob);
+
+ cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_REQUEST);
+ cle.pszService = MS_AUTH_SHOWREQUEST;
+ g_clistApi.pfnAddEvent(&cle);
+ }
+ else if (dbei.eventType == EVENTTYPE_ADDED) {
+ Skin_PlaySound("AddedEvent");
+ if (szUid)
+ mir_snwprintf(szTooltip, TranslateT("%s added you to their contact list"), szUid.get());
+ else
+ mir_snwprintf(szTooltip, TranslateT("%u added you to their contact list"), *(uint32_t*)dbei.pBlob);
+
+ cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_ADD);
+ cle.pszService = MS_AUTH_SHOWADDED;
+ g_clistApi.pfnAddEvent(&cle);
+ }
+ return 0;
+}
+
+static void CALLBACK LaunchAuth()
+{
+ HookEvent(ME_DB_EVENT_ADDED, AuthEventAdded);
+}
+
+int LoadSendRecvAuthModule(void)
+{
+ CreateServiceFunction(MS_AUTH_SHOWREQUEST, ShowReqWindow);
+ CreateServiceFunction(MS_AUTH_SHOWADDED, ShowAddedWindow);
+ Miranda_WaitOnHandle(LaunchAuth);
+
+ g_plugin.addSound("AuthRequest", LPGENW("Alerts"), LPGENW("Authorization request"));
+ g_plugin.addSound("AddedEvent", LPGENW("Alerts"), LPGENW("Added event"));
+ return 0;
+}
diff --git a/src/mir_app/src/button.cpp b/src/mir_app/src/button.cpp index 4a9b548c83..b97685716e 100644 --- a/src/mir_app/src/button.cpp +++ b/src/mir_app/src/button.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/chat_manager.cpp b/src/mir_app/src/chat_manager.cpp index 3e0f2b8b5d..7019e3a2a7 100644 --- a/src/mir_app/src/chat_manager.cpp +++ b/src/mir_app/src/chat_manager.cpp @@ -1,7 +1,7 @@ /*
Chat module plugin for Miranda IM
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/chat_rtf.cpp b/src/mir_app/src/chat_rtf.cpp index 82545dc363..508fc1a86d 100644 --- a/src/mir_app/src/chat_rtf.cpp +++ b/src/mir_app/src/chat_rtf.cpp @@ -1,204 +1,204 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "chat.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// convert rich edit code to bbcode (if wanted). Otherwise, strip all RTF formatting -// tags and return plain text - -static wchar_t tszRtfBreaks[] = L" \\\n\r"; - -static void CreateColorMap(CMStringW &Text, int iCount, COLORREF *pSrc, int *pDst) -{ - const wchar_t *pszText = Text; - int iIndex = 1; - - static const wchar_t *lpszFmt = L"\\red%[^ \x5b\\]\\green%[^ \x5b\\]\\blue%[^ \x5b;];"; - wchar_t szRed[10], szGreen[10], szBlue[10]; - - const wchar_t *p1 = wcsstr(pszText, L"\\colortbl"); - if (!p1) - return; - - const wchar_t *pEnd = wcschr(p1, '}'); - - const wchar_t *p2 = wcsstr(p1, L"\\red"); - - for (int i = 0; i < iCount; i++) - pDst[i] = -1; - - while (p2 && p2 < pEnd) { - if (swscanf(p2, lpszFmt, &szRed, &szGreen, &szBlue) > 0) { - for (int i = 0; i < iCount; i++) { - if (pSrc[i] == RGB(_wtoi(szRed), _wtoi(szGreen), _wtoi(szBlue))) - pDst[i] = iIndex; - } - } - iIndex++; - p1 = p2; - p1++; - - p2 = wcsstr(p1, L"\\red"); - } -} - -static int GetRtfIndex(int iCol, int iCount, int *pIndex) -{ - for (int i = 0; i < iCount; i++) - if (pIndex[i] == iCol) - return i; - - return -1; -} - -int DoRtfToTags(CMStringW &pszText, int iNumColors, COLORREF *pColors) -{ - if (pszText.IsEmpty()) - return FALSE; - - // create an index of colors in the module and map them to - // corresponding colors in the RTF color table - int *pIndex = (int*)_alloca(iNumColors * sizeof(int)); - CreateColorMap(pszText, iNumColors, pColors, pIndex); - - // scan the file for rtf commands and remove or parse them - int idx = pszText.Find(L"\\pard"); - if (idx == -1) { - if ((idx = pszText.Find(L"\\ltrpar")) == -1) - return FALSE; - idx += 7; - } - else idx += 5; - - bool bInsideColor = false, bInsideUl = false; - CMStringW res; - - // iterate through all characters, if rtf control character found then take action - for (const wchar_t *p = pszText.GetString() + idx; *p;) { - switch (*p) { - case '\\': - if (p[1] == '\\' || p[1] == '{' || p[1] == '}') { // escaped characters - res.AppendChar(p[1]); - p += 2; break; - } - if (p[1] == '~') { // non-breaking space - res.AppendChar(0xA0); - p += 2; break; - } - - if (!wcsncmp(p, L"\\cf", 3)) { // foreground color - int iCol = _wtoi(p + 3); - int iInd = GetRtfIndex(iCol, iNumColors, pIndex); - bInsideColor = iInd > 0; - } - else if (!wcsncmp(p, L"\\highlight", 10)) { //background color - wchar_t szTemp[20]; - int iCol = _wtoi(p + 10); - mir_snwprintf(szTemp, L"%d", iCol); - } - else if (!wcsncmp(p, L"\\line", 5)) { // soft line break; - res.AppendChar('\n'); - } - else if (!wcsncmp(p, L"\\endash", 7)) { - res.AppendChar(0x2013); - } - else if (!wcsncmp(p, L"\\emdash", 7)) { - res.AppendChar(0x2014); - } - else if (!wcsncmp(p, L"\\bullet", 7)) { - res.AppendChar(0x2022); - } - else if (!wcsncmp(p, L"\\ldblquote", 10)) { - res.AppendChar(0x201C); - } - else if (!wcsncmp(p, L"\\rdblquote", 10)) { - res.AppendChar(0x201D); - } - else if (!wcsncmp(p, L"\\lquote", 7)) { - res.AppendChar(0x2018); - } - else if (!wcsncmp(p, L"\\rquote", 7)) { - res.AppendChar(0x2019); - } - else if (!wcsncmp(p, L"\\b", 2)) { //bold - res.Append((p[2] != '0') ? L"[b]" : L"[/b]"); - } - else if (!wcsncmp(p, L"\\i", 2)) { // italics - res.Append((p[2] != '0') ? L"[i]" : L"[/i]"); - } - else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out - res.Append((p[7] != '0') ? L"[s]" : L"[/s]"); - } - else if (!wcsncmp(p, L"\\ul", 3)) { // underlined - if (p[3] == 0 || wcschr(tszRtfBreaks, p[3])) { - res.Append(L"[u]"); - bInsideUl = true; - } - else if (!wcsncmp(p + 3, L"none", 4)) { - if (bInsideUl) - res.Append(L"[/u]"); - bInsideUl = false; - } - } - else if (!wcsncmp(p, L"\\tab", 4)) { // tab - res.AppendChar('\t'); - } - else if (p[1] == '\'') { // special character - if (p[2] != ' ' && p[2] != '\\') { - wchar_t tmp[10], *t = tmp; - *t++ = p[2]; - if (p[3] != ' ' && p[3] != '\\') - *t++ = p[3]; - *t = 0; - - // convert string containing char in hex format to int. - wchar_t *stoppedHere; - res.AppendChar(wcstol(tmp, &stoppedHere, 16)); - } - } - - p++; // skip initial slash - p += wcscspn(p, tszRtfBreaks); - if (*p == ' ') - p++; - break; - - case '{': // other RTF control characters - case '}': - p++; - break; - - default: // other text that should not be touched - res.AppendChar(*p++); - break; - } - } - - if (bInsideUl) - res.Append(L"[/u]"); - - pszText = res; - return TRUE; -} +/*
+Chat module plugin for Miranda IM
+
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "chat.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// convert rich edit code to bbcode (if wanted). Otherwise, strip all RTF formatting
+// tags and return plain text
+
+static wchar_t tszRtfBreaks[] = L" \\\n\r";
+
+static void CreateColorMap(CMStringW &Text, int iCount, COLORREF *pSrc, int *pDst)
+{
+ const wchar_t *pszText = Text;
+ int iIndex = 1;
+
+ static const wchar_t *lpszFmt = L"\\red%[^ \x5b\\]\\green%[^ \x5b\\]\\blue%[^ \x5b;];";
+ wchar_t szRed[10], szGreen[10], szBlue[10];
+
+ const wchar_t *p1 = wcsstr(pszText, L"\\colortbl");
+ if (!p1)
+ return;
+
+ const wchar_t *pEnd = wcschr(p1, '}');
+
+ const wchar_t *p2 = wcsstr(p1, L"\\red");
+
+ for (int i = 0; i < iCount; i++)
+ pDst[i] = -1;
+
+ while (p2 && p2 < pEnd) {
+ if (swscanf(p2, lpszFmt, &szRed, &szGreen, &szBlue) > 0) {
+ for (int i = 0; i < iCount; i++) {
+ if (pSrc[i] == RGB(_wtoi(szRed), _wtoi(szGreen), _wtoi(szBlue)))
+ pDst[i] = iIndex;
+ }
+ }
+ iIndex++;
+ p1 = p2;
+ p1++;
+
+ p2 = wcsstr(p1, L"\\red");
+ }
+}
+
+static int GetRtfIndex(int iCol, int iCount, int *pIndex)
+{
+ for (int i = 0; i < iCount; i++)
+ if (pIndex[i] == iCol)
+ return i;
+
+ return -1;
+}
+
+int DoRtfToTags(CMStringW &pszText, int iNumColors, COLORREF *pColors)
+{
+ if (pszText.IsEmpty())
+ return FALSE;
+
+ // create an index of colors in the module and map them to
+ // corresponding colors in the RTF color table
+ int *pIndex = (int*)_alloca(iNumColors * sizeof(int));
+ CreateColorMap(pszText, iNumColors, pColors, pIndex);
+
+ // scan the file for rtf commands and remove or parse them
+ int idx = pszText.Find(L"\\pard");
+ if (idx == -1) {
+ if ((idx = pszText.Find(L"\\ltrpar")) == -1)
+ return FALSE;
+ idx += 7;
+ }
+ else idx += 5;
+
+ bool bInsideColor = false, bInsideUl = false;
+ CMStringW res;
+
+ // iterate through all characters, if rtf control character found then take action
+ for (const wchar_t *p = pszText.GetString() + idx; *p;) {
+ switch (*p) {
+ case '\\':
+ if (p[1] == '\\' || p[1] == '{' || p[1] == '}') { // escaped characters
+ res.AppendChar(p[1]);
+ p += 2; break;
+ }
+ if (p[1] == '~') { // non-breaking space
+ res.AppendChar(0xA0);
+ p += 2; break;
+ }
+
+ if (!wcsncmp(p, L"\\cf", 3)) { // foreground color
+ int iCol = _wtoi(p + 3);
+ int iInd = GetRtfIndex(iCol, iNumColors, pIndex);
+ bInsideColor = iInd > 0;
+ }
+ else if (!wcsncmp(p, L"\\highlight", 10)) { //background color
+ wchar_t szTemp[20];
+ int iCol = _wtoi(p + 10);
+ mir_snwprintf(szTemp, L"%d", iCol);
+ }
+ else if (!wcsncmp(p, L"\\line", 5)) { // soft line break;
+ res.AppendChar('\n');
+ }
+ else if (!wcsncmp(p, L"\\endash", 7)) {
+ res.AppendChar(0x2013);
+ }
+ else if (!wcsncmp(p, L"\\emdash", 7)) {
+ res.AppendChar(0x2014);
+ }
+ else if (!wcsncmp(p, L"\\bullet", 7)) {
+ res.AppendChar(0x2022);
+ }
+ else if (!wcsncmp(p, L"\\ldblquote", 10)) {
+ res.AppendChar(0x201C);
+ }
+ else if (!wcsncmp(p, L"\\rdblquote", 10)) {
+ res.AppendChar(0x201D);
+ }
+ else if (!wcsncmp(p, L"\\lquote", 7)) {
+ res.AppendChar(0x2018);
+ }
+ else if (!wcsncmp(p, L"\\rquote", 7)) {
+ res.AppendChar(0x2019);
+ }
+ else if (!wcsncmp(p, L"\\b", 2)) { //bold
+ res.Append((p[2] != '0') ? L"[b]" : L"[/b]");
+ }
+ else if (!wcsncmp(p, L"\\i", 2)) { // italics
+ res.Append((p[2] != '0') ? L"[i]" : L"[/i]");
+ }
+ else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out
+ res.Append((p[7] != '0') ? L"[s]" : L"[/s]");
+ }
+ else if (!wcsncmp(p, L"\\ul", 3)) { // underlined
+ if (p[3] == 0 || wcschr(tszRtfBreaks, p[3])) {
+ res.Append(L"[u]");
+ bInsideUl = true;
+ }
+ else if (!wcsncmp(p + 3, L"none", 4)) {
+ if (bInsideUl)
+ res.Append(L"[/u]");
+ bInsideUl = false;
+ }
+ }
+ else if (!wcsncmp(p, L"\\tab", 4)) { // tab
+ res.AppendChar('\t');
+ }
+ else if (p[1] == '\'') { // special character
+ if (p[2] != ' ' && p[2] != '\\') {
+ wchar_t tmp[10], *t = tmp;
+ *t++ = p[2];
+ if (p[3] != ' ' && p[3] != '\\')
+ *t++ = p[3];
+ *t = 0;
+
+ // convert string containing char in hex format to int.
+ wchar_t *stoppedHere;
+ res.AppendChar(wcstol(tmp, &stoppedHere, 16));
+ }
+ }
+
+ p++; // skip initial slash
+ p += wcscspn(p, tszRtfBreaks);
+ if (*p == ' ')
+ p++;
+ break;
+
+ case '{': // other RTF control characters
+ case '}':
+ p++;
+ break;
+
+ default: // other text that should not be touched
+ res.AppendChar(*p++);
+ break;
+ }
+ }
+
+ if (bInsideUl)
+ res.Append(L"[/u]");
+
+ pszText = res;
+ return TRUE;
+}
diff --git a/src/mir_app/src/chat_svc.cpp b/src/mir_app/src/chat_svc.cpp index 0043df7ae6..021079ba46 100644 --- a/src/mir_app/src/chat_svc.cpp +++ b/src/mir_app/src/chat_svc.cpp @@ -1,7 +1,7 @@ /*
Chat module plugin for Miranda IM
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/chat_tools.cpp b/src/mir_app/src/chat_tools.cpp index 4c2bcc7698..cb962183de 100644 --- a/src/mir_app/src/chat_tools.cpp +++ b/src/mir_app/src/chat_tools.cpp @@ -1,7 +1,7 @@ /*
Chat module plugin for Miranda IM
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/chat_ui.cpp b/src/mir_app/src/chat_ui.cpp index 789f6fd075..4c5c21b6cd 100644 --- a/src/mir_app/src/chat_ui.cpp +++ b/src/mir_app/src/chat_ui.cpp @@ -1,220 +1,220 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "chat.h" - -CMOption<bool> g_bChatPopupInactive(CHAT_MODULE, "PopupInactiveOnly", true); -CMOption<bool> g_bChatTrayInactive(CHAT_MODULE, "TrayIconInactiveOnly", true); - -///////////////////////////////////////////////////////////////////////////////////////// -// Group chat - Events - -#define NR_GC_EVENTS 12 - -static UINT _eventorder[] = -{ - GC_EVENT_ACTION, - GC_EVENT_MESSAGE, - GC_EVENT_NICK, - GC_EVENT_JOIN, - GC_EVENT_PART, - GC_EVENT_TOPIC, - GC_EVENT_ADDSTATUS, - GC_EVENT_INFORMATION, - GC_EVENT_QUIT, - GC_EVENT_KICK, - GC_EVENT_NOTICE, - GC_EVENT_HIGHLIGHT -}; - -class CChatEventOptionDlg : public CDlgBase -{ - CCtrlCheck chkTray, chkPopup, chkRightClick; - CCtrlMButton btn1, btn2, btn3, btn4; - - void InvertColumn(int ctrlId) - { - int enabled = !IsDlgButtonChecked(m_hwnd, ctrlId); - for (int i = 0; i < _countof(_eventorder); i++) - CheckDlgButton(m_hwnd, ctrlId + i, enabled); - NotifyChange(); - } - -public: - CChatEventOptionDlg() : - CDlgBase(g_plugin, IDD_OPT_CHAT_EVENTS), - chkTray(this, IDC_TRAYONLYFORINACTIVE), - chkPopup(this, IDC_POPUPONLYFORINACTIVE), - chkRightClick(this, IDC_RIGHTCLICK), - btn1(this, IDC_ICON1, SKINICON_OTHER_POPUP, LPGEN("Popup")), - btn2(this, IDC_ICON2, SKINICON_OTHER_MIRANDA, LPGEN("Tray")), - btn3(this, IDC_ICON3, SKINICON_OTHER_SOUND, LPGEN("Sound")), - btn4(this, IDC_ICON4, SKINICON_EVENT_FILE, LPGEN("Log to file")) - { - CreateLink(chkTray, g_bChatTrayInactive); - CreateLink(chkPopup, g_bChatPopupInactive); - CreateLink(chkRightClick, g_chatApi.bRightClickFilter); - - btn1.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Popup); - btn2.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Tray); - btn3.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Sound); - btn4.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Log); - } - - bool OnInitDialog() override - { - btn1.MakeFlat(); btn2.MakeFlat(); btn3.MakeFlat(); btn4.MakeFlat(); - - uint32_t dwFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", GC_EVENT_ALL); - uint32_t dwTrayFlags = db_get_dw(0, CHAT_MODULE, "TrayIconFlags", GC_EVENT_HIGHLIGHT); - uint32_t dwPopupFlags = db_get_dw(0, CHAT_MODULE, "PopupFlags", GC_EVENT_HIGHLIGHT); - uint32_t dwSoundFlags = db_get_dw(0, CHAT_MODULE, "SoundFlags", GC_EVENT_HIGHLIGHT); - uint32_t dwLogFlags = db_get_dw(0, CHAT_MODULE, "DiskLogFlags", GC_EVENT_ALL); - - for (int i = 0; i < _countof(_eventorder); i++) { - if (_eventorder[i] != GC_EVENT_HIGHLIGHT) { - CheckDlgButton(m_hwnd, IDC_1 + i, dwFilterFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_L1 + i, dwLogFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - } - CheckDlgButton(m_hwnd, IDC_P1 + i, dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_T1 + i, dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_S1 + i, dwSoundFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - } - return true; - } - - bool OnApply() override - { - uint32_t dwFilterFlags = 0, dwTrayFlags = 0, dwPopupFlags = 0, dwSoundFlags = 0, dwLogFlags = 0; - - for (int i = 0; i < _countof(_eventorder); i++) { - if (_eventorder[i] != GC_EVENT_HIGHLIGHT) { - dwFilterFlags |= (IsDlgButtonChecked(m_hwnd, IDC_1 + i) ? _eventorder[i] : 0); - dwLogFlags |= (IsDlgButtonChecked(m_hwnd, IDC_L1 + i) ? _eventorder[i] : 0); - } - dwSoundFlags |= (IsDlgButtonChecked(m_hwnd, IDC_S1 + i) ? _eventorder[i] : 0); - dwPopupFlags |= (IsDlgButtonChecked(m_hwnd, IDC_P1 + i) ? _eventorder[i] : 0); - dwTrayFlags |= (IsDlgButtonChecked(m_hwnd, IDC_T1 + i) ? _eventorder[i] : 0); - } - db_set_dw(0, CHAT_MODULE, "FilterFlags", dwFilterFlags); - db_set_dw(0, CHAT_MODULE, "PopupFlags", dwPopupFlags); - db_set_dw(0, CHAT_MODULE, "SoundFlags", dwSoundFlags); - db_set_dw(0, CHAT_MODULE, "TrayIconFlags", dwTrayFlags); - db_set_dw(0, CHAT_MODULE, "DiskLogFlags", dwLogFlags); - - LoadGlobalSettings(); - return true; - } - - void onClick_Popup(CCtrlButton *) { InvertColumn(IDC_P1); } - void onClick_Sound(CCtrlButton *) { InvertColumn(IDC_S1); } - void onClick_Tray(CCtrlButton *) { InvertColumn(IDC_T1); } - void onClick_Log(CCtrlButton *) { InvertColumn(IDC_L1); } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -///////////////////////////////////////////////////////////////////////////////////////// -// Popup options - -class COptPopupDlg : public CDlgBase -{ - CCtrlSpin spinTimeout; - CCtrlCheck chkRadio1, chkRadio2, chkRadio3; - CCtrlColor clrBack, clrText; - -public: - COptPopupDlg() : - CDlgBase(g_plugin, IDD_OPTIONSPOPUP), - clrBack(this, IDC_BKG), - clrText(this, IDC_TEXT), - chkRadio1(this, IDC_RADIO1), - chkRadio2(this, IDC_RADIO2), - chkRadio3(this, IDC_RADIO3), - spinTimeout(this, IDC_SPIN1, 100, -1) - { - chkRadio1.OnChange = chkRadio2.OnChange = chkRadio3.OnChange = Callback(this, &COptPopupDlg::onChange_Radio); - } - - bool OnInitDialog() override - { - clrBack.SetColor(g_Settings->crPUBkgColour); - clrText.SetColor(g_Settings->crPUTextColour); - - if (g_Settings->iPopupStyle == 2) - CheckDlgButton(m_hwnd, IDC_RADIO2, BST_CHECKED); - else if (g_Settings->iPopupStyle == 3) - CheckDlgButton(m_hwnd, IDC_RADIO3, BST_CHECKED); - else - CheckDlgButton(m_hwnd, IDC_RADIO1, BST_CHECKED); - onChange_Radio(0); - - spinTimeout.SetPosition(g_Settings->iPopupTimeout); - return true; - } - - bool OnApply() override - { - if (IsDlgButtonChecked(m_hwnd, IDC_RADIO2) == BST_CHECKED) - g_Settings->iPopupStyle = 2; - else if (IsDlgButtonChecked(m_hwnd, IDC_RADIO3) == BST_CHECKED) - g_Settings->iPopupStyle = 3; - else - g_Settings->iPopupStyle = 1; - db_set_b(0, CHAT_MODULE, "PopupStyle", g_Settings->iPopupStyle); - - db_set_w(0, CHAT_MODULE, "PopupTimeout", g_Settings->iPopupTimeout = spinTimeout.GetPosition()); - db_set_dw(0, CHAT_MODULE, "PopupColorBG", g_Settings->crPUBkgColour = clrBack.GetColor()); - db_set_dw(0, CHAT_MODULE, "PopupColorText", g_Settings->crPUTextColour = clrText.GetColor()); - return true; - } - - void onChange_Radio(CCtrlCheck *) - { - bool bStatus = chkRadio3.GetState(); - clrBack.Enable(bStatus); - clrText.Enable(bStatus); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -void ChatOptionsInit(WPARAM wParam) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.flags = ODPF_BOLDGROUPS; - odp.position = 910000000; - odp.szGroup.a = LPGEN("Message sessions"); - odp.szTitle.a = LPGEN("Group chats"); - odp.szTab.a = LPGEN("Events and filters"); - odp.pDialog = new CChatEventOptionDlg(); - g_plugin.addOptions(wParam, &odp); - - odp.position = 910000002; - odp.szTitle.a = LPGEN("Group chats"); - odp.szGroup.a = LPGEN("Popups"); - odp.szTab.a = nullptr; - odp.pDialog = new COptPopupDlg(); - g_plugin.addOptions(wParam, &odp); -} +/*
+Chat module plugin for Miranda IM
+
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "chat.h"
+
+CMOption<bool> g_bChatPopupInactive(CHAT_MODULE, "PopupInactiveOnly", true);
+CMOption<bool> g_bChatTrayInactive(CHAT_MODULE, "TrayIconInactiveOnly", true);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Group chat - Events
+
+#define NR_GC_EVENTS 12
+
+static UINT _eventorder[] =
+{
+ GC_EVENT_ACTION,
+ GC_EVENT_MESSAGE,
+ GC_EVENT_NICK,
+ GC_EVENT_JOIN,
+ GC_EVENT_PART,
+ GC_EVENT_TOPIC,
+ GC_EVENT_ADDSTATUS,
+ GC_EVENT_INFORMATION,
+ GC_EVENT_QUIT,
+ GC_EVENT_KICK,
+ GC_EVENT_NOTICE,
+ GC_EVENT_HIGHLIGHT
+};
+
+class CChatEventOptionDlg : public CDlgBase
+{
+ CCtrlCheck chkTray, chkPopup, chkRightClick;
+ CCtrlMButton btn1, btn2, btn3, btn4;
+
+ void InvertColumn(int ctrlId)
+ {
+ int enabled = !IsDlgButtonChecked(m_hwnd, ctrlId);
+ for (int i = 0; i < _countof(_eventorder); i++)
+ CheckDlgButton(m_hwnd, ctrlId + i, enabled);
+ NotifyChange();
+ }
+
+public:
+ CChatEventOptionDlg() :
+ CDlgBase(g_plugin, IDD_OPT_CHAT_EVENTS),
+ chkTray(this, IDC_TRAYONLYFORINACTIVE),
+ chkPopup(this, IDC_POPUPONLYFORINACTIVE),
+ chkRightClick(this, IDC_RIGHTCLICK),
+ btn1(this, IDC_ICON1, SKINICON_OTHER_POPUP, LPGEN("Popup")),
+ btn2(this, IDC_ICON2, SKINICON_OTHER_MIRANDA, LPGEN("Tray")),
+ btn3(this, IDC_ICON3, SKINICON_OTHER_SOUND, LPGEN("Sound")),
+ btn4(this, IDC_ICON4, SKINICON_EVENT_FILE, LPGEN("Log to file"))
+ {
+ CreateLink(chkTray, g_bChatTrayInactive);
+ CreateLink(chkPopup, g_bChatPopupInactive);
+ CreateLink(chkRightClick, g_chatApi.bRightClickFilter);
+
+ btn1.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Popup);
+ btn2.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Tray);
+ btn3.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Sound);
+ btn4.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Log);
+ }
+
+ bool OnInitDialog() override
+ {
+ btn1.MakeFlat(); btn2.MakeFlat(); btn3.MakeFlat(); btn4.MakeFlat();
+
+ uint32_t dwFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", GC_EVENT_ALL);
+ uint32_t dwTrayFlags = db_get_dw(0, CHAT_MODULE, "TrayIconFlags", GC_EVENT_HIGHLIGHT);
+ uint32_t dwPopupFlags = db_get_dw(0, CHAT_MODULE, "PopupFlags", GC_EVENT_HIGHLIGHT);
+ uint32_t dwSoundFlags = db_get_dw(0, CHAT_MODULE, "SoundFlags", GC_EVENT_HIGHLIGHT);
+ uint32_t dwLogFlags = db_get_dw(0, CHAT_MODULE, "DiskLogFlags", GC_EVENT_ALL);
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ if (_eventorder[i] != GC_EVENT_HIGHLIGHT) {
+ CheckDlgButton(m_hwnd, IDC_1 + i, dwFilterFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_L1 + i, dwLogFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
+ }
+ CheckDlgButton(m_hwnd, IDC_P1 + i, dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_T1 + i, dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_S1 + i, dwSoundFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
+ }
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ uint32_t dwFilterFlags = 0, dwTrayFlags = 0, dwPopupFlags = 0, dwSoundFlags = 0, dwLogFlags = 0;
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ if (_eventorder[i] != GC_EVENT_HIGHLIGHT) {
+ dwFilterFlags |= (IsDlgButtonChecked(m_hwnd, IDC_1 + i) ? _eventorder[i] : 0);
+ dwLogFlags |= (IsDlgButtonChecked(m_hwnd, IDC_L1 + i) ? _eventorder[i] : 0);
+ }
+ dwSoundFlags |= (IsDlgButtonChecked(m_hwnd, IDC_S1 + i) ? _eventorder[i] : 0);
+ dwPopupFlags |= (IsDlgButtonChecked(m_hwnd, IDC_P1 + i) ? _eventorder[i] : 0);
+ dwTrayFlags |= (IsDlgButtonChecked(m_hwnd, IDC_T1 + i) ? _eventorder[i] : 0);
+ }
+ db_set_dw(0, CHAT_MODULE, "FilterFlags", dwFilterFlags);
+ db_set_dw(0, CHAT_MODULE, "PopupFlags", dwPopupFlags);
+ db_set_dw(0, CHAT_MODULE, "SoundFlags", dwSoundFlags);
+ db_set_dw(0, CHAT_MODULE, "TrayIconFlags", dwTrayFlags);
+ db_set_dw(0, CHAT_MODULE, "DiskLogFlags", dwLogFlags);
+
+ LoadGlobalSettings();
+ return true;
+ }
+
+ void onClick_Popup(CCtrlButton *) { InvertColumn(IDC_P1); }
+ void onClick_Sound(CCtrlButton *) { InvertColumn(IDC_S1); }
+ void onClick_Tray(CCtrlButton *) { InvertColumn(IDC_T1); }
+ void onClick_Log(CCtrlButton *) { InvertColumn(IDC_L1); }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Popup options
+
+class COptPopupDlg : public CDlgBase
+{
+ CCtrlSpin spinTimeout;
+ CCtrlCheck chkRadio1, chkRadio2, chkRadio3;
+ CCtrlColor clrBack, clrText;
+
+public:
+ COptPopupDlg() :
+ CDlgBase(g_plugin, IDD_OPTIONSPOPUP),
+ clrBack(this, IDC_BKG),
+ clrText(this, IDC_TEXT),
+ chkRadio1(this, IDC_RADIO1),
+ chkRadio2(this, IDC_RADIO2),
+ chkRadio3(this, IDC_RADIO3),
+ spinTimeout(this, IDC_SPIN1, 100, -1)
+ {
+ chkRadio1.OnChange = chkRadio2.OnChange = chkRadio3.OnChange = Callback(this, &COptPopupDlg::onChange_Radio);
+ }
+
+ bool OnInitDialog() override
+ {
+ clrBack.SetColor(g_Settings->crPUBkgColour);
+ clrText.SetColor(g_Settings->crPUTextColour);
+
+ if (g_Settings->iPopupStyle == 2)
+ CheckDlgButton(m_hwnd, IDC_RADIO2, BST_CHECKED);
+ else if (g_Settings->iPopupStyle == 3)
+ CheckDlgButton(m_hwnd, IDC_RADIO3, BST_CHECKED);
+ else
+ CheckDlgButton(m_hwnd, IDC_RADIO1, BST_CHECKED);
+ onChange_Radio(0);
+
+ spinTimeout.SetPosition(g_Settings->iPopupTimeout);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ if (IsDlgButtonChecked(m_hwnd, IDC_RADIO2) == BST_CHECKED)
+ g_Settings->iPopupStyle = 2;
+ else if (IsDlgButtonChecked(m_hwnd, IDC_RADIO3) == BST_CHECKED)
+ g_Settings->iPopupStyle = 3;
+ else
+ g_Settings->iPopupStyle = 1;
+ db_set_b(0, CHAT_MODULE, "PopupStyle", g_Settings->iPopupStyle);
+
+ db_set_w(0, CHAT_MODULE, "PopupTimeout", g_Settings->iPopupTimeout = spinTimeout.GetPosition());
+ db_set_dw(0, CHAT_MODULE, "PopupColorBG", g_Settings->crPUBkgColour = clrBack.GetColor());
+ db_set_dw(0, CHAT_MODULE, "PopupColorText", g_Settings->crPUTextColour = clrText.GetColor());
+ return true;
+ }
+
+ void onChange_Radio(CCtrlCheck *)
+ {
+ bool bStatus = chkRadio3.GetState();
+ clrBack.Enable(bStatus);
+ clrText.Enable(bStatus);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void ChatOptionsInit(WPARAM wParam)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.position = 910000000;
+ odp.szGroup.a = LPGEN("Message sessions");
+ odp.szTitle.a = LPGEN("Group chats");
+ odp.szTab.a = LPGEN("Events and filters");
+ odp.pDialog = new CChatEventOptionDlg();
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.position = 910000002;
+ odp.szTitle.a = LPGEN("Group chats");
+ odp.szGroup.a = LPGEN("Popups");
+ odp.szTab.a = nullptr;
+ odp.pDialog = new COptPopupDlg();
+ g_plugin.addOptions(wParam, &odp);
+}
diff --git a/src/mir_app/src/clc.cpp b/src/mir_app/src/clc.cpp index d44673425e..0266b7bcf3 100644 --- a/src/mir_app/src/clc.cpp +++ b/src/mir_app/src/clc.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clc.h b/src/mir_app/src/clc.h index d57ec3b1ab..15eae7d75c 100644 --- a/src/mir_app/src/clc.h +++ b/src/mir_app/src/clc.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clccontact.cpp b/src/mir_app/src/clccontact.cpp index 3dbf578cd6..06639ba8d0 100644 --- a/src/mir_app/src/clccontact.cpp +++ b/src/mir_app/src/clccontact.cpp @@ -1,72 +1,72 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "clc.h" - -extern HANDLE hGroupChangeEvent; - -MIR_APP_DLL(void) Clist_LoadContactTree(void) -{ - bool hideOffline = Clist::HideOffline; - for (auto &hContact : Contacts()) { - int status = Contact::GetStatus(hContact); - if ((!hideOffline || status != ID_STATUS_OFFLINE) && !Contact::IsHidden(hContact)) - Clist_ChangeContactIcon(hContact, g_clistApi.pfnIconFromStatusMode(Proto_GetBaseAccountName(hContact), status, hContact)); - } - Clist_EndRebuild(); -} - -MIR_APP_DLL(int) Clist_ContactChangeGroup(MCONTACT hContact, MGROUP hGroup) -{ - CLISTGROUPCHANGE grpChg = {}; - - if (hGroup == 0) - db_unset(hContact, "CList", "Group"); - else { - grpChg.pszNewName = Clist_GroupGetName(hGroup, nullptr); - db_set_ws(hContact, "CList", "Group", grpChg.pszNewName); - } - - NotifyEventHooks(hGroupChangeEvent, hContact, (LPARAM)&grpChg); - return 0; -} - -int fnSetHideOffline(int iValue) -{ - if (iValue == -1) // invert the current value - iValue = !Clist::HideOffline; - - switch (iValue) { - case 0: - case 1: - Clist::HideOffline = iValue; - break; - - default: - return -1; - } - Clist_LoadContactTree(); - return iValue; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "clc.h"
+
+extern HANDLE hGroupChangeEvent;
+
+MIR_APP_DLL(void) Clist_LoadContactTree(void)
+{
+ bool hideOffline = Clist::HideOffline;
+ for (auto &hContact : Contacts()) {
+ int status = Contact::GetStatus(hContact);
+ if ((!hideOffline || status != ID_STATUS_OFFLINE) && !Contact::IsHidden(hContact))
+ Clist_ChangeContactIcon(hContact, g_clistApi.pfnIconFromStatusMode(Proto_GetBaseAccountName(hContact), status, hContact));
+ }
+ Clist_EndRebuild();
+}
+
+MIR_APP_DLL(int) Clist_ContactChangeGroup(MCONTACT hContact, MGROUP hGroup)
+{
+ CLISTGROUPCHANGE grpChg = {};
+
+ if (hGroup == 0)
+ db_unset(hContact, "CList", "Group");
+ else {
+ grpChg.pszNewName = Clist_GroupGetName(hGroup, nullptr);
+ db_set_ws(hContact, "CList", "Group", grpChg.pszNewName);
+ }
+
+ NotifyEventHooks(hGroupChangeEvent, hContact, (LPARAM)&grpChg);
+ return 0;
+}
+
+int fnSetHideOffline(int iValue)
+{
+ if (iValue == -1) // invert the current value
+ iValue = !Clist::HideOffline;
+
+ switch (iValue) {
+ case 0:
+ case 1:
+ Clist::HideOffline = iValue;
+ break;
+
+ default:
+ return -1;
+ }
+ Clist_LoadContactTree();
+ return iValue;
+}
diff --git a/src/mir_app/src/clcfiledrop.cpp b/src/mir_app/src/clcfiledrop.cpp index 25501d7dac..840a046eae 100644 --- a/src/mir_app/src/clcfiledrop.cpp +++ b/src/mir_app/src/clcfiledrop.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clcidents.cpp b/src/mir_app/src/clcidents.cpp index 98ca64a451..83497cf50d 100644 --- a/src/mir_app/src/clcidents.cpp +++ b/src/mir_app/src/clcidents.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clcitems.cpp b/src/mir_app/src/clcitems.cpp index 42c3f37d2f..eef6b57d56 100644 --- a/src/mir_app/src/clcitems.cpp +++ b/src/mir_app/src/clcitems.cpp @@ -1,730 +1,730 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "clc.h" - -static ClcCacheEntry nullpce = {}; - -// routines for managing adding/removal of items in the list, including sorting - -ClcContact* fnAddItemToGroup(ClcGroup *group, int iAboveItem) -{ - ClcContact* newItem = g_clistApi.pfnCreateClcContact(); - newItem->type = CLCIT_DIVIDER; - newItem->flags = 0; - newItem->szText[0] = '\0'; - memset(newItem->iExtraImage, 0xFF, sizeof(newItem->iExtraImage)); - group->cl.insert(newItem, iAboveItem); - return newItem; -} - -ClcGroup* fnAddGroup(HWND hwnd, ClcData *dat, const wchar_t *szName, uint32_t flags, int groupId, int calcTotalMembers) -{ - dat->bNeedsResort = true; - if (!(GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_USEGROUPS)) - return &dat->list; - - ClcGroup *group = &dat->list; - wchar_t *pNextField = NEWWSTR_ALLOCA(szName); - do { - wchar_t *pBackslash = wcschr(pNextField, '\\'), *pThisField = pNextField; - if (pBackslash == nullptr) { - pNextField = nullptr; - } - else { - *pBackslash = 0; - pNextField = pBackslash + 1; - } - - int i, compareResult = 1; - for (i = 0; i < group->cl.getCount(); i++) { - ClcContact *cc = group->cl[i]; - if (cc->type == CLCIT_CONTACT) - break; - if (cc->type != CLCIT_GROUP) - continue; - compareResult = mir_wstrcmp(pThisField, cc->szText); - if (compareResult == 0) { - if (pNextField == nullptr && flags != (uint32_t)-1) { - cc->groupId = (uint16_t)groupId; - group = cc->group; - group->expanded = (flags & GROUPF_EXPANDED) != 0; - group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; - group->groupId = groupId; - } - else group = cc->group; - break; - } - if (pNextField == nullptr && cc->groupId == 0) - break; - if (!(dat->exStyle & CLS_EX_SORTGROUPSALPHA) && groupId && cc->groupId > groupId) - break; - } - - if (compareResult) { // not found - if (groupId == 0) - return nullptr; - - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); - cc->type = CLCIT_GROUP; - mir_wstrncpy(cc->szText, pThisField, _countof(cc->szText)); - cc->groupId = (uint16_t)(pNextField ? 0 : groupId); - cc->group = new ClcGroup(10); - cc->group->parent = group; - group = cc->group; - - if (flags == (uint32_t)-1 || pNextField != nullptr) { - group->expanded = 0; - group->hideOffline = 0; - } - else { - group->expanded = (flags & GROUPF_EXPANDED) != 0; - group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; - } - group->groupId = pNextField ? 0 : groupId; - group->totalMembers = 0; - if (flags != (uint32_t)-1 && pNextField == nullptr && calcTotalMembers) { - uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); - for (auto &hContact : Contacts()) { - ClcCacheEntry *cache = Clist_GetCacheEntry(hContact); - if (!mir_wstrcmp(cache->tszGroup, szName) && (style & CLS_SHOWHIDDEN || !cache->bIsHidden)) - group->totalMembers++; - } - } - } - } while (pNextField); - return group; -} - -void fnFreeContact(ClcContact* p) -{ - if (p->type == CLCIT_GROUP) { - FreeGroup(p->group); - delete p->group; p->group = nullptr; - } - - mir_free(p); -} - -void FreeGroup(ClcGroup *group) -{ - if (!group) - return; - - for (auto &it : group->cl) - g_clistApi.pfnFreeContact(it); - - group->cl.destroy(); -} - -static int iInfoItemUniqueHandle = 0; -ClcContact* fnAddInfoItemToGroup(ClcGroup *group, int flags, const wchar_t *pszText) -{ - int i = 0; - - if (flags & CLCIIF_BELOWCONTACTS) - i = group->cl.getCount(); - else if (flags & CLCIIF_BELOWGROUPS) { - for (; i < group->cl.getCount(); i++) - if (group->cl[i]->type == CLCIT_CONTACT) - break; - } - else - for (; i < group->cl.getCount(); i++) - if (group->cl[i]->type != CLCIT_INFO) - break; - - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); - iInfoItemUniqueHandle = LOWORD(iInfoItemUniqueHandle + 1); - if (iInfoItemUniqueHandle == 0) - ++iInfoItemUniqueHandle; - cc->type = CLCIT_INFO; - cc->flags = (uint8_t)flags; - cc->hContact = (MCONTACT)++iInfoItemUniqueHandle; - mir_wstrncpy(cc->szText, pszText, _countof(cc->szText)); - return cc; -} - -ClcContact* fnAddContactToGroup(ClcData *dat, ClcGroup *group, MCONTACT hContact) -{ - int i, index = -1; - - dat->bNeedsResort = true; - for (i = group->cl.getCount() - 1; i >= 0; i--) { - ClcContact *cc = group->cl[i]; - if (cc->hContact == hContact) - return cc; - - if (index == -1) - if (cc->type != CLCIT_INFO || !(cc->flags & CLCIIF_BELOWCONTACTS)) - index = i; - } - - char *szProto = Proto_GetBaseAccountName(hContact); - - ClcCacheEntry *pce = Clist_GetCacheEntry(hContact); - replaceStrW(pce->tszGroup, nullptr); - - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, index + 1); - cc->type = CLCIT_CONTACT; - cc->iImage = Clist_GetContactIcon(hContact); - cc->hContact = hContact; - cc->pce = pce; - if (szProto != nullptr && !Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE))) - cc->flags |= CONTACTF_ONLINE; - uint16_t apparentMode = szProto != nullptr ? db_get_w(hContact, szProto, "ApparentMode", 0) : 0; - if (apparentMode == ID_STATUS_OFFLINE) - cc->flags |= CONTACTF_INVISTO; - else if (apparentMode == ID_STATUS_ONLINE) - cc->flags |= CONTACTF_VISTO; - else if (apparentMode) - cc->flags |= CONTACTF_VISTO | CONTACTF_INVISTO; - if (!Contact::OnList(hContact)) - cc->flags |= CONTACTF_NOTONLIST; - uint32_t idleMode = szProto != nullptr ? db_get_dw(hContact, szProto, "IdleTS", 0) : 0; - if (idleMode) - cc->flags |= CONTACTF_IDLE; - mir_wstrncpy(cc->szText, Clist_GetContactDisplayName(hContact), _countof(cc->szText)); - return cc; -} - -void fnAddContactToTree(HWND hwnd, ClcData *dat, MCONTACT hContact, int updateTotalCount, int checkHideOffline) -{ - uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); - uint16_t status = ID_STATUS_OFFLINE; - char *szProto = Proto_GetBaseAccountName(hContact); - - dat->bNeedsResort = true; - if (style & CLS_NOHIDEOFFLINE) - checkHideOffline = 0; - if (checkHideOffline) - if (szProto != nullptr) - status = db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE); - - int i; - uint32_t groupFlags; - ClcGroup *group; - ptrW tszGroup(Clist_GetGroup(hContact)); - if (tszGroup == nullptr) - group = &dat->list; - else { - group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, (uint32_t)-1, 0, 0); - if (group == nullptr) { - if (!(style & CLS_HIDEEMPTYGROUPS)) - return; - - if (checkHideOffline && Clist_IsHiddenMode(dat, status)) { - for (i = 1;; i++) { - wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); - if (szGroupName == nullptr) - return; - - if (!mir_wstrcmp(szGroupName, tszGroup)) - break; - } - if (groupFlags & GROUPF_HIDEOFFLINE) - return; - } - for (i = 1;; i++) { - wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); - if (szGroupName == nullptr) - return; - - if (!mir_wstrcmp(szGroupName, tszGroup)) - break; - - size_t len = mir_wstrlen(szGroupName); - if (!wcsncmp(szGroupName, tszGroup, len) && tszGroup[len] == '\\') - g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 1); - } - group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, groupFlags, i, 1); - } - } - - if (checkHideOffline) { - if (Clist_IsHiddenMode(dat, status) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { - if (updateTotalCount) - group->totalMembers++; - return; - } - } - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - if (updateTotalCount) - group->totalMembers++; -} - -MIR_APP_DLL(ClcGroup*) Clist_RemoveItemFromGroup(HWND hwnd, ClcGroup *group, ClcContact *contact, int updateTotalCount) -{ - int iContact = group->cl.indexOf(contact); - if (iContact == -1) - return group; - - if (contact->type == CLCIT_CONTACT) { - if (updateTotalCount) - group->totalMembers--; - - g_clistApi.pfnInvalidateDisplayNameCacheEntry(contact->hContact); - } - - g_clistApi.pfnFreeContact(group->cl[iContact]); - group->cl.remove(iContact); - - if ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) && group->cl.getCount() == 0 && group->parent != nullptr) - for (auto &cc : group->parent->cl) - if (cc->type == CLCIT_GROUP && cc->groupId == group->groupId) - return Clist_RemoveItemFromGroup(hwnd, group->parent, cc, 0); - - return group; -} - -MIR_APP_DLL(void) Clist_DeleteItemFromTree(HWND hwnd, MCONTACT hItem) -{ - ClcData *dat = (ClcData*)GetWindowLongPtr(hwnd, 0); - dat->bNeedsResort = true; - - // if a contact is found in our contact list, remove it from its group and detach from cache - ClcGroup *group; - ClcContact *contact; - if (Clist_FindItem(hwnd, dat, hItem, &contact, &group)) { - Clist_RemoveItemFromGroup(hwnd, group, contact, 1); - contact->pce = &nullpce; - return; - } - - // if we don't have this contact, simply try to update the number of contacts in a group - if (!IsHContactContact(hItem)) - return; - - ptrW wszGroup(Clist_GetGroup(hItem)); - if (wszGroup == nullptr) - return; - - // decrease member counts of all parent groups too - group = &dat->list; - int nameOffset = 0; - for (int i = 0;; i++) { - if (group->scanIndex == group->cl.getCount()) - break; - - ClcContact *cc = group->cl[i]; - if (cc->type == CLCIT_GROUP) { - size_t len = mir_wstrlen(cc->szText); - if (!wcsncmp(cc->szText, wszGroup.get() + nameOffset, len) && (wszGroup[nameOffset + len] == '\\' || wszGroup[nameOffset + len] == '\0')) { - group->totalMembers--; - if (wszGroup[nameOffset + len] == '\0') - break; - } - } - } -} - -int fnGetContactHiddenStatus(MCONTACT hContact, char*, ClcData*) -{ - return Contact::IsHidden(hContact); -} - -void fnRebuildEntireList(HWND hwnd, ClcData *dat) -{ - uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); - - dat->list.expanded = 1; - dat->list.hideOffline = db_get_b(0, "CLC", "HideOfflineRoot", 0) && (style & CLS_USEGROUPS); - dat->list.cl.destroy(); - dat->list.totalMembers = 0; - dat->selection = -1; - - for (int i = 1;; i++) { - uint32_t groupFlags; - wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); - if (szGroupName == nullptr) - break; - g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 0); - } - - for (auto &hContact : Contacts()) { - int nHiddenStatus = g_clistApi.pfnGetContactHiddenStatus(hContact, nullptr, dat); - if (((style & CLS_SHOWHIDDEN) && nHiddenStatus != -1) || !nHiddenStatus) { - ClcCacheEntry *pce = Clist_GetCacheEntry(hContact); - if (pce->szProto == nullptr) - continue; - - ClcGroup *group; - ptrW tszGroupName(Clist_GetGroup(hContact)); - if (tszGroupName == nullptr) - group = &dat->list; - else { - group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroupName, (uint32_t)-1, 0, 0); - if (group == nullptr && style & CLS_SHOWHIDDEN) - group = &dat->list; - } - - if (group != nullptr) { - group->totalMembers++; - - if (dat->bFilterSearch && dat->szQuickSearch[0] != '\0') { - wchar_t *name = Clist_GetContactDisplayName(hContact); - wchar_t *lowered_name = CharLowerW(NEWWSTR_ALLOCA(name)); - wchar_t *lowered_search = CharLowerW(NEWWSTR_ALLOCA(dat->szQuickSearch)); - - if (wcsstr(lowered_name, lowered_search)) - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - else if (!(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { - char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto == nullptr) { - if (!Clist_IsHiddenMode(dat, ID_STATUS_OFFLINE) || g_clistApi.pfnIsVisibleContact(pce, group)) - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - else if (!Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE)) || g_clistApi.pfnIsVisibleContact(pce, group)) - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - else g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - } - } - - if (style & CLS_HIDEEMPTYGROUPS) { - ClcGroup *group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - if (cc->group->cl.getCount() == 0) { - group = Clist_RemoveItemFromGroup(hwnd, group, cc, 0); - } - else { - group = cc->group; - group->scanIndex = 0; - } - continue; - } - group->scanIndex++; - } - } - - g_clistApi.pfnSortCLC(hwnd, dat, 0); - ExtraIcon_SetAll(); -} - -int fnGetGroupContentsCount(ClcGroup *group, int visibleOnly) -{ - int count = group->cl.getCount(); - ClcGroup *topgroup = group; - - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if (group == topgroup) - break; - group = group->parent; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP && (!visibleOnly || cc->group->expanded)) { - group = cc->group; - group->scanIndex = 0; - count += group->cl.getCount(); - continue; - } - group->scanIndex++; - } - return count; -} - -static int __cdecl GroupSortProc(const void* p1, const void* p2) -{ - ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; - - return mir_wstrcmpi(contact1[0]->szText, contact2[0]->szText); -} - -static int __cdecl ContactSortProc(const void* p1, const void* p2) -{ - ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; - - int result = g_clistApi.pfnCompareContacts(contact1[0], contact2[0]); - if (result) - return result; - //nothing to distinguish them, so make sure they stay in the same order - return (int)((INT_PTR)contact2[0]->hContact - (INT_PTR)contact1[0]->hContact); -} - -static void InsertionSort(ClcContact **pContactArray, int nArray, int(*CompareProc) (const void *, const void *)) -{ - int i, j; - ClcContact* testElement; - - for (i = 1; i < nArray; i++) { - if (CompareProc(&pContactArray[i - 1], &pContactArray[i]) > 0) { - testElement = pContactArray[i]; - for (j = i - 2; j >= 0; j--) - if (CompareProc(&pContactArray[j], &testElement) <= 0) - break; - j++; - memmove(&pContactArray[j + 1], &pContactArray[j], sizeof(void*) * (i - j)); - pContactArray[j] = testElement; - } - } -} - -static void SortGroup(ClcData *dat, ClcGroup *group, int useInsertionSort) -{ - int i, sortCount; - - for (i = group->cl.getCount() - 1; i >= 0; i--) { - if (group->cl[i]->type == CLCIT_DIVIDER) { - mir_free(group->cl[i]); - group->cl.remove(i); - } - } - - for (i = 0; i < group->cl.getCount(); i++) - if (group->cl[i]->type != CLCIT_INFO) - break; - if (i > group->cl.getCount() - 2) - return; - if (group->cl[i]->type == CLCIT_GROUP) { - if (dat->exStyle & CLS_EX_SORTGROUPSALPHA) { - for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++) - if (group->cl[i + sortCount]->type != CLCIT_GROUP) - break; - qsort(group->cl.getArray() + i, sortCount, sizeof(void*), GroupSortProc); - i = i + sortCount; - } - for (; i < group->cl.getCount(); i++) - if (group->cl[i]->type == CLCIT_CONTACT) - break; - if (group->cl.getCount() - i < 2) - return; - } - for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++) - if (group->cl[i + sortCount]->type != CLCIT_CONTACT) - break; - if (useInsertionSort) - InsertionSort(group->cl.getArray() + i, sortCount, ContactSortProc); - else - qsort(group->cl.getArray() + i, sortCount, sizeof(void*), ContactSortProc); - if (dat->exStyle & CLS_EX_DIVIDERONOFF) { - int prevContactOnline = 0; - for (i = 0; i < group->cl.getCount(); i++) { - if (group->cl[i]->type != CLCIT_CONTACT) - continue; - if (group->cl[i]->flags & CONTACTF_ONLINE) - prevContactOnline = 1; - else { - if (prevContactOnline) { - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); - cc->type = CLCIT_DIVIDER; - mir_wstrcpy(cc->szText, TranslateT("Offline")); - } - break; - } - } - } -} - -void fnSortCLC(HWND hwnd, ClcData *dat, int useInsertionSort) -{ - ClcGroup *group = &dat->list; - - if (dat->bNeedsResort) { - MCONTACT hSelItem; - ClcContact *selcontact; - if (g_clistApi.pfnGetRowByIndex(dat, dat->selection, &selcontact, nullptr) == -1) - hSelItem = 0; - else - hSelItem = Clist_ContactToHItem(selcontact); - group->scanIndex = 0; - SortGroup(dat, group, useInsertionSort); - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - group = cc->group; - group->scanIndex = 0; - SortGroup(dat, group, useInsertionSort); - continue; - } - group->scanIndex++; - } - - if (hSelItem) { - ClcGroup *selgroup; - if (Clist_FindItem(hwnd, dat, hSelItem, &selcontact, &selgroup)) - dat->selection = g_clistApi.pfnGetRowsPriorTo(&dat->list, selgroup, selgroup->cl.indexOf(selcontact)); - } - - g_clistApi.pfnRecalcScrollBar(hwnd, dat); - } - dat->bNeedsResort = false; - g_clistApi.pfnInvalidateRect(hwnd, nullptr, FALSE); -} - -struct SavedContactState_t -{ - MCONTACT hContact; - uint16_t iExtraImage[EXTRA_ICON_COUNT]; - int checked; -}; - -struct SavedGroupState_t -{ - int groupId, expanded; -}; - -struct SavedInfoState_t -{ - int parentId; - ClcContact contact; -}; - -MIR_APP_DLL(void) Clist_SaveStateAndRebuildList(HWND hwnd, ClcData *dat) -{ - Clist_HideInfoTip(dat); - KillTimer(hwnd, TIMERID_INFOTIP); - KillTimer(hwnd, TIMERID_RENAME); - Clist_EndRename(dat, 1); - - dat->bLockScrollbar = true; - - OBJLIST<SavedContactState_t> saveContact(10, NumericKeySortT); - OBJLIST<SavedGroupState_t> saveGroup(100, NumericKeySortT); - OBJLIST<SavedInfoState_t> saveInfo(10, NumericKeySortT); - - dat->bNeedsResort = true; - ClcGroup *group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - group = cc->group; - group->scanIndex = 0; - - SavedGroupState_t *p = new SavedGroupState_t; - p->groupId = group->groupId; - p->expanded = group->expanded; - saveGroup.insert(p); - continue; - } - else if (cc->type == CLCIT_CONTACT) { - SavedContactState_t *p = new SavedContactState_t; - p->hContact = cc->hContact; - memcpy(p->iExtraImage, cc->iExtraImage, sizeof(p->iExtraImage)); - p->checked = cc->flags & CONTACTF_CHECKED; - saveContact.insert(p); - } - else if (cc->type == CLCIT_INFO) { - SavedInfoState_t *p = new SavedInfoState_t; - p->parentId = (group->parent == nullptr) ? -1 : group->groupId; - p->contact = *cc; - saveInfo.insert(p); - } - group->scanIndex++; - } - - FreeGroup(&dat->list); - g_clistApi.pfnRebuildEntireList(hwnd, dat); - - group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - group = cc->group; - group->scanIndex = 0; - - SavedGroupState_t tmp, *p; - tmp.groupId = group->groupId; - if ((p = saveGroup.find(&tmp)) != nullptr) - group->expanded = p->expanded; - continue; - } - else if (cc->type == CLCIT_CONTACT) { - SavedContactState_t tmp, *p; - tmp.hContact = cc->hContact; - if ((p = saveContact.find(&tmp)) != nullptr) { - memcpy(cc->iExtraImage, p->iExtraImage, sizeof(p->iExtraImage)); - if (p->checked) - cc->flags |= CONTACTF_CHECKED; - } - } - - group->scanIndex++; - } - - for (auto &it : saveInfo) { - if (it->parentId == -1) - group = &dat->list; - else { - ClcContact *contact; - if (!Clist_FindItem(hwnd, dat, it->parentId | HCONTACT_ISGROUP, &contact)) - continue; - group = contact->group; - } - - ClcContact *cc = g_clistApi.pfnAddInfoItemToGroup(group, it->contact.flags, L""); - *cc = it->contact; - } - - dat->bLockScrollbar = false; - Clist_RecalculateGroupCheckboxes(dat); - - g_clistApi.pfnRecalcScrollBar(hwnd, dat); - - NMCLISTCONTROL nm; - nm.hdr.code = CLN_LISTREBUILT; - nm.hdr.hwndFrom = hwnd; - nm.hdr.idFrom = GetDlgCtrlID(hwnd); - SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "clc.h"
+
+static ClcCacheEntry nullpce = {};
+
+// routines for managing adding/removal of items in the list, including sorting
+
+ClcContact* fnAddItemToGroup(ClcGroup *group, int iAboveItem)
+{
+ ClcContact* newItem = g_clistApi.pfnCreateClcContact();
+ newItem->type = CLCIT_DIVIDER;
+ newItem->flags = 0;
+ newItem->szText[0] = '\0';
+ memset(newItem->iExtraImage, 0xFF, sizeof(newItem->iExtraImage));
+ group->cl.insert(newItem, iAboveItem);
+ return newItem;
+}
+
+ClcGroup* fnAddGroup(HWND hwnd, ClcData *dat, const wchar_t *szName, uint32_t flags, int groupId, int calcTotalMembers)
+{
+ dat->bNeedsResort = true;
+ if (!(GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_USEGROUPS))
+ return &dat->list;
+
+ ClcGroup *group = &dat->list;
+ wchar_t *pNextField = NEWWSTR_ALLOCA(szName);
+ do {
+ wchar_t *pBackslash = wcschr(pNextField, '\\'), *pThisField = pNextField;
+ if (pBackslash == nullptr) {
+ pNextField = nullptr;
+ }
+ else {
+ *pBackslash = 0;
+ pNextField = pBackslash + 1;
+ }
+
+ int i, compareResult = 1;
+ for (i = 0; i < group->cl.getCount(); i++) {
+ ClcContact *cc = group->cl[i];
+ if (cc->type == CLCIT_CONTACT)
+ break;
+ if (cc->type != CLCIT_GROUP)
+ continue;
+ compareResult = mir_wstrcmp(pThisField, cc->szText);
+ if (compareResult == 0) {
+ if (pNextField == nullptr && flags != (uint32_t)-1) {
+ cc->groupId = (uint16_t)groupId;
+ group = cc->group;
+ group->expanded = (flags & GROUPF_EXPANDED) != 0;
+ group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0;
+ group->groupId = groupId;
+ }
+ else group = cc->group;
+ break;
+ }
+ if (pNextField == nullptr && cc->groupId == 0)
+ break;
+ if (!(dat->exStyle & CLS_EX_SORTGROUPSALPHA) && groupId && cc->groupId > groupId)
+ break;
+ }
+
+ if (compareResult) { // not found
+ if (groupId == 0)
+ return nullptr;
+
+ ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i);
+ cc->type = CLCIT_GROUP;
+ mir_wstrncpy(cc->szText, pThisField, _countof(cc->szText));
+ cc->groupId = (uint16_t)(pNextField ? 0 : groupId);
+ cc->group = new ClcGroup(10);
+ cc->group->parent = group;
+ group = cc->group;
+
+ if (flags == (uint32_t)-1 || pNextField != nullptr) {
+ group->expanded = 0;
+ group->hideOffline = 0;
+ }
+ else {
+ group->expanded = (flags & GROUPF_EXPANDED) != 0;
+ group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0;
+ }
+ group->groupId = pNextField ? 0 : groupId;
+ group->totalMembers = 0;
+ if (flags != (uint32_t)-1 && pNextField == nullptr && calcTotalMembers) {
+ uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ for (auto &hContact : Contacts()) {
+ ClcCacheEntry *cache = Clist_GetCacheEntry(hContact);
+ if (!mir_wstrcmp(cache->tszGroup, szName) && (style & CLS_SHOWHIDDEN || !cache->bIsHidden))
+ group->totalMembers++;
+ }
+ }
+ }
+ } while (pNextField);
+ return group;
+}
+
+void fnFreeContact(ClcContact* p)
+{
+ if (p->type == CLCIT_GROUP) {
+ FreeGroup(p->group);
+ delete p->group; p->group = nullptr;
+ }
+
+ mir_free(p);
+}
+
+void FreeGroup(ClcGroup *group)
+{
+ if (!group)
+ return;
+
+ for (auto &it : group->cl)
+ g_clistApi.pfnFreeContact(it);
+
+ group->cl.destroy();
+}
+
+static int iInfoItemUniqueHandle = 0;
+ClcContact* fnAddInfoItemToGroup(ClcGroup *group, int flags, const wchar_t *pszText)
+{
+ int i = 0;
+
+ if (flags & CLCIIF_BELOWCONTACTS)
+ i = group->cl.getCount();
+ else if (flags & CLCIIF_BELOWGROUPS) {
+ for (; i < group->cl.getCount(); i++)
+ if (group->cl[i]->type == CLCIT_CONTACT)
+ break;
+ }
+ else
+ for (; i < group->cl.getCount(); i++)
+ if (group->cl[i]->type != CLCIT_INFO)
+ break;
+
+ ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i);
+ iInfoItemUniqueHandle = LOWORD(iInfoItemUniqueHandle + 1);
+ if (iInfoItemUniqueHandle == 0)
+ ++iInfoItemUniqueHandle;
+ cc->type = CLCIT_INFO;
+ cc->flags = (uint8_t)flags;
+ cc->hContact = (MCONTACT)++iInfoItemUniqueHandle;
+ mir_wstrncpy(cc->szText, pszText, _countof(cc->szText));
+ return cc;
+}
+
+ClcContact* fnAddContactToGroup(ClcData *dat, ClcGroup *group, MCONTACT hContact)
+{
+ int i, index = -1;
+
+ dat->bNeedsResort = true;
+ for (i = group->cl.getCount() - 1; i >= 0; i--) {
+ ClcContact *cc = group->cl[i];
+ if (cc->hContact == hContact)
+ return cc;
+
+ if (index == -1)
+ if (cc->type != CLCIT_INFO || !(cc->flags & CLCIIF_BELOWCONTACTS))
+ index = i;
+ }
+
+ char *szProto = Proto_GetBaseAccountName(hContact);
+
+ ClcCacheEntry *pce = Clist_GetCacheEntry(hContact);
+ replaceStrW(pce->tszGroup, nullptr);
+
+ ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, index + 1);
+ cc->type = CLCIT_CONTACT;
+ cc->iImage = Clist_GetContactIcon(hContact);
+ cc->hContact = hContact;
+ cc->pce = pce;
+ if (szProto != nullptr && !Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE)))
+ cc->flags |= CONTACTF_ONLINE;
+ uint16_t apparentMode = szProto != nullptr ? db_get_w(hContact, szProto, "ApparentMode", 0) : 0;
+ if (apparentMode == ID_STATUS_OFFLINE)
+ cc->flags |= CONTACTF_INVISTO;
+ else if (apparentMode == ID_STATUS_ONLINE)
+ cc->flags |= CONTACTF_VISTO;
+ else if (apparentMode)
+ cc->flags |= CONTACTF_VISTO | CONTACTF_INVISTO;
+ if (!Contact::OnList(hContact))
+ cc->flags |= CONTACTF_NOTONLIST;
+ uint32_t idleMode = szProto != nullptr ? db_get_dw(hContact, szProto, "IdleTS", 0) : 0;
+ if (idleMode)
+ cc->flags |= CONTACTF_IDLE;
+ mir_wstrncpy(cc->szText, Clist_GetContactDisplayName(hContact), _countof(cc->szText));
+ return cc;
+}
+
+void fnAddContactToTree(HWND hwnd, ClcData *dat, MCONTACT hContact, int updateTotalCount, int checkHideOffline)
+{
+ uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ uint16_t status = ID_STATUS_OFFLINE;
+ char *szProto = Proto_GetBaseAccountName(hContact);
+
+ dat->bNeedsResort = true;
+ if (style & CLS_NOHIDEOFFLINE)
+ checkHideOffline = 0;
+ if (checkHideOffline)
+ if (szProto != nullptr)
+ status = db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE);
+
+ int i;
+ uint32_t groupFlags;
+ ClcGroup *group;
+ ptrW tszGroup(Clist_GetGroup(hContact));
+ if (tszGroup == nullptr)
+ group = &dat->list;
+ else {
+ group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, (uint32_t)-1, 0, 0);
+ if (group == nullptr) {
+ if (!(style & CLS_HIDEEMPTYGROUPS))
+ return;
+
+ if (checkHideOffline && Clist_IsHiddenMode(dat, status)) {
+ for (i = 1;; i++) {
+ wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags);
+ if (szGroupName == nullptr)
+ return;
+
+ if (!mir_wstrcmp(szGroupName, tszGroup))
+ break;
+ }
+ if (groupFlags & GROUPF_HIDEOFFLINE)
+ return;
+ }
+ for (i = 1;; i++) {
+ wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags);
+ if (szGroupName == nullptr)
+ return;
+
+ if (!mir_wstrcmp(szGroupName, tszGroup))
+ break;
+
+ size_t len = mir_wstrlen(szGroupName);
+ if (!wcsncmp(szGroupName, tszGroup, len) && tszGroup[len] == '\\')
+ g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 1);
+ }
+ group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, groupFlags, i, 1);
+ }
+ }
+
+ if (checkHideOffline) {
+ if (Clist_IsHiddenMode(dat, status) && (style & CLS_HIDEOFFLINE || group->hideOffline)) {
+ if (updateTotalCount)
+ group->totalMembers++;
+ return;
+ }
+ }
+ g_clistApi.pfnAddContactToGroup(dat, group, hContact);
+ if (updateTotalCount)
+ group->totalMembers++;
+}
+
+MIR_APP_DLL(ClcGroup*) Clist_RemoveItemFromGroup(HWND hwnd, ClcGroup *group, ClcContact *contact, int updateTotalCount)
+{
+ int iContact = group->cl.indexOf(contact);
+ if (iContact == -1)
+ return group;
+
+ if (contact->type == CLCIT_CONTACT) {
+ if (updateTotalCount)
+ group->totalMembers--;
+
+ g_clistApi.pfnInvalidateDisplayNameCacheEntry(contact->hContact);
+ }
+
+ g_clistApi.pfnFreeContact(group->cl[iContact]);
+ group->cl.remove(iContact);
+
+ if ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) && group->cl.getCount() == 0 && group->parent != nullptr)
+ for (auto &cc : group->parent->cl)
+ if (cc->type == CLCIT_GROUP && cc->groupId == group->groupId)
+ return Clist_RemoveItemFromGroup(hwnd, group->parent, cc, 0);
+
+ return group;
+}
+
+MIR_APP_DLL(void) Clist_DeleteItemFromTree(HWND hwnd, MCONTACT hItem)
+{
+ ClcData *dat = (ClcData*)GetWindowLongPtr(hwnd, 0);
+ dat->bNeedsResort = true;
+
+ // if a contact is found in our contact list, remove it from its group and detach from cache
+ ClcGroup *group;
+ ClcContact *contact;
+ if (Clist_FindItem(hwnd, dat, hItem, &contact, &group)) {
+ Clist_RemoveItemFromGroup(hwnd, group, contact, 1);
+ contact->pce = &nullpce;
+ return;
+ }
+
+ // if we don't have this contact, simply try to update the number of contacts in a group
+ if (!IsHContactContact(hItem))
+ return;
+
+ ptrW wszGroup(Clist_GetGroup(hItem));
+ if (wszGroup == nullptr)
+ return;
+
+ // decrease member counts of all parent groups too
+ group = &dat->list;
+ int nameOffset = 0;
+ for (int i = 0;; i++) {
+ if (group->scanIndex == group->cl.getCount())
+ break;
+
+ ClcContact *cc = group->cl[i];
+ if (cc->type == CLCIT_GROUP) {
+ size_t len = mir_wstrlen(cc->szText);
+ if (!wcsncmp(cc->szText, wszGroup.get() + nameOffset, len) && (wszGroup[nameOffset + len] == '\\' || wszGroup[nameOffset + len] == '\0')) {
+ group->totalMembers--;
+ if (wszGroup[nameOffset + len] == '\0')
+ break;
+ }
+ }
+ }
+}
+
+int fnGetContactHiddenStatus(MCONTACT hContact, char*, ClcData*)
+{
+ return Contact::IsHidden(hContact);
+}
+
+void fnRebuildEntireList(HWND hwnd, ClcData *dat)
+{
+ uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE);
+
+ dat->list.expanded = 1;
+ dat->list.hideOffline = db_get_b(0, "CLC", "HideOfflineRoot", 0) && (style & CLS_USEGROUPS);
+ dat->list.cl.destroy();
+ dat->list.totalMembers = 0;
+ dat->selection = -1;
+
+ for (int i = 1;; i++) {
+ uint32_t groupFlags;
+ wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags);
+ if (szGroupName == nullptr)
+ break;
+ g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 0);
+ }
+
+ for (auto &hContact : Contacts()) {
+ int nHiddenStatus = g_clistApi.pfnGetContactHiddenStatus(hContact, nullptr, dat);
+ if (((style & CLS_SHOWHIDDEN) && nHiddenStatus != -1) || !nHiddenStatus) {
+ ClcCacheEntry *pce = Clist_GetCacheEntry(hContact);
+ if (pce->szProto == nullptr)
+ continue;
+
+ ClcGroup *group;
+ ptrW tszGroupName(Clist_GetGroup(hContact));
+ if (tszGroupName == nullptr)
+ group = &dat->list;
+ else {
+ group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroupName, (uint32_t)-1, 0, 0);
+ if (group == nullptr && style & CLS_SHOWHIDDEN)
+ group = &dat->list;
+ }
+
+ if (group != nullptr) {
+ group->totalMembers++;
+
+ if (dat->bFilterSearch && dat->szQuickSearch[0] != '\0') {
+ wchar_t *name = Clist_GetContactDisplayName(hContact);
+ wchar_t *lowered_name = CharLowerW(NEWWSTR_ALLOCA(name));
+ wchar_t *lowered_search = CharLowerW(NEWWSTR_ALLOCA(dat->szQuickSearch));
+
+ if (wcsstr(lowered_name, lowered_search))
+ g_clistApi.pfnAddContactToGroup(dat, group, hContact);
+ }
+ else if (!(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline)) {
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto == nullptr) {
+ if (!Clist_IsHiddenMode(dat, ID_STATUS_OFFLINE) || g_clistApi.pfnIsVisibleContact(pce, group))
+ g_clistApi.pfnAddContactToGroup(dat, group, hContact);
+ }
+ else if (!Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE)) || g_clistApi.pfnIsVisibleContact(pce, group))
+ g_clistApi.pfnAddContactToGroup(dat, group, hContact);
+ }
+ else g_clistApi.pfnAddContactToGroup(dat, group, hContact);
+ }
+ }
+ }
+
+ if (style & CLS_HIDEEMPTYGROUPS) {
+ ClcGroup *group = &dat->list;
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if ((group = group->parent) == nullptr)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *cc = group->cl[group->scanIndex];
+ if (cc->type == CLCIT_GROUP) {
+ if (cc->group->cl.getCount() == 0) {
+ group = Clist_RemoveItemFromGroup(hwnd, group, cc, 0);
+ }
+ else {
+ group = cc->group;
+ group->scanIndex = 0;
+ }
+ continue;
+ }
+ group->scanIndex++;
+ }
+ }
+
+ g_clistApi.pfnSortCLC(hwnd, dat, 0);
+ ExtraIcon_SetAll();
+}
+
+int fnGetGroupContentsCount(ClcGroup *group, int visibleOnly)
+{
+ int count = group->cl.getCount();
+ ClcGroup *topgroup = group;
+
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if (group == topgroup)
+ break;
+ group = group->parent;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *cc = group->cl[group->scanIndex];
+ if (cc->type == CLCIT_GROUP && (!visibleOnly || cc->group->expanded)) {
+ group = cc->group;
+ group->scanIndex = 0;
+ count += group->cl.getCount();
+ continue;
+ }
+ group->scanIndex++;
+ }
+ return count;
+}
+
+static int __cdecl GroupSortProc(const void* p1, const void* p2)
+{
+ ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2;
+
+ return mir_wstrcmpi(contact1[0]->szText, contact2[0]->szText);
+}
+
+static int __cdecl ContactSortProc(const void* p1, const void* p2)
+{
+ ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2;
+
+ int result = g_clistApi.pfnCompareContacts(contact1[0], contact2[0]);
+ if (result)
+ return result;
+ //nothing to distinguish them, so make sure they stay in the same order
+ return (int)((INT_PTR)contact2[0]->hContact - (INT_PTR)contact1[0]->hContact);
+}
+
+static void InsertionSort(ClcContact **pContactArray, int nArray, int(*CompareProc) (const void *, const void *))
+{
+ int i, j;
+ ClcContact* testElement;
+
+ for (i = 1; i < nArray; i++) {
+ if (CompareProc(&pContactArray[i - 1], &pContactArray[i]) > 0) {
+ testElement = pContactArray[i];
+ for (j = i - 2; j >= 0; j--)
+ if (CompareProc(&pContactArray[j], &testElement) <= 0)
+ break;
+ j++;
+ memmove(&pContactArray[j + 1], &pContactArray[j], sizeof(void*) * (i - j));
+ pContactArray[j] = testElement;
+ }
+ }
+}
+
+static void SortGroup(ClcData *dat, ClcGroup *group, int useInsertionSort)
+{
+ int i, sortCount;
+
+ for (i = group->cl.getCount() - 1; i >= 0; i--) {
+ if (group->cl[i]->type == CLCIT_DIVIDER) {
+ mir_free(group->cl[i]);
+ group->cl.remove(i);
+ }
+ }
+
+ for (i = 0; i < group->cl.getCount(); i++)
+ if (group->cl[i]->type != CLCIT_INFO)
+ break;
+ if (i > group->cl.getCount() - 2)
+ return;
+ if (group->cl[i]->type == CLCIT_GROUP) {
+ if (dat->exStyle & CLS_EX_SORTGROUPSALPHA) {
+ for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++)
+ if (group->cl[i + sortCount]->type != CLCIT_GROUP)
+ break;
+ qsort(group->cl.getArray() + i, sortCount, sizeof(void*), GroupSortProc);
+ i = i + sortCount;
+ }
+ for (; i < group->cl.getCount(); i++)
+ if (group->cl[i]->type == CLCIT_CONTACT)
+ break;
+ if (group->cl.getCount() - i < 2)
+ return;
+ }
+ for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++)
+ if (group->cl[i + sortCount]->type != CLCIT_CONTACT)
+ break;
+ if (useInsertionSort)
+ InsertionSort(group->cl.getArray() + i, sortCount, ContactSortProc);
+ else
+ qsort(group->cl.getArray() + i, sortCount, sizeof(void*), ContactSortProc);
+ if (dat->exStyle & CLS_EX_DIVIDERONOFF) {
+ int prevContactOnline = 0;
+ for (i = 0; i < group->cl.getCount(); i++) {
+ if (group->cl[i]->type != CLCIT_CONTACT)
+ continue;
+ if (group->cl[i]->flags & CONTACTF_ONLINE)
+ prevContactOnline = 1;
+ else {
+ if (prevContactOnline) {
+ ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i);
+ cc->type = CLCIT_DIVIDER;
+ mir_wstrcpy(cc->szText, TranslateT("Offline"));
+ }
+ break;
+ }
+ }
+ }
+}
+
+void fnSortCLC(HWND hwnd, ClcData *dat, int useInsertionSort)
+{
+ ClcGroup *group = &dat->list;
+
+ if (dat->bNeedsResort) {
+ MCONTACT hSelItem;
+ ClcContact *selcontact;
+ if (g_clistApi.pfnGetRowByIndex(dat, dat->selection, &selcontact, nullptr) == -1)
+ hSelItem = 0;
+ else
+ hSelItem = Clist_ContactToHItem(selcontact);
+ group->scanIndex = 0;
+ SortGroup(dat, group, useInsertionSort);
+ for (;;) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if ((group = group->parent) == nullptr)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *cc = group->cl[group->scanIndex];
+ if (cc->type == CLCIT_GROUP) {
+ group = cc->group;
+ group->scanIndex = 0;
+ SortGroup(dat, group, useInsertionSort);
+ continue;
+ }
+ group->scanIndex++;
+ }
+
+ if (hSelItem) {
+ ClcGroup *selgroup;
+ if (Clist_FindItem(hwnd, dat, hSelItem, &selcontact, &selgroup))
+ dat->selection = g_clistApi.pfnGetRowsPriorTo(&dat->list, selgroup, selgroup->cl.indexOf(selcontact));
+ }
+
+ g_clistApi.pfnRecalcScrollBar(hwnd, dat);
+ }
+ dat->bNeedsResort = false;
+ g_clistApi.pfnInvalidateRect(hwnd, nullptr, FALSE);
+}
+
+struct SavedContactState_t
+{
+ MCONTACT hContact;
+ uint16_t iExtraImage[EXTRA_ICON_COUNT];
+ int checked;
+};
+
+struct SavedGroupState_t
+{
+ int groupId, expanded;
+};
+
+struct SavedInfoState_t
+{
+ int parentId;
+ ClcContact contact;
+};
+
+MIR_APP_DLL(void) Clist_SaveStateAndRebuildList(HWND hwnd, ClcData *dat)
+{
+ Clist_HideInfoTip(dat);
+ KillTimer(hwnd, TIMERID_INFOTIP);
+ KillTimer(hwnd, TIMERID_RENAME);
+ Clist_EndRename(dat, 1);
+
+ dat->bLockScrollbar = true;
+
+ OBJLIST<SavedContactState_t> saveContact(10, NumericKeySortT);
+ OBJLIST<SavedGroupState_t> saveGroup(100, NumericKeySortT);
+ OBJLIST<SavedInfoState_t> saveInfo(10, NumericKeySortT);
+
+ dat->bNeedsResort = true;
+ ClcGroup *group = &dat->list;
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if ((group = group->parent) == nullptr)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *cc = group->cl[group->scanIndex];
+ if (cc->type == CLCIT_GROUP) {
+ group = cc->group;
+ group->scanIndex = 0;
+
+ SavedGroupState_t *p = new SavedGroupState_t;
+ p->groupId = group->groupId;
+ p->expanded = group->expanded;
+ saveGroup.insert(p);
+ continue;
+ }
+ else if (cc->type == CLCIT_CONTACT) {
+ SavedContactState_t *p = new SavedContactState_t;
+ p->hContact = cc->hContact;
+ memcpy(p->iExtraImage, cc->iExtraImage, sizeof(p->iExtraImage));
+ p->checked = cc->flags & CONTACTF_CHECKED;
+ saveContact.insert(p);
+ }
+ else if (cc->type == CLCIT_INFO) {
+ SavedInfoState_t *p = new SavedInfoState_t;
+ p->parentId = (group->parent == nullptr) ? -1 : group->groupId;
+ p->contact = *cc;
+ saveInfo.insert(p);
+ }
+ group->scanIndex++;
+ }
+
+ FreeGroup(&dat->list);
+ g_clistApi.pfnRebuildEntireList(hwnd, dat);
+
+ group = &dat->list;
+ group->scanIndex = 0;
+ for (;;) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if ((group = group->parent) == nullptr)
+ break;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *cc = group->cl[group->scanIndex];
+ if (cc->type == CLCIT_GROUP) {
+ group = cc->group;
+ group->scanIndex = 0;
+
+ SavedGroupState_t tmp, *p;
+ tmp.groupId = group->groupId;
+ if ((p = saveGroup.find(&tmp)) != nullptr)
+ group->expanded = p->expanded;
+ continue;
+ }
+ else if (cc->type == CLCIT_CONTACT) {
+ SavedContactState_t tmp, *p;
+ tmp.hContact = cc->hContact;
+ if ((p = saveContact.find(&tmp)) != nullptr) {
+ memcpy(cc->iExtraImage, p->iExtraImage, sizeof(p->iExtraImage));
+ if (p->checked)
+ cc->flags |= CONTACTF_CHECKED;
+ }
+ }
+
+ group->scanIndex++;
+ }
+
+ for (auto &it : saveInfo) {
+ if (it->parentId == -1)
+ group = &dat->list;
+ else {
+ ClcContact *contact;
+ if (!Clist_FindItem(hwnd, dat, it->parentId | HCONTACT_ISGROUP, &contact))
+ continue;
+ group = contact->group;
+ }
+
+ ClcContact *cc = g_clistApi.pfnAddInfoItemToGroup(group, it->contact.flags, L"");
+ *cc = it->contact;
+ }
+
+ dat->bLockScrollbar = false;
+ Clist_RecalculateGroupCheckboxes(dat);
+
+ g_clistApi.pfnRecalcScrollBar(hwnd, dat);
+
+ NMCLISTCONTROL nm;
+ nm.hdr.code = CLN_LISTREBUILT;
+ nm.hdr.hwndFrom = hwnd;
+ nm.hdr.idFrom = GetDlgCtrlID(hwnd);
+ SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm);
+}
diff --git a/src/mir_app/src/clcmsgs.cpp b/src/mir_app/src/clcmsgs.cpp index 49267f5cdb..5b92d1a0f0 100644 --- a/src/mir_app/src/clcmsgs.cpp +++ b/src/mir_app/src/clcmsgs.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clcutils.cpp b/src/mir_app/src/clcutils.cpp index c3849572cb..1cd238ffc6 100644 --- a/src/mir_app/src/clcutils.cpp +++ b/src/mir_app/src/clcutils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clistcore.cpp b/src/mir_app/src/clistcore.cpp index 27372c0101..ebfa53dda6 100644 --- a/src/mir_app/src/clistcore.cpp +++ b/src/mir_app/src/clistcore.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clistevents.cpp b/src/mir_app/src/clistevents.cpp index 0533b5b1c7..4e5455ebc2 100644 --- a/src/mir_app/src/clistevents.cpp +++ b/src/mir_app/src/clistevents.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clistgroups.cpp b/src/mir_app/src/clistgroups.cpp index e91b5686d1..79d320515b 100644 --- a/src/mir_app/src/clistgroups.cpp +++ b/src/mir_app/src/clistgroups.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clistmod.cpp b/src/mir_app/src/clistmod.cpp index 6a205db148..19b9a8a860 100644 --- a/src/mir_app/src/clistmod.cpp +++ b/src/mir_app/src/clistmod.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clistopts.cpp b/src/mir_app/src/clistopts.cpp index 169ee85b6b..8df408c45e 100644 --- a/src/mir_app/src/clistopts.cpp +++ b/src/mir_app/src/clistopts.cpp @@ -1,162 +1,162 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "clc.h" - -CMOption<bool> Clist::UseGroups(MODULENAME, "UseGroups", true); -CMOption<bool> Clist::HideOffline(MODULENAME, "HideOffline", false); -CMOption<bool> Clist::ConfirmDelete(MODULENAME, "ConfirmDelete", true); -CMOption<bool> Clist::EnableIconBlink(MODULENAME, "EnableIconBlink", true); -CMOption<bool> Clist::EnableTrayFlash(MODULENAME, "EnableTrayFlash", true); -CMOption<bool> Clist::HideEmptyGroups(MODULENAME, "HideEmptyGroups", false); -CMOption<bool> Clist::RemoveTempContacts(MODULENAME, "RemoveTempContacts", true); - -CMOption<bool> Clist::Tray1Click(MODULENAME, "Tray1Click", IsWinVer7Plus()); -CMOption<bool> Clist::TrayAlwaysStatus(MODULENAME, "AlwaysStatus", false); - -CMOption<bool> Clist::FilterSearch("CLC", "FilterSearch", false); -CMOption<uint32_t> Clist::OfflineModes("CLC", "OfflineModes", MODEF_OFFLINE); - -struct -{ - uint32_t style; - wchar_t *szDescr; -} -static const offlineValues[] = -{ - { MODEF_OFFLINE, LPGENW("Offline") }, - { PF2_ONLINE, LPGENW("Online") }, - { PF2_SHORTAWAY, LPGENW("Away") }, - { PF2_LONGAWAY, LPGENW("Not available") }, - { PF2_LIGHTDND, LPGENW("Occupied") }, - { PF2_HEAVYDND, LPGENW("Do not disturb") }, - { PF2_FREECHAT, LPGENW("Free for chat") }, - { PF2_INVISIBLE, LPGENW("Invisible") } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -class ClistCommonOptsDlg : public CDlgBase -{ - CCtrlSpin blink; - CCtrlCheck chkUseGroups, chkHideOffline, chkConfirmDelete, chkHideEmptyGroups, chkRemoveTempContacts, chkEnableIconBlink, chkFilterSearch; - CCtrlCheck chkAlwaysStatus, chkOneClick, chkEnableTrayBlink; - CCtrlTreeView hideStatuses; - -public: - ClistCommonOptsDlg() : - CDlgBase(g_plugin, IDD_OPT_CLIST), - blink(this, IDC_BLINKSPIN, 0x3FFF, 250), - hideStatuses(this, IDC_HIDEOFFLINEOPTS), - chkOneClick(this, IDC_ONECLK), - chkUseGroups(this, IDC_USEGROUPS), - chkHideOffline(this, IDC_HIDEOFFLINE), - chkFilterSearch(this, IDC_FILTER_SEARCH), - chkAlwaysStatus(this, IDC_ALWAYSSTATUS), - chkConfirmDelete(this, IDC_CONFIRMDELETE), - chkHideEmptyGroups(this, IDC_HIDEEMPTYGROUPS), - chkEnableIconBlink(this, IDC_ENABLE_ICON_BLINK), - chkEnableTrayBlink(this, IDC_ENABLE_TRAY_BLINK), - chkRemoveTempContacts(this, IDC_REMOVETEMP) - { - chkEnableTrayBlink.OnChange = Callback(this, &ClistCommonOptsDlg::onChange_TrayBlink); - - CreateLink(chkOneClick, Clist::Tray1Click); - CreateLink(chkUseGroups, Clist::UseGroups); - CreateLink(chkHideOffline, Clist::HideOffline); - CreateLink(chkFilterSearch, Clist::FilterSearch); - CreateLink(chkAlwaysStatus, Clist::TrayAlwaysStatus); - CreateLink(chkConfirmDelete, Clist::ConfirmDelete); - CreateLink(chkHideEmptyGroups, Clist::HideEmptyGroups); - CreateLink(chkEnableIconBlink, Clist::EnableIconBlink); - CreateLink(chkEnableTrayBlink, Clist::EnableTrayFlash); - CreateLink(chkRemoveTempContacts, Clist::RemoveTempContacts); - } - - bool OnInitDialog() override - { - blink.SetPosition(db_get_w(0, MODULENAME, "IconFlashTime", 550)); - - SetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE, - GetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES); - - int style = Clist::OfflineModes; - - TVINSERTSTRUCT tvis; - tvis.hParent = nullptr; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE; - for (auto &it : offlineValues) { - tvis.item.lParam = it.style; - tvis.item.pszText = TranslateW(it.szDescr); - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - tvis.item.state = INDEXTOSTATEIMAGEMASK((style & it.style) != 0 ? 2 : 1); - hideStatuses.InsertItem(&tvis); - } - return true; - } - - bool OnApply() override - { - db_set_w(0, MODULENAME, "IconFlashTime", blink.GetPosition()); - - uint32_t flags = 0; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE; - tvi.hItem = hideStatuses.GetRoot(); - while (tvi.hItem) { - hideStatuses.GetItem(&tvi); - if ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2) - flags |= tvi.lParam; - tvi.hItem = hideStatuses.GetNextSibling(tvi.hItem); - } - Clist::OfflineModes = flags; - - Clist_ClcOptionsChanged(); - Clist_LoadContactTree(); - Clist_InitAutoRebuild(g_clistApi.hwndContactTree); - return true; - } - - void onChange_TrayBlink(CCtrlCheck*) - { - bool bEnabled = chkEnableTrayBlink.IsChecked(); - EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKTIME), bEnabled); - EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKSPIN), bEnabled); - } -}; - -int ClcOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = -200000000; - odp.szGroup.a = LPGEN("Contact list"); - odp.szTitle.a = LPGEN("Common"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new ClistCommonOptsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "clc.h"
+
+CMOption<bool> Clist::UseGroups(MODULENAME, "UseGroups", true);
+CMOption<bool> Clist::HideOffline(MODULENAME, "HideOffline", false);
+CMOption<bool> Clist::ConfirmDelete(MODULENAME, "ConfirmDelete", true);
+CMOption<bool> Clist::EnableIconBlink(MODULENAME, "EnableIconBlink", true);
+CMOption<bool> Clist::EnableTrayFlash(MODULENAME, "EnableTrayFlash", true);
+CMOption<bool> Clist::HideEmptyGroups(MODULENAME, "HideEmptyGroups", false);
+CMOption<bool> Clist::RemoveTempContacts(MODULENAME, "RemoveTempContacts", true);
+
+CMOption<bool> Clist::Tray1Click(MODULENAME, "Tray1Click", IsWinVer7Plus());
+CMOption<bool> Clist::TrayAlwaysStatus(MODULENAME, "AlwaysStatus", false);
+
+CMOption<bool> Clist::FilterSearch("CLC", "FilterSearch", false);
+CMOption<uint32_t> Clist::OfflineModes("CLC", "OfflineModes", MODEF_OFFLINE);
+
+struct
+{
+ uint32_t style;
+ wchar_t *szDescr;
+}
+static const offlineValues[] =
+{
+ { MODEF_OFFLINE, LPGENW("Offline") },
+ { PF2_ONLINE, LPGENW("Online") },
+ { PF2_SHORTAWAY, LPGENW("Away") },
+ { PF2_LONGAWAY, LPGENW("Not available") },
+ { PF2_LIGHTDND, LPGENW("Occupied") },
+ { PF2_HEAVYDND, LPGENW("Do not disturb") },
+ { PF2_FREECHAT, LPGENW("Free for chat") },
+ { PF2_INVISIBLE, LPGENW("Invisible") }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class ClistCommonOptsDlg : public CDlgBase
+{
+ CCtrlSpin blink;
+ CCtrlCheck chkUseGroups, chkHideOffline, chkConfirmDelete, chkHideEmptyGroups, chkRemoveTempContacts, chkEnableIconBlink, chkFilterSearch;
+ CCtrlCheck chkAlwaysStatus, chkOneClick, chkEnableTrayBlink;
+ CCtrlTreeView hideStatuses;
+
+public:
+ ClistCommonOptsDlg() :
+ CDlgBase(g_plugin, IDD_OPT_CLIST),
+ blink(this, IDC_BLINKSPIN, 0x3FFF, 250),
+ hideStatuses(this, IDC_HIDEOFFLINEOPTS),
+ chkOneClick(this, IDC_ONECLK),
+ chkUseGroups(this, IDC_USEGROUPS),
+ chkHideOffline(this, IDC_HIDEOFFLINE),
+ chkFilterSearch(this, IDC_FILTER_SEARCH),
+ chkAlwaysStatus(this, IDC_ALWAYSSTATUS),
+ chkConfirmDelete(this, IDC_CONFIRMDELETE),
+ chkHideEmptyGroups(this, IDC_HIDEEMPTYGROUPS),
+ chkEnableIconBlink(this, IDC_ENABLE_ICON_BLINK),
+ chkEnableTrayBlink(this, IDC_ENABLE_TRAY_BLINK),
+ chkRemoveTempContacts(this, IDC_REMOVETEMP)
+ {
+ chkEnableTrayBlink.OnChange = Callback(this, &ClistCommonOptsDlg::onChange_TrayBlink);
+
+ CreateLink(chkOneClick, Clist::Tray1Click);
+ CreateLink(chkUseGroups, Clist::UseGroups);
+ CreateLink(chkHideOffline, Clist::HideOffline);
+ CreateLink(chkFilterSearch, Clist::FilterSearch);
+ CreateLink(chkAlwaysStatus, Clist::TrayAlwaysStatus);
+ CreateLink(chkConfirmDelete, Clist::ConfirmDelete);
+ CreateLink(chkHideEmptyGroups, Clist::HideEmptyGroups);
+ CreateLink(chkEnableIconBlink, Clist::EnableIconBlink);
+ CreateLink(chkEnableTrayBlink, Clist::EnableTrayFlash);
+ CreateLink(chkRemoveTempContacts, Clist::RemoveTempContacts);
+ }
+
+ bool OnInitDialog() override
+ {
+ blink.SetPosition(db_get_w(0, MODULENAME, "IconFlashTime", 550));
+
+ SetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE,
+ GetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+
+ int style = Clist::OfflineModes;
+
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = nullptr;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
+ for (auto &it : offlineValues) {
+ tvis.item.lParam = it.style;
+ tvis.item.pszText = TranslateW(it.szDescr);
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.state = INDEXTOSTATEIMAGEMASK((style & it.style) != 0 ? 2 : 1);
+ hideStatuses.InsertItem(&tvis);
+ }
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ db_set_w(0, MODULENAME, "IconFlashTime", blink.GetPosition());
+
+ uint32_t flags = 0;
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
+ tvi.hItem = hideStatuses.GetRoot();
+ while (tvi.hItem) {
+ hideStatuses.GetItem(&tvi);
+ if ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2)
+ flags |= tvi.lParam;
+ tvi.hItem = hideStatuses.GetNextSibling(tvi.hItem);
+ }
+ Clist::OfflineModes = flags;
+
+ Clist_ClcOptionsChanged();
+ Clist_LoadContactTree();
+ Clist_InitAutoRebuild(g_clistApi.hwndContactTree);
+ return true;
+ }
+
+ void onChange_TrayBlink(CCtrlCheck*)
+ {
+ bool bEnabled = chkEnableTrayBlink.IsChecked();
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKTIME), bEnabled);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKSPIN), bEnabled);
+ }
+};
+
+int ClcOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = -200000000;
+ odp.szGroup.a = LPGEN("Contact list");
+ odp.szTitle.a = LPGEN("Common");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new ClistCommonOptsDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/mir_app/src/clistsettings.cpp b/src/mir_app/src/clistsettings.cpp index 6b160e0046..4d3e60b9d4 100644 --- a/src/mir_app/src/clistsettings.cpp +++ b/src/mir_app/src/clistsettings.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clisttray.cpp b/src/mir_app/src/clisttray.cpp index 9d4137748b..c10100a1b3 100644 --- a/src/mir_app/src/clisttray.cpp +++ b/src/mir_app/src/clisttray.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/clui.cpp b/src/mir_app/src/clui.cpp index e5201fb244..39015a1819 100644 --- a/src/mir_app/src/clui.cpp +++ b/src/mir_app/src/clui.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/cluiservices.cpp b/src/mir_app/src/cluiservices.cpp index dc17de4dfb..ae6d2c66de 100644 --- a/src/mir_app/src/cluiservices.cpp +++ b/src/mir_app/src/cluiservices.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/colorchooser.cpp b/src/mir_app/src/colorchooser.cpp index 2931d9f3cf..6a93e61f54 100644 --- a/src/mir_app/src/colorchooser.cpp +++ b/src/mir_app/src/colorchooser.cpp @@ -1,7 +1,7 @@ /*
Chat module plugin for Miranda IM
-Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team,
+Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/contacts.cpp b/src/mir_app/src/contacts.cpp index 3a4c32b18b..8cec2a273a 100644 --- a/src/mir_app/src/contacts.cpp +++ b/src/mir_app/src/contacts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/copyright.h b/src/mir_app/src/copyright.h index 29b0f97206..641ed66f54 100644 --- a/src/mir_app/src/copyright.h +++ b/src/mir_app/src/copyright.h @@ -1,2 +1,2 @@ - -#define LEGAL_COPYRIGHT "Copyright © 2000-11 Miranda IM, 2012-22 Miranda NG team. This software is released under the terms of the GNU General Public License.\0" +
+#define LEGAL_COPYRIGHT "Copyright © 2000-11 Miranda IM, 2012-23 Miranda NG team. This software is released under the terms of the GNU General Public License.\0"
diff --git a/src/mir_app/src/database.cpp b/src/mir_app/src/database.cpp index d20361c3b4..149377bc27 100644 --- a/src/mir_app/src/database.cpp +++ b/src/mir_app/src/database.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/database.h b/src/mir_app/src/database.h index 11d5b1b58b..07f32d6bad 100644 --- a/src/mir_app/src/database.h +++ b/src/mir_app/src/database.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright 2012-22 Miranda NG team,
+Copyright 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/db_events.cpp b/src/mir_app/src/db_events.cpp index 68049037f1..b6bce698d4 100644 --- a/src/mir_app/src/db_events.cpp +++ b/src/mir_app/src/db_events.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/db_ini.cpp b/src/mir_app/src/db_ini.cpp index 8ce8f88385..6953345787 100644 --- a/src/mir_app/src/db_ini.cpp +++ b/src/mir_app/src/db_ini.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/db_intf.cpp b/src/mir_app/src/db_intf.cpp index fc9467d8fd..a422ab7b25 100644 --- a/src/mir_app/src/db_intf.cpp +++ b/src/mir_app/src/db_intf.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/db_upgrade.cpp b/src/mir_app/src/db_upgrade.cpp index c96f638125..2b6f6d7755 100644 --- a/src/mir_app/src/db_upgrade.cpp +++ b/src/mir_app/src/db_upgrade.cpp @@ -1,71 +1,71 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define CONVERT_MSG LPGEN("This database is in the old format that isn't supported anymore. Press Yes to convert it to the new format or No to return") -#define MISSING_DB_MSG LPGEN("To open this database you need to install the Dbx_sqlite plugin. Click Yes to download it from Miranda NG's site or No to return") -#define MISSING_PLUG_MSG LPGEN("To open this database you need to install the Import plugin. Click Yes to download it from Miranda NG's site or No to return") - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(MDatabaseCommon*) DB::Upgrade(const wchar_t *profile) -{ - DATABASELINK *pLink = GetDatabasePlugin("dbx_sqlite"); - if (pLink == nullptr) { - if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_DB_MSG), L"Miranda NG", MB_YESNO)) - Utils_OpenUrl("https://miranda-ng.org/p/Dbx_sqlite"); - return nullptr; - } - - if (!Profile_GetSettingInt(L"Database/SilentUpgrade")) - if (IDYES != MessageBoxW(nullptr, TranslateT(CONVERT_MSG), L"Miranda NG", MB_YESNO)) - return nullptr; - - int errorCode; - CMStringW wszBackupName(profile); - wszBackupName.Append(L".bak"); - DeleteFileW(wszBackupName); - if (!MoveFileW(profile, wszBackupName)) { - uint32_t dwError = GetLastError(); - CMStringW wszError(FORMAT, TranslateT("Cannot move old profile '%s' to '%s': error %d"), profile, wszBackupName.c_str(), dwError); - MessageBoxW(nullptr, wszError, L"Miranda NG", MB_ICONERROR | MB_OK); - return nullptr; - } - - if ((errorCode = pLink->makeDatabase(profile)) != 0) { - MessageBoxW(nullptr, CMStringW(FORMAT, TranslateT("Attempt to create database '%s' failed with error code %d"), profile, errorCode), L"Miranda NG", MB_ICONERROR | MB_OK); -LBL_Error: - DeleteFileW(profile); - MoveFileW(wszBackupName, profile); - return nullptr; - } - - if (SetServiceModePlugin("import", 1) != ERROR_SUCCESS) { - if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_PLUG_MSG), L"Miranda NG", MB_YESNO)) - Utils_OpenUrl("https://miranda-ng.org/p/Import"); - goto LBL_Error; - } - - return pLink->Load(profile, false); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#define CONVERT_MSG LPGEN("This database is in the old format that isn't supported anymore. Press Yes to convert it to the new format or No to return")
+#define MISSING_DB_MSG LPGEN("To open this database you need to install the Dbx_sqlite plugin. Click Yes to download it from Miranda NG's site or No to return")
+#define MISSING_PLUG_MSG LPGEN("To open this database you need to install the Import plugin. Click Yes to download it from Miranda NG's site or No to return")
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(MDatabaseCommon*) DB::Upgrade(const wchar_t *profile)
+{
+ DATABASELINK *pLink = GetDatabasePlugin("dbx_sqlite");
+ if (pLink == nullptr) {
+ if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_DB_MSG), L"Miranda NG", MB_YESNO))
+ Utils_OpenUrl("https://miranda-ng.org/p/Dbx_sqlite");
+ return nullptr;
+ }
+
+ if (!Profile_GetSettingInt(L"Database/SilentUpgrade"))
+ if (IDYES != MessageBoxW(nullptr, TranslateT(CONVERT_MSG), L"Miranda NG", MB_YESNO))
+ return nullptr;
+
+ int errorCode;
+ CMStringW wszBackupName(profile);
+ wszBackupName.Append(L".bak");
+ DeleteFileW(wszBackupName);
+ if (!MoveFileW(profile, wszBackupName)) {
+ uint32_t dwError = GetLastError();
+ CMStringW wszError(FORMAT, TranslateT("Cannot move old profile '%s' to '%s': error %d"), profile, wszBackupName.c_str(), dwError);
+ MessageBoxW(nullptr, wszError, L"Miranda NG", MB_ICONERROR | MB_OK);
+ return nullptr;
+ }
+
+ if ((errorCode = pLink->makeDatabase(profile)) != 0) {
+ MessageBoxW(nullptr, CMStringW(FORMAT, TranslateT("Attempt to create database '%s' failed with error code %d"), profile, errorCode), L"Miranda NG", MB_ICONERROR | MB_OK);
+LBL_Error:
+ DeleteFileW(profile);
+ MoveFileW(wszBackupName, profile);
+ return nullptr;
+ }
+
+ if (SetServiceModePlugin("import", 1) != ERROR_SUCCESS) {
+ if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_PLUG_MSG), L"Miranda NG", MB_YESNO))
+ Utils_OpenUrl("https://miranda-ng.org/p/Import");
+ goto LBL_Error;
+ }
+
+ return pLink->Load(profile, false);
+}
diff --git a/src/mir_app/src/db_util.cpp b/src/mir_app/src/db_util.cpp index fc23a10050..6fd23cb4f5 100644 --- a/src/mir_app/src/db_util.cpp +++ b/src/mir_app/src/db_util.cpp @@ -1,129 +1,129 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "profilemanager.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Profile_GetPathA(size_t cbLen, char *pszDest) -{ - if (!pszDest || !cbLen) - return 1; - - strncpy_s(pszDest, cbLen, _T2A(g_profileDir), _TRUNCATE); - return 0; -} - -MIR_APP_DLL(int) Profile_GetPathW(size_t cbLen, wchar_t *pwszDest) -{ - if (!pwszDest || !cbLen) - return 1; - - wcsncpy_s(pwszDest, cbLen, g_profileDir, _TRUNCATE); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Profile_GetNameA(size_t cbLen, char *pszDest) -{ - if (!cbLen || !pszDest) - return 1; - - strncpy_s(pszDest, cbLen, ptrA(makeFileName(g_profileName)), _TRUNCATE); - return 0; -} - -MIR_APP_DLL(int) Profile_GetNameW(size_t cbLen, wchar_t *pwszDest) -{ - if (!cbLen || !pwszDest) - return 1; - - wcsncpy_s(pwszDest, cbLen, g_profileName, _TRUNCATE); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(bool) Profile_GetSetting(const wchar_t *pwszSetting, wchar_t *pwszBuf, size_t cbLen, const wchar_t *pwszDefault) -{ - if (pwszSetting == nullptr) { - *pwszBuf = 0; - return false; - } - - if (pwszDefault == nullptr) - pwszDefault = L""; - - wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting); - - wchar_t *p = wcschr(pBuf, '/'); - if (p) { - *p = 0; p++; - GetPrivateProfileStringW(pBuf, p, pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini); - } - else GetPrivateProfileStringW(pBuf, L"", pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini); - - return pwszBuf[0] != 0; -} - -MIR_APP_DLL(int) Profile_GetSettingInt(const wchar_t *pwszSetting, int iDefault) -{ - if (pwszSetting == nullptr) - return iDefault; - - wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting); - - wchar_t *p = wcschr(pBuf, '/'); - if (p) { - *p = 0; p++; - return GetPrivateProfileIntW(pBuf, p, iDefault, mirandabootini); - } - - return GetPrivateProfileIntW(pBuf, L"", iDefault, mirandabootini); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Profile_SetDefault(const wchar_t *pwszPath) -{ - extern wchar_t* g_defaultProfile; - replaceStrW(g_defaultProfile, pwszPath); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(bool) Profile_CheckOpened(const wchar_t *pwszProfileName) -{ - CMStringW wszPhysName(pwszProfileName); - wszPhysName.Replace(L"\\", L"_"); - wszPhysName.Insert(0, L"Global\\"); - - HANDLE hMutex = ::OpenMutexW(SYNCHRONIZE, false, wszPhysName); - if (hMutex == nullptr) - return false; - - ::CloseHandle(hMutex); - return true; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "profilemanager.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Profile_GetPathA(size_t cbLen, char *pszDest)
+{
+ if (!pszDest || !cbLen)
+ return 1;
+
+ strncpy_s(pszDest, cbLen, _T2A(g_profileDir), _TRUNCATE);
+ return 0;
+}
+
+MIR_APP_DLL(int) Profile_GetPathW(size_t cbLen, wchar_t *pwszDest)
+{
+ if (!pwszDest || !cbLen)
+ return 1;
+
+ wcsncpy_s(pwszDest, cbLen, g_profileDir, _TRUNCATE);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Profile_GetNameA(size_t cbLen, char *pszDest)
+{
+ if (!cbLen || !pszDest)
+ return 1;
+
+ strncpy_s(pszDest, cbLen, ptrA(makeFileName(g_profileName)), _TRUNCATE);
+ return 0;
+}
+
+MIR_APP_DLL(int) Profile_GetNameW(size_t cbLen, wchar_t *pwszDest)
+{
+ if (!cbLen || !pwszDest)
+ return 1;
+
+ wcsncpy_s(pwszDest, cbLen, g_profileName, _TRUNCATE);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(bool) Profile_GetSetting(const wchar_t *pwszSetting, wchar_t *pwszBuf, size_t cbLen, const wchar_t *pwszDefault)
+{
+ if (pwszSetting == nullptr) {
+ *pwszBuf = 0;
+ return false;
+ }
+
+ if (pwszDefault == nullptr)
+ pwszDefault = L"";
+
+ wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting);
+
+ wchar_t *p = wcschr(pBuf, '/');
+ if (p) {
+ *p = 0; p++;
+ GetPrivateProfileStringW(pBuf, p, pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini);
+ }
+ else GetPrivateProfileStringW(pBuf, L"", pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini);
+
+ return pwszBuf[0] != 0;
+}
+
+MIR_APP_DLL(int) Profile_GetSettingInt(const wchar_t *pwszSetting, int iDefault)
+{
+ if (pwszSetting == nullptr)
+ return iDefault;
+
+ wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting);
+
+ wchar_t *p = wcschr(pBuf, '/');
+ if (p) {
+ *p = 0; p++;
+ return GetPrivateProfileIntW(pBuf, p, iDefault, mirandabootini);
+ }
+
+ return GetPrivateProfileIntW(pBuf, L"", iDefault, mirandabootini);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(void) Profile_SetDefault(const wchar_t *pwszPath)
+{
+ extern wchar_t* g_defaultProfile;
+ replaceStrW(g_defaultProfile, pwszPath);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(bool) Profile_CheckOpened(const wchar_t *pwszProfileName)
+{
+ CMStringW wszPhysName(pwszProfileName);
+ wszPhysName.Replace(L"\\", L"_");
+ wszPhysName.Insert(0, L"Global\\");
+
+ HANDLE hMutex = ::OpenMutexW(SYNCHRONIZE, false, wszPhysName);
+ if (hMutex == nullptr)
+ return false;
+
+ ::CloseHandle(hMutex);
+ return true;
+}
diff --git a/src/mir_app/src/descbutton.cpp b/src/mir_app/src/descbutton.cpp index ad198bdaf1..4fdd74f856 100644 --- a/src/mir_app/src/descbutton.cpp +++ b/src/mir_app/src/descbutton.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
Copyright (c) 2007 Artem Shpynov
all portions of this codebase are copyrighted to the people
diff --git a/src/mir_app/src/dll_sniffer.cpp b/src/mir_app/src/dll_sniffer.cpp index 037ca328e5..7f2aca1955 100644 --- a/src/mir_app/src/dll_sniffer.cpp +++ b/src/mir_app/src/dll_sniffer.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/ei_baseIcon.cpp b/src/mir_app/src/ei_baseIcon.cpp index 5ddce65bab..7244d20edb 100644 --- a/src/mir_app/src/ei_baseIcon.cpp +++ b/src/mir_app/src/ei_baseIcon.cpp @@ -1,70 +1,70 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -BaseExtraIcon::BaseExtraIcon(const char *name, const wchar_t *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM param) : - ExtraIcon(name), - m_OnClick(OnClick), - m_onClickParam(param), - m_tszDescription(mir_wstrdup(description)), - m_hDescIcon(descIcon) -{ - if (!IcoLib_IsManaged((HICON)descIcon)) - m_hDescIcon = IcoLib_GetIconHandle((const char *)descIcon); - - m_id = registeredExtraIcons.getCount() + 1; -} - -BaseExtraIcon::~BaseExtraIcon() -{ -} - -void BaseExtraIcon::setOnClick(MIRANDAHOOKPARAM pFunc, LPARAM pParam) -{ - m_OnClick = pFunc; - m_onClickParam = pParam; -} - -const wchar_t* BaseExtraIcon::getDescription() const -{ - return TranslateW_LP(m_tszDescription, m_pPlugin); -} - -HANDLE BaseExtraIcon::getDescIcon() const -{ - return m_hDescIcon; -} - -void BaseExtraIcon::onClick(MCONTACT hContact) -{ - if (m_OnClick != nullptr) - m_OnClick(hContact, (LPARAM)ConvertToClistSlot(m_slot), m_onClickParam); -} - -int BaseExtraIcon::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage) -{ - if (m_pParent) - return m_pParent->ClistSetExtraIcon(hContact, hImage); - return Clist_SetExtraIcon(hContact, m_slot, hImage); -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+
+BaseExtraIcon::BaseExtraIcon(const char *name, const wchar_t *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM param) :
+ ExtraIcon(name),
+ m_OnClick(OnClick),
+ m_onClickParam(param),
+ m_tszDescription(mir_wstrdup(description)),
+ m_hDescIcon(descIcon)
+{
+ if (!IcoLib_IsManaged((HICON)descIcon))
+ m_hDescIcon = IcoLib_GetIconHandle((const char *)descIcon);
+
+ m_id = registeredExtraIcons.getCount() + 1;
+}
+
+BaseExtraIcon::~BaseExtraIcon()
+{
+}
+
+void BaseExtraIcon::setOnClick(MIRANDAHOOKPARAM pFunc, LPARAM pParam)
+{
+ m_OnClick = pFunc;
+ m_onClickParam = pParam;
+}
+
+const wchar_t* BaseExtraIcon::getDescription() const
+{
+ return TranslateW_LP(m_tszDescription, m_pPlugin);
+}
+
+HANDLE BaseExtraIcon::getDescIcon() const
+{
+ return m_hDescIcon;
+}
+
+void BaseExtraIcon::onClick(MCONTACT hContact)
+{
+ if (m_OnClick != nullptr)
+ m_OnClick(hContact, (LPARAM)ConvertToClistSlot(m_slot), m_onClickParam);
+}
+
+int BaseExtraIcon::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage)
+{
+ if (m_pParent)
+ return m_pParent->ClistSetExtraIcon(hContact, hImage);
+ return Clist_SetExtraIcon(hContact, m_slot, hImage);
+}
diff --git a/src/mir_app/src/ei_callbackIcon.cpp b/src/mir_app/src/ei_callbackIcon.cpp index a28bd24ed0..205003d054 100644 --- a/src/mir_app/src/ei_callbackIcon.cpp +++ b/src/mir_app/src/ei_callbackIcon.cpp @@ -1,75 +1,75 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -CallbackExtraIcon::CallbackExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon, - MIRANDAHOOK _RebuildIcons, MIRANDAHOOK _ApplyIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) : - BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param), - m_pfnRebuildIcons(_RebuildIcons), m_pfnApplyIcon(_ApplyIcon), m_needToRebuild(true) -{ -} - -CallbackExtraIcon::~CallbackExtraIcon() -{ -} - -int CallbackExtraIcon::getType() const -{ - return EXTRAICON_TYPE_CALLBACK; -} - -void CallbackExtraIcon::rebuildIcons() -{ - if (!isEnabled()) { - m_needToRebuild = true; - return; - } - - m_needToRebuild = false; - m_pfnRebuildIcons(0, 0); -} - -void CallbackExtraIcon::applyIcon(MCONTACT hContact) -{ - if (!isEnabled() || hContact == 0) - return; - - if (m_needToRebuild) - rebuildIcons(); - - m_pfnApplyIcon(hContact, 0); -} - -int CallbackExtraIcon::setIcon(MCONTACT hContact, HANDLE icon) -{ - if (!isEnabled() || hContact == 0) - return -1; - - return ClistSetExtraIcon(hContact, icon); -} - -int CallbackExtraIcon::setIconByName(MCONTACT, const char*) -{ - return -1; -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+
+CallbackExtraIcon::CallbackExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon,
+ MIRANDAHOOK _RebuildIcons, MIRANDAHOOK _ApplyIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) :
+ BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param),
+ m_pfnRebuildIcons(_RebuildIcons), m_pfnApplyIcon(_ApplyIcon), m_needToRebuild(true)
+{
+}
+
+CallbackExtraIcon::~CallbackExtraIcon()
+{
+}
+
+int CallbackExtraIcon::getType() const
+{
+ return EXTRAICON_TYPE_CALLBACK;
+}
+
+void CallbackExtraIcon::rebuildIcons()
+{
+ if (!isEnabled()) {
+ m_needToRebuild = true;
+ return;
+ }
+
+ m_needToRebuild = false;
+ m_pfnRebuildIcons(0, 0);
+}
+
+void CallbackExtraIcon::applyIcon(MCONTACT hContact)
+{
+ if (!isEnabled() || hContact == 0)
+ return;
+
+ if (m_needToRebuild)
+ rebuildIcons();
+
+ m_pfnApplyIcon(hContact, 0);
+}
+
+int CallbackExtraIcon::setIcon(MCONTACT hContact, HANDLE icon)
+{
+ if (!isEnabled() || hContact == 0)
+ return -1;
+
+ return ClistSetExtraIcon(hContact, icon);
+}
+
+int CallbackExtraIcon::setIconByName(MCONTACT, const char*)
+{
+ return -1;
+}
diff --git a/src/mir_app/src/ei_defaulticons.cpp b/src/mir_app/src/ei_defaulticons.cpp index 14c1b2b359..e2eb20cbf0 100644 --- a/src/mir_app/src/ei_defaulticons.cpp +++ b/src/mir_app/src/ei_defaulticons.cpp @@ -1,339 +1,339 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "m_cluiframes.h" - -#include "extraicons.h" -#include "chat.h" - -//////////////////////////////////////////////////////////////////////////////////////// -// DB extra icons - -HANDLE hExtraVisibility, hExtraChat, hExtraChatMute, hExtraGender, hExtraProto; - -static void SetVisibility(MCONTACT hContact, int apparentMode, bool clear) -{ - if (hContact == 0) - return; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return; - - if (apparentMode <= 0) - apparentMode = db_get_w(hContact, proto, "ApparentMode", 0); - - HANDLE hExtraIcon, hIcolib = nullptr; - - // Is chat - if (Contact::IsGroupChat(hContact, proto)) { - hExtraIcon = hExtraChat; - if (apparentMode == ID_STATUS_OFFLINE) - hIcolib = IcoLib_GetIconHandle("ChatActivity"); - } - else { // Not chat - hExtraIcon = hExtraVisibility; - if (apparentMode == ID_STATUS_OFFLINE) - hIcolib = Skin_GetIconHandle(SKINICON_OTHER_INVISIBLE_ALL); - else if (apparentMode == ID_STATUS_ONLINE) - hIcolib = Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL); - } - - if (hIcolib != nullptr || clear) - ExtraIcon_SetIcon(hExtraIcon, hContact, hIcolib); -} - -static void SetGender(MCONTACT hContact, int gender, bool clear) -{ - if (hContact == 0) - return; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return; - - if (gender <= 0) - gender = db_get_b(hContact, proto, "Gender", 0); - if (gender <= 0) - gender = db_get_b(hContact, "UserInfo", "Gender", 0); - - const char *ico; - if (gender == 'M') - ico = "gender_male"; - else if (gender == 'F') - ico = "gender_female"; - else - ico = nullptr; - - if (ico != nullptr || clear) - ExtraIcon_SetIconByName(hExtraGender, hContact, ico); -} - -static void SetChatMute(MCONTACT hContact, int mode) -{ - if (hContact == 0) - return; - - if (mode == -1) - mode = db_get_b(hContact, "SRMM", "MuteMode", CHATMODE_NORMAL); - - HANDLE hIcon; - switch (mode) { - case CHATMODE_MUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_OFF); break; - case CHATMODE_UNMUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_ON); break; - default: - hIcon = nullptr; break; - } - - ExtraIcon_SetIcon(hExtraChatMute, hContact, hIcon); -} - -struct Info -{ - const char *name; - const char *desc; - int iSkinIcon; - const char *db[8]; - void(*OnClick)(Info *info, const char *text); - int flags; - - HANDLE hIcolib, hExtraIcon; -}; - -static void EmailOnClick(Info*, const char *text) -{ - char cmd[1024]; - mir_snprintf(cmd, "mailto:%s", text); - ShellExecuteA(nullptr, "open", cmd, nullptr, nullptr, SW_SHOW); -} - -static void HomepageOnClick(Info*, const char *text) -{ - ShellExecuteA(nullptr, "open", text, nullptr, nullptr, SW_SHOW); -} - -static Info infos[] = -{ - { "homepage", LPGEN("Homepage"), SKINICON_OTHER_MIRANDAWEB, - { nullptr, "Homepage", "UserInfo", "Homepage" }, - &HomepageOnClick, EIF_DISABLED_BY_DEFAULT }, - { "sms", LPGEN("Phone/SMS"), SKINICON_OTHER_SMS, - { nullptr, "Cellular", "UserInfo", "Cellular", "UserInfo", "Phone", "UserInfo", "MyPhone0" }, - nullptr, EIF_DISABLED_BY_DEFAULT }, - { "email", LPGEN("E-mail"), SKINICON_OTHER_SENDEMAIL, - { nullptr, "e-mail", "UserInfo", "e-mail", "UserInfo", "Mye-mail0" }, - &EmailOnClick, EIF_DISABLED_BY_DEFAULT }, -}; - -static void SetExtraIcons(MCONTACT hContact) -{ - if (hContact == 0) - return; - - char *proto = Proto_GetBaseAccountName(hContact); - if ( IsEmpty(proto)) - return; - - for (auto &p : infos) { - for (unsigned int j = 0; j < _countof(p.db); j += 2) { - if (p.db[j + 1] == nullptr) - break; - - ptrA szValue(db_get_sa(hContact, p.db[j] == nullptr ? proto : p.db[j], p.db[j + 1])); - if (!IsEmpty(szValue)) { - ExtraIcon_SetIcon(p.hExtraIcon, hContact, p.hIcolib); - break; - } - } - } -} - -static int SettingChanged(WPARAM hContact, LPARAM lParam) -{ - if (hContact == 0) - return 0; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return 0; - - DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam; - bool isProto = (strcmp(cws->szModule, proto) == 0); - if (isProto && !strcmp(cws->szSetting, "ApparentMode")) { - SetVisibility(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.wVal, true); - return 0; - } - - if (!strcmp(cws->szModule, "SRMM") && !strcmp(cws->szSetting, "MuteMode")) { - SetChatMute(hContact, cws->value.type == DBVT_DELETED ? CHATMODE_NORMAL : cws->value.bVal); - return 0; - } - - if (!strcmp(cws->szSetting, "Gender") && (isProto || !strcmp(cws->szModule, "UserInfo"))) { - SetGender(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.bVal, true); - return 0; - } - - for (auto &p : infos) { - for (int j = 0; j < _countof(p.db); j += 2) { - if (p.db[j + 1] == nullptr) - break; - if (p.db[j] == nullptr && !isProto) - continue; - if (p.db[j] != nullptr && strcmp(cws->szModule, p.db[j])) - continue; - if (strcmp(cws->szSetting, p.db[j + 1])) - continue; - - bool show = (cws->value.type != DBVT_DELETED && !IsEmpty(cws->value.pszVal)); - ExtraIcon_SetIcon(p.hExtraIcon, hContact, show ? p.hIcolib : nullptr); - break; - } - } - - return 0; -} - -static int DefaultOnClick(WPARAM hContact, LPARAM, LPARAM param) -{ - Info *p = (Info*)param; - if (p == nullptr) - return 0; - - if (hContact == 0) - return 0; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return 0; - - bool found = false; - for (int j = 0; !found && j < _countof(p->db); j += 2) { - if (p->db[j + 1] == nullptr) - break; - - ptrA szValue(db_get_sa(hContact, p->db[j] == nullptr ? proto : p->db[j], p->db[j + 1])); - if (!IsEmpty(szValue)) { - p->OnClick(p, szValue); - found = true; - } - } - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// Protocol icon - -struct ProtoInfo -{ - ProtoInfo(LPCSTR _proto, HANDLE _image) : - proto(mir_strdup(_proto)), - hImage(_image) - {} - - ptrA proto; - HANDLE hImage; -}; - -static int CompareProtos(const ProtoInfo *p1, const ProtoInfo *p2) -{ return mir_strcmp(p1->proto, p2->proto); -} - -OBJLIST<ProtoInfo> arProtos(10, CompareProtos); - -static int ProtocolRebuildIcons(WPARAM, LPARAM) -{ - arProtos.destroy(); - return 0; -} - -static ProtoInfo* FindProto(const char *proto) -{ - ProtoInfo *p = arProtos.find((ProtoInfo*)&proto); - if (p) - return p; - - HICON hIcon = Skin_LoadProtoIcon(proto, ID_STATUS_ONLINE); - if (hIcon == nullptr) - return nullptr; - - HANDLE hImage = ExtraIcon_AddIcon(hIcon); - if (hImage == INVALID_HANDLE_VALUE) - return nullptr; - - p = new ProtoInfo(proto, hImage); - arProtos.insert(p); - return p; -} - -static int ProtocolApplyIcon(WPARAM hContact, LPARAM) -{ - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return 0; - - HANDLE hImage = INVALID_HANDLE_VALUE; - ProtoInfo *pi = FindProto(proto); - if (pi != nullptr) - hImage = pi->hImage; - - ExtraIcon_SetIcon(hExtraProto, hContact, hImage); - return 0; -} - -static int ProtocolOnClick(WPARAM wParam, LPARAM, LPARAM) -{ - if (wParam) - CallService(MS_USERINFO_SHOWDIALOG, wParam, 0); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void DefaultExtraIcons_Load() -{ - hExtraChat = ExtraIcon_RegisterIcolib("chat_activity", LPGEN("Chat activity"), "ChatActivity"); - hExtraChatMute = ExtraIcon_RegisterIcolib("chat_mute", LPGEN("Chat mute mode"), "ChatMute"); - hExtraVisibility = ExtraIcon_RegisterIcolib("visibility", "Visibility", Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL)); - hExtraGender = ExtraIcon_RegisterIcolib("gender", "Gender", "gender_male", nullptr, 0, EIF_DISABLED_BY_DEFAULT); - hExtraProto = ExtraIcon_RegisterCallback("protocol", "Account", Skin_GetIconHandle(SKINICON_OTHER_ACCMGR), - &ProtocolRebuildIcons, &ProtocolApplyIcon, &ProtocolOnClick, 0, EIF_DISABLED_BY_DEFAULT); - - for (auto &p : infos) { - p.hIcolib = Skin_GetIconHandle(p.iSkinIcon); - if (p.OnClick) - p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), DefaultOnClick, (LPARAM)&p, p.flags); - else - p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), nullptr, 0, p.flags); - } - - for (auto &hContact : Contacts()) { - SetChatMute(hContact, -1); - SetExtraIcons(hContact); - SetVisibility(hContact, -1, false); - SetGender(hContact, -1, false); - } - - HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged); -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "m_cluiframes.h"
+
+#include "extraicons.h"
+#include "chat.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// DB extra icons
+
+HANDLE hExtraVisibility, hExtraChat, hExtraChatMute, hExtraGender, hExtraProto;
+
+static void SetVisibility(MCONTACT hContact, int apparentMode, bool clear)
+{
+ if (hContact == 0)
+ return;
+
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (IsEmpty(proto))
+ return;
+
+ if (apparentMode <= 0)
+ apparentMode = db_get_w(hContact, proto, "ApparentMode", 0);
+
+ HANDLE hExtraIcon, hIcolib = nullptr;
+
+ // Is chat
+ if (Contact::IsGroupChat(hContact, proto)) {
+ hExtraIcon = hExtraChat;
+ if (apparentMode == ID_STATUS_OFFLINE)
+ hIcolib = IcoLib_GetIconHandle("ChatActivity");
+ }
+ else { // Not chat
+ hExtraIcon = hExtraVisibility;
+ if (apparentMode == ID_STATUS_OFFLINE)
+ hIcolib = Skin_GetIconHandle(SKINICON_OTHER_INVISIBLE_ALL);
+ else if (apparentMode == ID_STATUS_ONLINE)
+ hIcolib = Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL);
+ }
+
+ if (hIcolib != nullptr || clear)
+ ExtraIcon_SetIcon(hExtraIcon, hContact, hIcolib);
+}
+
+static void SetGender(MCONTACT hContact, int gender, bool clear)
+{
+ if (hContact == 0)
+ return;
+
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (IsEmpty(proto))
+ return;
+
+ if (gender <= 0)
+ gender = db_get_b(hContact, proto, "Gender", 0);
+ if (gender <= 0)
+ gender = db_get_b(hContact, "UserInfo", "Gender", 0);
+
+ const char *ico;
+ if (gender == 'M')
+ ico = "gender_male";
+ else if (gender == 'F')
+ ico = "gender_female";
+ else
+ ico = nullptr;
+
+ if (ico != nullptr || clear)
+ ExtraIcon_SetIconByName(hExtraGender, hContact, ico);
+}
+
+static void SetChatMute(MCONTACT hContact, int mode)
+{
+ if (hContact == 0)
+ return;
+
+ if (mode == -1)
+ mode = db_get_b(hContact, "SRMM", "MuteMode", CHATMODE_NORMAL);
+
+ HANDLE hIcon;
+ switch (mode) {
+ case CHATMODE_MUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_OFF); break;
+ case CHATMODE_UNMUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_ON); break;
+ default:
+ hIcon = nullptr; break;
+ }
+
+ ExtraIcon_SetIcon(hExtraChatMute, hContact, hIcon);
+}
+
+struct Info
+{
+ const char *name;
+ const char *desc;
+ int iSkinIcon;
+ const char *db[8];
+ void(*OnClick)(Info *info, const char *text);
+ int flags;
+
+ HANDLE hIcolib, hExtraIcon;
+};
+
+static void EmailOnClick(Info*, const char *text)
+{
+ char cmd[1024];
+ mir_snprintf(cmd, "mailto:%s", text);
+ ShellExecuteA(nullptr, "open", cmd, nullptr, nullptr, SW_SHOW);
+}
+
+static void HomepageOnClick(Info*, const char *text)
+{
+ ShellExecuteA(nullptr, "open", text, nullptr, nullptr, SW_SHOW);
+}
+
+static Info infos[] =
+{
+ { "homepage", LPGEN("Homepage"), SKINICON_OTHER_MIRANDAWEB,
+ { nullptr, "Homepage", "UserInfo", "Homepage" },
+ &HomepageOnClick, EIF_DISABLED_BY_DEFAULT },
+ { "sms", LPGEN("Phone/SMS"), SKINICON_OTHER_SMS,
+ { nullptr, "Cellular", "UserInfo", "Cellular", "UserInfo", "Phone", "UserInfo", "MyPhone0" },
+ nullptr, EIF_DISABLED_BY_DEFAULT },
+ { "email", LPGEN("E-mail"), SKINICON_OTHER_SENDEMAIL,
+ { nullptr, "e-mail", "UserInfo", "e-mail", "UserInfo", "Mye-mail0" },
+ &EmailOnClick, EIF_DISABLED_BY_DEFAULT },
+};
+
+static void SetExtraIcons(MCONTACT hContact)
+{
+ if (hContact == 0)
+ return;
+
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if ( IsEmpty(proto))
+ return;
+
+ for (auto &p : infos) {
+ for (unsigned int j = 0; j < _countof(p.db); j += 2) {
+ if (p.db[j + 1] == nullptr)
+ break;
+
+ ptrA szValue(db_get_sa(hContact, p.db[j] == nullptr ? proto : p.db[j], p.db[j + 1]));
+ if (!IsEmpty(szValue)) {
+ ExtraIcon_SetIcon(p.hExtraIcon, hContact, p.hIcolib);
+ break;
+ }
+ }
+ }
+}
+
+static int SettingChanged(WPARAM hContact, LPARAM lParam)
+{
+ if (hContact == 0)
+ return 0;
+
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (IsEmpty(proto))
+ return 0;
+
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam;
+ bool isProto = (strcmp(cws->szModule, proto) == 0);
+ if (isProto && !strcmp(cws->szSetting, "ApparentMode")) {
+ SetVisibility(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.wVal, true);
+ return 0;
+ }
+
+ if (!strcmp(cws->szModule, "SRMM") && !strcmp(cws->szSetting, "MuteMode")) {
+ SetChatMute(hContact, cws->value.type == DBVT_DELETED ? CHATMODE_NORMAL : cws->value.bVal);
+ return 0;
+ }
+
+ if (!strcmp(cws->szSetting, "Gender") && (isProto || !strcmp(cws->szModule, "UserInfo"))) {
+ SetGender(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.bVal, true);
+ return 0;
+ }
+
+ for (auto &p : infos) {
+ for (int j = 0; j < _countof(p.db); j += 2) {
+ if (p.db[j + 1] == nullptr)
+ break;
+ if (p.db[j] == nullptr && !isProto)
+ continue;
+ if (p.db[j] != nullptr && strcmp(cws->szModule, p.db[j]))
+ continue;
+ if (strcmp(cws->szSetting, p.db[j + 1]))
+ continue;
+
+ bool show = (cws->value.type != DBVT_DELETED && !IsEmpty(cws->value.pszVal));
+ ExtraIcon_SetIcon(p.hExtraIcon, hContact, show ? p.hIcolib : nullptr);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int DefaultOnClick(WPARAM hContact, LPARAM, LPARAM param)
+{
+ Info *p = (Info*)param;
+ if (p == nullptr)
+ return 0;
+
+ if (hContact == 0)
+ return 0;
+
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (IsEmpty(proto))
+ return 0;
+
+ bool found = false;
+ for (int j = 0; !found && j < _countof(p->db); j += 2) {
+ if (p->db[j + 1] == nullptr)
+ break;
+
+ ptrA szValue(db_get_sa(hContact, p->db[j] == nullptr ? proto : p->db[j], p->db[j + 1]));
+ if (!IsEmpty(szValue)) {
+ p->OnClick(p, szValue);
+ found = true;
+ }
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// Protocol icon
+
+struct ProtoInfo
+{
+ ProtoInfo(LPCSTR _proto, HANDLE _image) :
+ proto(mir_strdup(_proto)),
+ hImage(_image)
+ {}
+
+ ptrA proto;
+ HANDLE hImage;
+};
+
+static int CompareProtos(const ProtoInfo *p1, const ProtoInfo *p2)
+{ return mir_strcmp(p1->proto, p2->proto);
+}
+
+OBJLIST<ProtoInfo> arProtos(10, CompareProtos);
+
+static int ProtocolRebuildIcons(WPARAM, LPARAM)
+{
+ arProtos.destroy();
+ return 0;
+}
+
+static ProtoInfo* FindProto(const char *proto)
+{
+ ProtoInfo *p = arProtos.find((ProtoInfo*)&proto);
+ if (p)
+ return p;
+
+ HICON hIcon = Skin_LoadProtoIcon(proto, ID_STATUS_ONLINE);
+ if (hIcon == nullptr)
+ return nullptr;
+
+ HANDLE hImage = ExtraIcon_AddIcon(hIcon);
+ if (hImage == INVALID_HANDLE_VALUE)
+ return nullptr;
+
+ p = new ProtoInfo(proto, hImage);
+ arProtos.insert(p);
+ return p;
+}
+
+static int ProtocolApplyIcon(WPARAM hContact, LPARAM)
+{
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (IsEmpty(proto))
+ return 0;
+
+ HANDLE hImage = INVALID_HANDLE_VALUE;
+ ProtoInfo *pi = FindProto(proto);
+ if (pi != nullptr)
+ hImage = pi->hImage;
+
+ ExtraIcon_SetIcon(hExtraProto, hContact, hImage);
+ return 0;
+}
+
+static int ProtocolOnClick(WPARAM wParam, LPARAM, LPARAM)
+{
+ if (wParam)
+ CallService(MS_USERINFO_SHOWDIALOG, wParam, 0);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void DefaultExtraIcons_Load()
+{
+ hExtraChat = ExtraIcon_RegisterIcolib("chat_activity", LPGEN("Chat activity"), "ChatActivity");
+ hExtraChatMute = ExtraIcon_RegisterIcolib("chat_mute", LPGEN("Chat mute mode"), "ChatMute");
+ hExtraVisibility = ExtraIcon_RegisterIcolib("visibility", "Visibility", Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL));
+ hExtraGender = ExtraIcon_RegisterIcolib("gender", "Gender", "gender_male", nullptr, 0, EIF_DISABLED_BY_DEFAULT);
+ hExtraProto = ExtraIcon_RegisterCallback("protocol", "Account", Skin_GetIconHandle(SKINICON_OTHER_ACCMGR),
+ &ProtocolRebuildIcons, &ProtocolApplyIcon, &ProtocolOnClick, 0, EIF_DISABLED_BY_DEFAULT);
+
+ for (auto &p : infos) {
+ p.hIcolib = Skin_GetIconHandle(p.iSkinIcon);
+ if (p.OnClick)
+ p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), DefaultOnClick, (LPARAM)&p, p.flags);
+ else
+ p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), nullptr, 0, p.flags);
+ }
+
+ for (auto &hContact : Contacts()) {
+ SetChatMute(hContact, -1);
+ SetExtraIcons(hContact);
+ SetVisibility(hContact, -1, false);
+ SetGender(hContact, -1, false);
+ }
+
+ HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged);
+}
diff --git a/src/mir_app/src/ei_extraIcon.cpp b/src/mir_app/src/ei_extraIcon.cpp index ecec626b7e..fbdfc901ec 100644 --- a/src/mir_app/src/ei_extraIcon.cpp +++ b/src/mir_app/src/ei_extraIcon.cpp @@ -1,83 +1,83 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -ExtraIcon::ExtraIcon(const char *name) : - m_szName(mir_strdup(name)) -{ -} - -ExtraIcon::~ExtraIcon() -{ -} - -const char *ExtraIcon::getName() const -{ - return m_szName; -} - -int ExtraIcon::getSlot() const -{ - return m_slot; -} - -void ExtraIcon::setSlot(int slot) -{ - m_slot = slot; -} - -int ExtraIcon::getPosition() const -{ - return m_position; -} - -void ExtraIcon::setPosition(int position) -{ - m_position = position; -} - -bool ExtraIcon::isEnabled() const -{ - return m_slot >= 0; -} - -void ExtraIcon::doApply(MCONTACT hContact) -{ - if (m_pParent) - m_pParent->applyIcon(hContact); - else - applyIcon(hContact); -} - -void ExtraIcon::applyIcons() -{ - if (!isEnabled()) - return; - - for (auto &hContact : Contacts()) { - // Clear to assert that it will be cleared - Clist_SetExtraIcon(hContact, m_slot, INVALID_HANDLE_VALUE); - doApply(hContact); - } -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+
+ExtraIcon::ExtraIcon(const char *name) :
+ m_szName(mir_strdup(name))
+{
+}
+
+ExtraIcon::~ExtraIcon()
+{
+}
+
+const char *ExtraIcon::getName() const
+{
+ return m_szName;
+}
+
+int ExtraIcon::getSlot() const
+{
+ return m_slot;
+}
+
+void ExtraIcon::setSlot(int slot)
+{
+ m_slot = slot;
+}
+
+int ExtraIcon::getPosition() const
+{
+ return m_position;
+}
+
+void ExtraIcon::setPosition(int position)
+{
+ m_position = position;
+}
+
+bool ExtraIcon::isEnabled() const
+{
+ return m_slot >= 0;
+}
+
+void ExtraIcon::doApply(MCONTACT hContact)
+{
+ if (m_pParent)
+ m_pParent->applyIcon(hContact);
+ else
+ applyIcon(hContact);
+}
+
+void ExtraIcon::applyIcons()
+{
+ if (!isEnabled())
+ return;
+
+ for (auto &hContact : Contacts()) {
+ // Clear to assert that it will be cleared
+ Clist_SetExtraIcon(hContact, m_slot, INVALID_HANDLE_VALUE);
+ doApply(hContact);
+ }
+}
diff --git a/src/mir_app/src/ei_groupIcon.cpp b/src/mir_app/src/ei_groupIcon.cpp index 50bea072e0..44dcbbd686 100644 --- a/src/mir_app/src/ei_groupIcon.cpp +++ b/src/mir_app/src/ei_groupIcon.cpp @@ -1,200 +1,200 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -ExtraIconGroup::ExtraIconGroup(const char *_name) : - ExtraIcon(_name), - m_items(1) -{ - db_set_resident(EI_MODULE_NAME, _name); -} - -ExtraIconGroup::~ExtraIconGroup() -{ -} - -void ExtraIconGroup::addExtraIcon(BaseExtraIcon *extra) -{ - m_items.insert(extra); - - CMStringW description; - for (auto &p : m_items) { - if (!description.IsEmpty()) - description.Append(L" / "); - description += p->getDescription(); - } - - m_tszDescription = mir_wstrdup(description); -} - -void ExtraIconGroup::rebuildIcons() -{ - for (auto &p : m_items) - p->rebuildIcons(); -} - -void ExtraIconGroup::applyIcon(MCONTACT hContact) -{ - if (!isEnabled() || hContact == 0) - return; - - m_setValidExtraIcon = false; - m_insideApply = true; - - for (auto &p : m_items) { - p->applyIcon(hContact); - if (m_setValidExtraIcon) { - m_pCurrentItem = p; - break; - } - } - - m_insideApply = false; -} - -int ExtraIconGroup::getPosition() const -{ - int pos = INT_MAX; - for (auto &p : m_items) - pos = min(pos, p->getPosition()); - return pos; -} - -void ExtraIconGroup::setSlot(int slot) -{ - ExtraIcon::setSlot(slot); - - for (auto &p : m_items) - p->setSlot(slot); -} - -void ExtraIconGroup::onClick(MCONTACT hContact) -{ - if (m_pCurrentItem != nullptr) - m_pCurrentItem->onClick(hContact); -} - -int ExtraIconGroup::setIcon(MCONTACT, HANDLE) -{ - return -1; - // return internalSetIcon(hContact, (void*)value, false); -} - -int ExtraIconGroup::setIconByName(MCONTACT, const char*) -{ - return -1; - // return internalSetIcon(hContact, (void*)value, true); -} - -int ExtraIconGroup::internalSetIcon(ExtraIcon *pChild, MCONTACT hContact, HANDLE value, bool bByName) -{ - if (m_insideApply) { - for (auto &p : m_items) - if (p == pChild) { - if (bByName) - return p->setIconByName(hContact, (const char*)value); - return p->setIcon(hContact, value); - } - - return -1; - } - - int currentPos = m_items.getCount(); - int storePos = m_items.getCount(); - for (int i = 0; i < m_items.getCount(); i++) { - if (m_items[i] == pChild) - storePos = i; - - if (m_items[i] == m_pCurrentItem) - currentPos = i; - } - - if (storePos == m_items.getCount()) - return -1; - - if (storePos > currentPos) { - m_items[storePos]->storeIcon(hContact, value); - return 0; - } - - // Ok, we have to set the icon, but we have to assert it is a valid icon - m_setValidExtraIcon = false; - - int ret; - if (bByName) - ret = m_items[storePos]->setIconByName(hContact, (const char*)value); - else - ret = m_items[storePos]->setIcon(hContact, (HANDLE)value); - - if (storePos < currentPos) { - if (m_setValidExtraIcon) - m_pCurrentItem = m_items[storePos]; - } - else if (storePos == currentPos) { - if (!m_setValidExtraIcon) { - m_pCurrentItem = nullptr; - - m_insideApply = true; - - for (++storePos; storePos < m_items.getCount(); ++storePos) { - m_items[storePos]->applyIcon(hContact); - if (m_setValidExtraIcon) { - m_pCurrentItem = m_items[storePos]; - break; - } - } - - m_insideApply = false; - } - } - - return ret; -} - -const wchar_t* ExtraIconGroup::getDescription() const -{ - return m_tszDescription; -} - -HANDLE ExtraIconGroup::getDescIcon() const -{ - for (auto &p : m_items) - if (HANDLE ret = p->getDescIcon()) - return ret; - - return nullptr; -} - -int ExtraIconGroup::getType() const -{ - return EXTRAICON_TYPE_GROUP; -} - -int ExtraIconGroup::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage) -{ - if (hImage != INVALID_HANDLE_VALUE) - m_setValidExtraIcon = true; - - return Clist_SetExtraIcon(hContact, m_slot, hImage); -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+
+ExtraIconGroup::ExtraIconGroup(const char *_name) :
+ ExtraIcon(_name),
+ m_items(1)
+{
+ db_set_resident(EI_MODULE_NAME, _name);
+}
+
+ExtraIconGroup::~ExtraIconGroup()
+{
+}
+
+void ExtraIconGroup::addExtraIcon(BaseExtraIcon *extra)
+{
+ m_items.insert(extra);
+
+ CMStringW description;
+ for (auto &p : m_items) {
+ if (!description.IsEmpty())
+ description.Append(L" / ");
+ description += p->getDescription();
+ }
+
+ m_tszDescription = mir_wstrdup(description);
+}
+
+void ExtraIconGroup::rebuildIcons()
+{
+ for (auto &p : m_items)
+ p->rebuildIcons();
+}
+
+void ExtraIconGroup::applyIcon(MCONTACT hContact)
+{
+ if (!isEnabled() || hContact == 0)
+ return;
+
+ m_setValidExtraIcon = false;
+ m_insideApply = true;
+
+ for (auto &p : m_items) {
+ p->applyIcon(hContact);
+ if (m_setValidExtraIcon) {
+ m_pCurrentItem = p;
+ break;
+ }
+ }
+
+ m_insideApply = false;
+}
+
+int ExtraIconGroup::getPosition() const
+{
+ int pos = INT_MAX;
+ for (auto &p : m_items)
+ pos = min(pos, p->getPosition());
+ return pos;
+}
+
+void ExtraIconGroup::setSlot(int slot)
+{
+ ExtraIcon::setSlot(slot);
+
+ for (auto &p : m_items)
+ p->setSlot(slot);
+}
+
+void ExtraIconGroup::onClick(MCONTACT hContact)
+{
+ if (m_pCurrentItem != nullptr)
+ m_pCurrentItem->onClick(hContact);
+}
+
+int ExtraIconGroup::setIcon(MCONTACT, HANDLE)
+{
+ return -1;
+ // return internalSetIcon(hContact, (void*)value, false);
+}
+
+int ExtraIconGroup::setIconByName(MCONTACT, const char*)
+{
+ return -1;
+ // return internalSetIcon(hContact, (void*)value, true);
+}
+
+int ExtraIconGroup::internalSetIcon(ExtraIcon *pChild, MCONTACT hContact, HANDLE value, bool bByName)
+{
+ if (m_insideApply) {
+ for (auto &p : m_items)
+ if (p == pChild) {
+ if (bByName)
+ return p->setIconByName(hContact, (const char*)value);
+ return p->setIcon(hContact, value);
+ }
+
+ return -1;
+ }
+
+ int currentPos = m_items.getCount();
+ int storePos = m_items.getCount();
+ for (int i = 0; i < m_items.getCount(); i++) {
+ if (m_items[i] == pChild)
+ storePos = i;
+
+ if (m_items[i] == m_pCurrentItem)
+ currentPos = i;
+ }
+
+ if (storePos == m_items.getCount())
+ return -1;
+
+ if (storePos > currentPos) {
+ m_items[storePos]->storeIcon(hContact, value);
+ return 0;
+ }
+
+ // Ok, we have to set the icon, but we have to assert it is a valid icon
+ m_setValidExtraIcon = false;
+
+ int ret;
+ if (bByName)
+ ret = m_items[storePos]->setIconByName(hContact, (const char*)value);
+ else
+ ret = m_items[storePos]->setIcon(hContact, (HANDLE)value);
+
+ if (storePos < currentPos) {
+ if (m_setValidExtraIcon)
+ m_pCurrentItem = m_items[storePos];
+ }
+ else if (storePos == currentPos) {
+ if (!m_setValidExtraIcon) {
+ m_pCurrentItem = nullptr;
+
+ m_insideApply = true;
+
+ for (++storePos; storePos < m_items.getCount(); ++storePos) {
+ m_items[storePos]->applyIcon(hContact);
+ if (m_setValidExtraIcon) {
+ m_pCurrentItem = m_items[storePos];
+ break;
+ }
+ }
+
+ m_insideApply = false;
+ }
+ }
+
+ return ret;
+}
+
+const wchar_t* ExtraIconGroup::getDescription() const
+{
+ return m_tszDescription;
+}
+
+HANDLE ExtraIconGroup::getDescIcon() const
+{
+ for (auto &p : m_items)
+ if (HANDLE ret = p->getDescIcon())
+ return ret;
+
+ return nullptr;
+}
+
+int ExtraIconGroup::getType() const
+{
+ return EXTRAICON_TYPE_GROUP;
+}
+
+int ExtraIconGroup::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage)
+{
+ if (hImage != INVALID_HANDLE_VALUE)
+ m_setValidExtraIcon = true;
+
+ return Clist_SetExtraIcon(hContact, m_slot, hImage);
+}
diff --git a/src/mir_app/src/ei_icolibIcon.cpp b/src/mir_app/src/ei_icolibIcon.cpp index 07f4a2b3df..7095c96902 100644 --- a/src/mir_app/src/ei_icolibIcon.cpp +++ b/src/mir_app/src/ei_icolibIcon.cpp @@ -1,118 +1,118 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" -#include "usedIcons.h" - -#include "IcoLib.h" - -IcolibExtraIcon::IcolibExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) : - BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param) -{ - db_set_resident(EI_MODULE_NAME, _name); -} - -IcolibExtraIcon::~IcolibExtraIcon() -{ -} - -int IcolibExtraIcon::getType() const -{ - return EXTRAICON_TYPE_ICOLIB; -} - -void IcolibExtraIcon::rebuildIcons() -{ -} - -void IcolibExtraIcon::applyIcon(MCONTACT hContact) -{ - if (!isEnabled() || hContact == 0) - return; - - HANDLE hImage = INVALID_HANDLE_VALUE; - - ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); - if (!IsEmpty(szIconName)) - hImage = GetIcon(szIconName); - - ClistSetExtraIcon(hContact, hImage); -} - -int IcolibExtraIcon::setIcon(MCONTACT hContact, HANDLE hIcoLib) -{ - if (hContact == 0) - return -1; - - if (hIcoLib == INVALID_HANDLE_VALUE) - hIcoLib = nullptr; - - if (isEnabled()) { - ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); - if (!IsEmpty(szIconName)) - RemoveIcon(szIconName); - } - - IcolibItem *p = (IcolibItem*)hIcoLib; - char *szName = (p) ? p->name : nullptr; - storeIcon(hContact, szName); - - if (isEnabled()) - return ClistSetExtraIcon(hContact, (hIcoLib == nullptr) ? INVALID_HANDLE_VALUE : AddIcon(szName)); - - return 0; -} - -int IcolibExtraIcon::setIconByName(MCONTACT hContact, const char *icon) -{ - if (hContact == 0) - return -1; - - if (icon == INVALID_HANDLE_VALUE) - icon = nullptr; - - if (isEnabled()) { - ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); - if (!IsEmpty(szIconName)) - RemoveIcon(szIconName); - } - - storeIcon(hContact, (char*)icon); - - if (isEnabled()) - return ClistSetExtraIcon(hContact, (IsEmpty(icon)) ? INVALID_HANDLE_VALUE : AddIcon(icon)); - - return 0; -} - -void IcolibExtraIcon::storeIcon(MCONTACT hContact, void *icon) -{ - if (hContact == 0) - return; - - const char *icolibName = (const char *)icon; - if (IsEmpty(icolibName)) - db_unset(hContact, EI_MODULE_NAME, m_szName); - else - db_set_s(hContact, EI_MODULE_NAME, m_szName, icolibName); -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+#include "usedIcons.h"
+
+#include "IcoLib.h"
+
+IcolibExtraIcon::IcolibExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) :
+ BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param)
+{
+ db_set_resident(EI_MODULE_NAME, _name);
+}
+
+IcolibExtraIcon::~IcolibExtraIcon()
+{
+}
+
+int IcolibExtraIcon::getType() const
+{
+ return EXTRAICON_TYPE_ICOLIB;
+}
+
+void IcolibExtraIcon::rebuildIcons()
+{
+}
+
+void IcolibExtraIcon::applyIcon(MCONTACT hContact)
+{
+ if (!isEnabled() || hContact == 0)
+ return;
+
+ HANDLE hImage = INVALID_HANDLE_VALUE;
+
+ ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName));
+ if (!IsEmpty(szIconName))
+ hImage = GetIcon(szIconName);
+
+ ClistSetExtraIcon(hContact, hImage);
+}
+
+int IcolibExtraIcon::setIcon(MCONTACT hContact, HANDLE hIcoLib)
+{
+ if (hContact == 0)
+ return -1;
+
+ if (hIcoLib == INVALID_HANDLE_VALUE)
+ hIcoLib = nullptr;
+
+ if (isEnabled()) {
+ ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName));
+ if (!IsEmpty(szIconName))
+ RemoveIcon(szIconName);
+ }
+
+ IcolibItem *p = (IcolibItem*)hIcoLib;
+ char *szName = (p) ? p->name : nullptr;
+ storeIcon(hContact, szName);
+
+ if (isEnabled())
+ return ClistSetExtraIcon(hContact, (hIcoLib == nullptr) ? INVALID_HANDLE_VALUE : AddIcon(szName));
+
+ return 0;
+}
+
+int IcolibExtraIcon::setIconByName(MCONTACT hContact, const char *icon)
+{
+ if (hContact == 0)
+ return -1;
+
+ if (icon == INVALID_HANDLE_VALUE)
+ icon = nullptr;
+
+ if (isEnabled()) {
+ ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName));
+ if (!IsEmpty(szIconName))
+ RemoveIcon(szIconName);
+ }
+
+ storeIcon(hContact, (char*)icon);
+
+ if (isEnabled())
+ return ClistSetExtraIcon(hContact, (IsEmpty(icon)) ? INVALID_HANDLE_VALUE : AddIcon(icon));
+
+ return 0;
+}
+
+void IcolibExtraIcon::storeIcon(MCONTACT hContact, void *icon)
+{
+ if (hContact == 0)
+ return;
+
+ const char *icolibName = (const char *)icon;
+ if (IsEmpty(icolibName))
+ db_unset(hContact, EI_MODULE_NAME, m_szName);
+ else
+ db_set_s(hContact, EI_MODULE_NAME, m_szName, icolibName);
+}
diff --git a/src/mir_app/src/ei_options.cpp b/src/mir_app/src/ei_options.cpp index 442c120fbb..11e98abd18 100644 --- a/src/mir_app/src/ei_options.cpp +++ b/src/mir_app/src/ei_options.cpp @@ -1,481 +1,481 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -static class CExtraIconOptsDlg *pGlgOptions; - -int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2); - -struct intlist -{ - intlist() : count(0), data(nullptr) {} - ~intlist() { mir_free(data); } - - void add(int val) - { - data = (int*)mir_realloc(data, sizeof(int)*(count + 1)); - data[count++] = val; - } - - int count; - int *data; -}; - -static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM) -{ - intlist *a = (intlist*)lParam1; - intlist *b = (intlist*)lParam2; - return SortFunc(registeredExtraIcons[a->data[0] - 1], registeredExtraIcons[b->data[0] - 1]); -} - -// Functions ////////////////////////////////////////////////////////////////////////////////////// - -BOOL ScreenToClient(HWND hWnd, LPRECT lpRect) -{ - POINT pt; - pt.x = lpRect->left; - pt.y = lpRect->top; - - BOOL ret = ScreenToClient(hWnd, &pt); - if (!ret) - return ret; - - lpRect->left = pt.x; - lpRect->top = pt.y; - - pt.x = lpRect->right; - pt.y = lpRect->bottom; - - ret = ScreenToClient(hWnd, &pt); - - lpRect->right = pt.x; - lpRect->bottom = pt.y; - - return ret; -} - -static void RemoveExtraIcons(int slot) -{ - for (auto &hContact : Contacts()) - Clist_SetExtraIcon(hContact, slot, INVALID_HANDLE_VALUE); -} - -class CExtraIconOptsDlg : public CDlgBase -{ - intlist* Tree_GetIDs(HTREEITEM hItem) - { - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hItem; - m_tree.GetItem(&tvi); - return (intlist*)tvi.lParam; - } - - HTREEITEM Tree_AddExtraIcon(BaseExtraIcon *extra, bool selected, HTREEITEM hAfter = TVI_LAST) - { - intlist *ids = new intlist(); - ids->add(extra->getID()); - - TVINSERTSTRUCT tvis = {}; - tvis.hInsertAfter = hAfter; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE; - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - tvis.item.iSelectedImage = tvis.item.iImage = extra->getID(); - tvis.item.lParam = (LPARAM)ids; - tvis.item.pszText = (LPTSTR)extra->getDescription(); - tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1); - return m_tree.InsertItem(&tvis); - } - - HTREEITEM Tree_AddExtraIconGroup(intlist &group, bool selected, HTREEITEM hAfter = TVI_LAST) - { - intlist *ids = new intlist(); - CMStringW desc; - int img = 0; - for (int i = 0; i < group.count; i++) { - BaseExtraIcon *extra = registeredExtraIcons[group.data[i] - 1]; - ids->add(extra->getID()); - - if (img == 0 && extra->getDescIcon() != nullptr) - img = extra->getID(); - - if (i > 0) - desc += L" / "; - desc += extra->getDescription(); - } - - TVINSERTSTRUCT tvis = {}; - tvis.hInsertAfter = hAfter; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE; - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - tvis.item.iSelectedImage = tvis.item.iImage = img; - tvis.item.lParam = (LPARAM)ids; - tvis.item.pszText = (wchar_t*)desc.c_str(); - tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1); - return m_tree.InsertItem(&tvis); - } - - void GroupSelectedItems() - { - LIST<_TREEITEM> toRemove(1); - intlist ids; - bool selected = false; - HTREEITEM hPlace = nullptr; - - // Find items - HTREEITEM hItem = m_tree.GetRoot(); - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT | TVIF_STATE; - while (hItem) { - if (m_tree.IsSelected(hItem)) { - if (hPlace == nullptr) - hPlace = hItem; - - tvi.hItem = hItem; - m_tree.GetItem(&tvi); - - intlist *iids = (intlist*)tvi.lParam; - for (int i = 0; i < iids->count; i++) - ids.add(iids->data[i]); - - if ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2)) - selected = true; - - toRemove.insert(hItem); - } - - hItem = m_tree.GetNextSibling(hItem); - } - - if (hPlace != nullptr) { - // Add new - HTREEITEM hNew = Tree_AddExtraIconGroup(ids, selected, hPlace); - - // Remove old - for (auto &p : toRemove) { - delete Tree_GetIDs(p); - m_tree.DeleteItem(p); - } - - // Select - m_tree.UnselectAll(); - m_tree.SelectItem(hNew); - } - } - - void UngroupSelectedItems() - { - HTREEITEM hItem = m_tree.GetSelection(); - if (hItem == nullptr) - return; - - intlist *ids = Tree_GetIDs(hItem); - if (ids->count < 2) - return; - - bool selected = m_tree.IsSelected(hItem); - - for (int i = ids->count - 1; i >= 0; i--) { - BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1]; - Tree_AddExtraIcon(extra, selected, hItem); - } - - delete ids; - m_tree.DeleteItem(hItem); - - m_tree.UnselectAll(); - } - - int ShowPopup(int popup) - { - // Fix selection - HTREEITEM hSelected = m_tree.GetDropHilight(); - HTREEITEM hItem = m_tree.GetRoot(); - while (hItem) { - if (hItem != hSelected && m_tree.IsSelected(hItem)) - m_tree.DropHilite(hItem); - - hItem = m_tree.GetNextSibling(hItem); - } - - HMENU menu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_OPT_POPUP)); - HMENU submenu = GetSubMenu(menu, popup); - TranslateMenu(submenu); - - uint32_t pos = GetMessagePos(); - int ret = TrackPopupMenu(submenu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_LEFTALIGN, LOWORD(pos), HIWORD(pos), 0, m_hwnd, nullptr); - - DestroyMenu(menu); - - // Revert selection - hItem = m_tree.GetRoot(); - while (hItem) { - if (hItem != hSelected && m_tree.IsSelected(hItem)) - m_tree.DropUnhilite(hItem); - hItem = m_tree.GetNextSibling(hItem); - } - - return ret; - } - - CCtrlTreeView m_tree; - CTimer m_timer; - -public: - CExtraIconOptsDlg() : - CDlgBase(g_plugin, IDD_EI_OPTIONS), - m_tree(this, IDC_EXTRAORDER), - m_timer(this, 1) - { - m_tree.SetFlags(MTREE_DND | MTREE_MULTISELECT); - m_tree.OnRightClick = Callback(this, &CExtraIconOptsDlg::onRClick); - - m_timer.OnEvent = Callback(this, &CExtraIconOptsDlg::onTimer); - } - - bool OnInitDialog() override - { - pGlgOptions = this; - - int numSlots = EXTRA_ICON_COUNT; - if (numSlots < (int)registeredExtraIcons.getCount()) { - HWND label = GetDlgItem(m_hwnd, IDC_MAX_ICONS_L); - SetWindowText(label, CMStringW(FORMAT, TranslateT("*only the first %d icons will be shown"), numSlots)); - ShowWindow(label, SW_SHOW); - } - - BuildIconList(); - return true; - } - - bool OnApply() override - { - // Store old slots - int *oldSlots = new int[registeredExtraIcons.getCount()]; - int lastUsedSlot = -1; - for (int i = 0; i < registeredExtraIcons.getCount(); i++) { - if (registeredExtraIcons[i]->getType() != EXTRAICON_TYPE_GROUP) - oldSlots[i] = registeredExtraIcons[i]->getSlot(); - else // Remove old slot for groups to re-set images - oldSlots[i] = -1; - lastUsedSlot = max(lastUsedSlot, registeredExtraIcons[i]->getSlot()); - } - lastUsedSlot = min(lastUsedSlot, EXTRA_ICON_COUNT); - - // Get user data and create new groups - LIST<ExtraIconGroup> groups(1); - - uint8_t pos = 0; - int firstEmptySlot = 0; - HTREEITEM ht = m_tree.GetRoot(); - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE; - tvi.stateMask = TVIS_STATEIMAGEMASK; - while (ht) { - tvi.hItem = ht; - m_tree.GetItem(&tvi); - - intlist *ids = (intlist*)tvi.lParam; - if (ids == nullptr || ids->count < 1) - continue; // ??? - - bool enabled = ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2)); - int slot = (enabled ? firstEmptySlot++ : -1); - if (slot >= EXTRA_ICON_COUNT) - slot = -1; - - if (ids->count == 1) { - BaseExtraIcon *extra = registeredExtraIcons[ids->data[0] - 1]; - extra->setPosition(pos++); - extra->setSlot(slot); - } - else { - char name[128]; - mir_snprintf(name, "__group_%d", groups.getCount()); - - ExtraIconGroup *group = new ExtraIconGroup(name); - - for (int i = 0; i < ids->count; i++) { - BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1]; - extra->setPosition(pos++); - - group->addExtraIcon(extra); - } - - group->setSlot(slot); - groups.insert(group); - } - - ht = m_tree.GetNextSibling(ht); - } - - // Store data - for (auto &extra : registeredExtraIcons) { - char setting[512]; - mir_snprintf(setting, "Position_%s", extra->getName()); - db_set_w(0, EI_MODULE_NAME, setting, extra->getPosition()); - - mir_snprintf(setting, "Slot_%s", extra->getName()); - db_set_w(0, EI_MODULE_NAME, setting, extra->getSlot()); - } - - db_delete_module(0, EI_MODULE_NAME "Groups"); - db_set_w(0, EI_MODULE_NAME "Groups", "Count", groups.getCount()); - for (int k = 0; k < groups.getCount(); k++) { - ExtraIconGroup *group = groups[k]; - - char setting[512]; - mir_snprintf(setting, "%d_count", k); - db_set_w(0, EI_MODULE_NAME "Groups", setting, (uint16_t)group->m_items.getCount()); - - for (int j = 0; j < group->m_items.getCount(); j++) { - BaseExtraIcon *extra = group->m_items[j]; - - mir_snprintf(setting, "%d_%d", k, j); - db_set_s(0, EI_MODULE_NAME "Groups", setting, extra->getName()); - } - } - - // Clean removed slots - for (int j = firstEmptySlot; j <= lastUsedSlot; j++) - RemoveExtraIcons(j); - - // Apply icons to new slots - RebuildListsBasedOnGroups(groups); - for (auto &extra : extraIconsBySlot) - if (extra->isEnabled()) - extra->applyIcons(); - - delete[] oldSlots; - return true; - } - - void OnDestroy() override - { - pGlgOptions = nullptr; - - HTREEITEM hItem = m_tree.GetRoot(); - while (hItem) { - delete Tree_GetIDs(hItem); - hItem = m_tree.GetNextSibling(hItem); - } - - ImageList_Destroy(m_tree.GetImageList(TVSIL_NORMAL)); - } - - void onRClick(CCtrlTreeView::TEventInfo*) - { - HTREEITEM hSelected = m_tree.GetDropHilight(); - if (hSelected != nullptr && !m_tree.IsSelected(hSelected)) { - m_tree.UnselectAll(); - m_tree.SelectItem(hSelected); - } - - int sels = m_tree.GetNumSelected(); - if (sels > 1) { - if (ShowPopup(0) == ID_GROUP) { - GroupSelectedItems(); - NotifyChange(); - } - } - else if (sels == 1) { - HTREEITEM hItem = m_tree.GetSelection(); - intlist *ids = Tree_GetIDs(hItem); - if (ids->count > 1) { - if (ShowPopup(1) == ID_UNGROUP) { - UngroupSelectedItems(); - NotifyChange(); - } - } - } - } - - void BuildIconList() - { - HIMAGELIST hImageList = ImageList_Create(g_iIconSX, g_iIconSX, ILC_COLOR32 | ILC_MASK, 2, 2); - ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK)); - - for (auto &extra : registeredExtraIcons) { - extra->setID(registeredExtraIcons.indexOf(&extra)+1); - - HICON hIcon = IcoLib_GetIconByHandle(extra->getDescIcon()); - if (hIcon == nullptr) - ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK)); - else { - ImageList_AddIcon(hImageList, hIcon); - IcoLib_ReleaseIcon(hIcon); - } - } - m_tree.SetImageList(hImageList, TVSIL_NORMAL); - - for (auto &extra : extraIconsBySlot) { - if (extra->getType() == EXTRAICON_TYPE_GROUP) { - ExtraIconGroup *group = (ExtraIconGroup *)extra; - intlist ids; - for (auto &p : group->m_items) - ids.add(p->getID()); - Tree_AddExtraIconGroup(ids, extra->isEnabled()); - } - else Tree_AddExtraIcon((BaseExtraIcon*)extra, extra->isEnabled()); - } - - TVSORTCB sort = {}; - sort.hParent = nullptr; - sort.lParam = 0; - sort.lpfnCompare = CompareFunc; - m_tree.SortChildrenCB(&sort, 0); - } - - void onTimer(CTimer*) - { - m_timer.Stop(); - m_tree.DeleteAllItems(); - BuildIconList(); - } - - void ResetIconList() - { - m_timer.Start(100); - } -}; - -void eiOptionsRefresh() -{ - if (pGlgOptions) - pGlgOptions->ResetIconList(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int InitOptionsCallback(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.szGroup.a = LPGEN("Contact list"); - odp.szTitle.a = LPGEN("Extra icons"); - odp.szTab.a = LPGEN("General"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CExtraIconOptsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "extraicons.h"
+
+static class CExtraIconOptsDlg *pGlgOptions;
+
+int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2);
+
+struct intlist
+{
+ intlist() : count(0), data(nullptr) {}
+ ~intlist() { mir_free(data); }
+
+ void add(int val)
+ {
+ data = (int*)mir_realloc(data, sizeof(int)*(count + 1));
+ data[count++] = val;
+ }
+
+ int count;
+ int *data;
+};
+
+static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM)
+{
+ intlist *a = (intlist*)lParam1;
+ intlist *b = (intlist*)lParam2;
+ return SortFunc(registeredExtraIcons[a->data[0] - 1], registeredExtraIcons[b->data[0] - 1]);
+}
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+BOOL ScreenToClient(HWND hWnd, LPRECT lpRect)
+{
+ POINT pt;
+ pt.x = lpRect->left;
+ pt.y = lpRect->top;
+
+ BOOL ret = ScreenToClient(hWnd, &pt);
+ if (!ret)
+ return ret;
+
+ lpRect->left = pt.x;
+ lpRect->top = pt.y;
+
+ pt.x = lpRect->right;
+ pt.y = lpRect->bottom;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ lpRect->right = pt.x;
+ lpRect->bottom = pt.y;
+
+ return ret;
+}
+
+static void RemoveExtraIcons(int slot)
+{
+ for (auto &hContact : Contacts())
+ Clist_SetExtraIcon(hContact, slot, INVALID_HANDLE_VALUE);
+}
+
+class CExtraIconOptsDlg : public CDlgBase
+{
+ intlist* Tree_GetIDs(HTREEITEM hItem)
+ {
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hItem;
+ m_tree.GetItem(&tvi);
+ return (intlist*)tvi.lParam;
+ }
+
+ HTREEITEM Tree_AddExtraIcon(BaseExtraIcon *extra, bool selected, HTREEITEM hAfter = TVI_LAST)
+ {
+ intlist *ids = new intlist();
+ ids->add(extra->getID());
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.hInsertAfter = hAfter;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE;
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.iSelectedImage = tvis.item.iImage = extra->getID();
+ tvis.item.lParam = (LPARAM)ids;
+ tvis.item.pszText = (LPTSTR)extra->getDescription();
+ tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1);
+ return m_tree.InsertItem(&tvis);
+ }
+
+ HTREEITEM Tree_AddExtraIconGroup(intlist &group, bool selected, HTREEITEM hAfter = TVI_LAST)
+ {
+ intlist *ids = new intlist();
+ CMStringW desc;
+ int img = 0;
+ for (int i = 0; i < group.count; i++) {
+ BaseExtraIcon *extra = registeredExtraIcons[group.data[i] - 1];
+ ids->add(extra->getID());
+
+ if (img == 0 && extra->getDescIcon() != nullptr)
+ img = extra->getID();
+
+ if (i > 0)
+ desc += L" / ";
+ desc += extra->getDescription();
+ }
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.hInsertAfter = hAfter;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE;
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.iSelectedImage = tvis.item.iImage = img;
+ tvis.item.lParam = (LPARAM)ids;
+ tvis.item.pszText = (wchar_t*)desc.c_str();
+ tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1);
+ return m_tree.InsertItem(&tvis);
+ }
+
+ void GroupSelectedItems()
+ {
+ LIST<_TREEITEM> toRemove(1);
+ intlist ids;
+ bool selected = false;
+ HTREEITEM hPlace = nullptr;
+
+ // Find items
+ HTREEITEM hItem = m_tree.GetRoot();
+ TVITEMEX tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
+ while (hItem) {
+ if (m_tree.IsSelected(hItem)) {
+ if (hPlace == nullptr)
+ hPlace = hItem;
+
+ tvi.hItem = hItem;
+ m_tree.GetItem(&tvi);
+
+ intlist *iids = (intlist*)tvi.lParam;
+ for (int i = 0; i < iids->count; i++)
+ ids.add(iids->data[i]);
+
+ if ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2))
+ selected = true;
+
+ toRemove.insert(hItem);
+ }
+
+ hItem = m_tree.GetNextSibling(hItem);
+ }
+
+ if (hPlace != nullptr) {
+ // Add new
+ HTREEITEM hNew = Tree_AddExtraIconGroup(ids, selected, hPlace);
+
+ // Remove old
+ for (auto &p : toRemove) {
+ delete Tree_GetIDs(p);
+ m_tree.DeleteItem(p);
+ }
+
+ // Select
+ m_tree.UnselectAll();
+ m_tree.SelectItem(hNew);
+ }
+ }
+
+ void UngroupSelectedItems()
+ {
+ HTREEITEM hItem = m_tree.GetSelection();
+ if (hItem == nullptr)
+ return;
+
+ intlist *ids = Tree_GetIDs(hItem);
+ if (ids->count < 2)
+ return;
+
+ bool selected = m_tree.IsSelected(hItem);
+
+ for (int i = ids->count - 1; i >= 0; i--) {
+ BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1];
+ Tree_AddExtraIcon(extra, selected, hItem);
+ }
+
+ delete ids;
+ m_tree.DeleteItem(hItem);
+
+ m_tree.UnselectAll();
+ }
+
+ int ShowPopup(int popup)
+ {
+ // Fix selection
+ HTREEITEM hSelected = m_tree.GetDropHilight();
+ HTREEITEM hItem = m_tree.GetRoot();
+ while (hItem) {
+ if (hItem != hSelected && m_tree.IsSelected(hItem))
+ m_tree.DropHilite(hItem);
+
+ hItem = m_tree.GetNextSibling(hItem);
+ }
+
+ HMENU menu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_OPT_POPUP));
+ HMENU submenu = GetSubMenu(menu, popup);
+ TranslateMenu(submenu);
+
+ uint32_t pos = GetMessagePos();
+ int ret = TrackPopupMenu(submenu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_LEFTALIGN, LOWORD(pos), HIWORD(pos), 0, m_hwnd, nullptr);
+
+ DestroyMenu(menu);
+
+ // Revert selection
+ hItem = m_tree.GetRoot();
+ while (hItem) {
+ if (hItem != hSelected && m_tree.IsSelected(hItem))
+ m_tree.DropUnhilite(hItem);
+ hItem = m_tree.GetNextSibling(hItem);
+ }
+
+ return ret;
+ }
+
+ CCtrlTreeView m_tree;
+ CTimer m_timer;
+
+public:
+ CExtraIconOptsDlg() :
+ CDlgBase(g_plugin, IDD_EI_OPTIONS),
+ m_tree(this, IDC_EXTRAORDER),
+ m_timer(this, 1)
+ {
+ m_tree.SetFlags(MTREE_DND | MTREE_MULTISELECT);
+ m_tree.OnRightClick = Callback(this, &CExtraIconOptsDlg::onRClick);
+
+ m_timer.OnEvent = Callback(this, &CExtraIconOptsDlg::onTimer);
+ }
+
+ bool OnInitDialog() override
+ {
+ pGlgOptions = this;
+
+ int numSlots = EXTRA_ICON_COUNT;
+ if (numSlots < (int)registeredExtraIcons.getCount()) {
+ HWND label = GetDlgItem(m_hwnd, IDC_MAX_ICONS_L);
+ SetWindowText(label, CMStringW(FORMAT, TranslateT("*only the first %d icons will be shown"), numSlots));
+ ShowWindow(label, SW_SHOW);
+ }
+
+ BuildIconList();
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ // Store old slots
+ int *oldSlots = new int[registeredExtraIcons.getCount()];
+ int lastUsedSlot = -1;
+ for (int i = 0; i < registeredExtraIcons.getCount(); i++) {
+ if (registeredExtraIcons[i]->getType() != EXTRAICON_TYPE_GROUP)
+ oldSlots[i] = registeredExtraIcons[i]->getSlot();
+ else // Remove old slot for groups to re-set images
+ oldSlots[i] = -1;
+ lastUsedSlot = max(lastUsedSlot, registeredExtraIcons[i]->getSlot());
+ }
+ lastUsedSlot = min(lastUsedSlot, EXTRA_ICON_COUNT);
+
+ // Get user data and create new groups
+ LIST<ExtraIconGroup> groups(1);
+
+ uint8_t pos = 0;
+ int firstEmptySlot = 0;
+ HTREEITEM ht = m_tree.GetRoot();
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
+ tvi.stateMask = TVIS_STATEIMAGEMASK;
+ while (ht) {
+ tvi.hItem = ht;
+ m_tree.GetItem(&tvi);
+
+ intlist *ids = (intlist*)tvi.lParam;
+ if (ids == nullptr || ids->count < 1)
+ continue; // ???
+
+ bool enabled = ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2));
+ int slot = (enabled ? firstEmptySlot++ : -1);
+ if (slot >= EXTRA_ICON_COUNT)
+ slot = -1;
+
+ if (ids->count == 1) {
+ BaseExtraIcon *extra = registeredExtraIcons[ids->data[0] - 1];
+ extra->setPosition(pos++);
+ extra->setSlot(slot);
+ }
+ else {
+ char name[128];
+ mir_snprintf(name, "__group_%d", groups.getCount());
+
+ ExtraIconGroup *group = new ExtraIconGroup(name);
+
+ for (int i = 0; i < ids->count; i++) {
+ BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1];
+ extra->setPosition(pos++);
+
+ group->addExtraIcon(extra);
+ }
+
+ group->setSlot(slot);
+ groups.insert(group);
+ }
+
+ ht = m_tree.GetNextSibling(ht);
+ }
+
+ // Store data
+ for (auto &extra : registeredExtraIcons) {
+ char setting[512];
+ mir_snprintf(setting, "Position_%s", extra->getName());
+ db_set_w(0, EI_MODULE_NAME, setting, extra->getPosition());
+
+ mir_snprintf(setting, "Slot_%s", extra->getName());
+ db_set_w(0, EI_MODULE_NAME, setting, extra->getSlot());
+ }
+
+ db_delete_module(0, EI_MODULE_NAME "Groups");
+ db_set_w(0, EI_MODULE_NAME "Groups", "Count", groups.getCount());
+ for (int k = 0; k < groups.getCount(); k++) {
+ ExtraIconGroup *group = groups[k];
+
+ char setting[512];
+ mir_snprintf(setting, "%d_count", k);
+ db_set_w(0, EI_MODULE_NAME "Groups", setting, (uint16_t)group->m_items.getCount());
+
+ for (int j = 0; j < group->m_items.getCount(); j++) {
+ BaseExtraIcon *extra = group->m_items[j];
+
+ mir_snprintf(setting, "%d_%d", k, j);
+ db_set_s(0, EI_MODULE_NAME "Groups", setting, extra->getName());
+ }
+ }
+
+ // Clean removed slots
+ for (int j = firstEmptySlot; j <= lastUsedSlot; j++)
+ RemoveExtraIcons(j);
+
+ // Apply icons to new slots
+ RebuildListsBasedOnGroups(groups);
+ for (auto &extra : extraIconsBySlot)
+ if (extra->isEnabled())
+ extra->applyIcons();
+
+ delete[] oldSlots;
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ pGlgOptions = nullptr;
+
+ HTREEITEM hItem = m_tree.GetRoot();
+ while (hItem) {
+ delete Tree_GetIDs(hItem);
+ hItem = m_tree.GetNextSibling(hItem);
+ }
+
+ ImageList_Destroy(m_tree.GetImageList(TVSIL_NORMAL));
+ }
+
+ void onRClick(CCtrlTreeView::TEventInfo*)
+ {
+ HTREEITEM hSelected = m_tree.GetDropHilight();
+ if (hSelected != nullptr && !m_tree.IsSelected(hSelected)) {
+ m_tree.UnselectAll();
+ m_tree.SelectItem(hSelected);
+ }
+
+ int sels = m_tree.GetNumSelected();
+ if (sels > 1) {
+ if (ShowPopup(0) == ID_GROUP) {
+ GroupSelectedItems();
+ NotifyChange();
+ }
+ }
+ else if (sels == 1) {
+ HTREEITEM hItem = m_tree.GetSelection();
+ intlist *ids = Tree_GetIDs(hItem);
+ if (ids->count > 1) {
+ if (ShowPopup(1) == ID_UNGROUP) {
+ UngroupSelectedItems();
+ NotifyChange();
+ }
+ }
+ }
+ }
+
+ void BuildIconList()
+ {
+ HIMAGELIST hImageList = ImageList_Create(g_iIconSX, g_iIconSX, ILC_COLOR32 | ILC_MASK, 2, 2);
+ ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK));
+
+ for (auto &extra : registeredExtraIcons) {
+ extra->setID(registeredExtraIcons.indexOf(&extra)+1);
+
+ HICON hIcon = IcoLib_GetIconByHandle(extra->getDescIcon());
+ if (hIcon == nullptr)
+ ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK));
+ else {
+ ImageList_AddIcon(hImageList, hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+ }
+ }
+ m_tree.SetImageList(hImageList, TVSIL_NORMAL);
+
+ for (auto &extra : extraIconsBySlot) {
+ if (extra->getType() == EXTRAICON_TYPE_GROUP) {
+ ExtraIconGroup *group = (ExtraIconGroup *)extra;
+ intlist ids;
+ for (auto &p : group->m_items)
+ ids.add(p->getID());
+ Tree_AddExtraIconGroup(ids, extra->isEnabled());
+ }
+ else Tree_AddExtraIcon((BaseExtraIcon*)extra, extra->isEnabled());
+ }
+
+ TVSORTCB sort = {};
+ sort.hParent = nullptr;
+ sort.lParam = 0;
+ sort.lpfnCompare = CompareFunc;
+ m_tree.SortChildrenCB(&sort, 0);
+ }
+
+ void onTimer(CTimer*)
+ {
+ m_timer.Stop();
+ m_tree.DeleteAllItems();
+ BuildIconList();
+ }
+
+ void ResetIconList()
+ {
+ m_timer.Start(100);
+ }
+};
+
+void eiOptionsRefresh()
+{
+ if (pGlgOptions)
+ pGlgOptions->ResetIconList();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int InitOptionsCallback(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.szGroup.a = LPGEN("Contact list");
+ odp.szTitle.a = LPGEN("Extra icons");
+ odp.szTab.a = LPGEN("General");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new CExtraIconOptsDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/mir_app/src/ei_services.cpp b/src/mir_app/src/ei_services.cpp index 9f41eb4679..f55551980d 100644 --- a/src/mir_app/src/ei_services.cpp +++ b/src/mir_app/src/ei_services.cpp @@ -1,504 +1,504 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "m_cluiframes.h" - -#include "extraicons.h" -#include "usedIcons.h" -#include "clc.h" - -// Prototypes /////////////////////////////////////////////////////////////////////////// - -int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2) -{ - int ret = p1->getPosition() - p2->getPosition(); - if (ret != 0) - return ret; - - return p1->getID() - p2->getID(); -} - -LIST<ExtraIcon> extraIconsBySlot(10, SortFunc); -LIST<BaseExtraIcon> registeredExtraIcons(10, PtrKeySortT); - -static bool clistRebuildAlreadyCalled = false, clistApplyAlreadyCalled = false; - -// Functions //////////////////////////////////////////////////////////////////////////// - -int InitOptionsCallback(WPARAM wParam, LPARAM lParam); - -int ConvertToClistSlot(int slot) -{ - if (slot < 0) - return slot; - - return slot + 1; -} - -int ExtraImage_ExtraIDToColumnNum(int extra) -{ - return (extra < 1 || extra > EXTRA_ICON_COUNT) ? -1 : extra - 1; -} - -int Clist_SetExtraIcon(MCONTACT hContact, int slot, HANDLE hImage) -{ - if (g_clistApi.hwndContactTree == nullptr) - return -1; - - int icol = ExtraImage_ExtraIDToColumnNum(ConvertToClistSlot(slot)); - if (icol == -1) - return -1; - - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGE, hContact, MAKELPARAM(icol, hImage)); - return 0; -} - -BaseExtraIcon* GetExtraIconByName(const char *name) -{ - for (auto &extra : registeredExtraIcons) - if (mir_strcmp(name, extra->getName()) == 0) - return extra; - - return nullptr; -} - -static void LoadGroups(LIST<ExtraIconGroup> &groups) -{ - int count = db_get_w(0, EI_MODULE_NAME "Groups", "Count", 0); - for (int i = 0; i < count; i++) { - char setting[512]; - mir_snprintf(setting, "%d_count", i); - unsigned int items = db_get_w(0, EI_MODULE_NAME "Groups", setting, 0); - if (items < 1) - continue; - - mir_snprintf(setting, "__group_%d", i); - ExtraIconGroup *group = new ExtraIconGroup(setting); - - for (unsigned int j = 0; j < items; j++) { - mir_snprintf(setting, "%d_%d", i, j); - ptrA szIconName(db_get_sa(0, EI_MODULE_NAME "Groups", setting)); - if (IsEmpty(szIconName)) - continue; - - BaseExtraIcon *extra = GetExtraIconByName(szIconName); - if (extra == nullptr) - continue; - - group->m_items.insert(extra); - if (extra->getSlot() >= 0) - group->setSlot(extra->getSlot()); - } - - if (group->m_items.getCount() < 2) { - delete group; - continue; - } - - groups.insert(group); - } -} - -static ExtraIconGroup* IsInGroup(LIST<ExtraIconGroup> &groups, BaseExtraIcon *extra) -{ - for (auto &group : groups) - for (auto &it : group->m_items) - if (extra == it) - return group; - - return nullptr; -} - -void RebuildListsBasedOnGroups(LIST<ExtraIconGroup> &groups) -{ - for (auto &extra : registeredExtraIcons) - extra->setParent(nullptr); - - for (auto &extra : extraIconsBySlot) - if (extra->getType() == EXTRAICON_TYPE_GROUP) - delete extra; - extraIconsBySlot.destroy(); - - for (auto &group : groups) { - for (auto &it : group->m_items) - it->setParent(group); - - extraIconsBySlot.insert(group); - } - - for (auto &extra : registeredExtraIcons) - if (extra->getParent() == nullptr) - extraIconsBySlot.insert(extra); -} - -/////////////////////////////////////////////////////////////////////////////// - -static void ResetSlots(BaseExtraIcon *extra, ExtraIconGroup *group, int iOldSlot = -1) -{ - int slot = 0, oldMaxSlot = -1; - for (auto &ex : extraIconsBySlot) { - if (ex->getSlot() < 0) - continue; - - int oldSlot = ex->getSlot(); - if (oldSlot > oldMaxSlot) - oldMaxSlot = oldSlot+1; - - ex->setSlot(slot++); - - if (clistApplyAlreadyCalled && (ex == group || ex == extra || oldSlot != slot)) - ex->applyIcons(); - } - - if (iOldSlot > oldMaxSlot) - oldMaxSlot = iOldSlot + 1; - - // slots were freed, we need to clear one or more items - if (extra == nullptr) - for (int i = slot; i < oldMaxSlot; i++) - for (auto &hContact : Contacts()) - Clist_SetExtraIcon(hContact, i, INVALID_HANDLE_VALUE); - - if (!Miranda_IsTerminated()) { - Clist_InitAutoRebuild(g_clistApi.hwndContactTree); - eiOptionsRefresh(); - } -} - -void KillModuleExtraIcons(CMPluginBase *pPlugin) -{ - LIST<ExtraIcon> arIcons(1); - - for (auto &it : registeredExtraIcons.rev_iter()) - if (it->m_pPlugin == pPlugin) { - arIcons.insert(it); - registeredExtraIcons.removeItem(&it); - } - - if (arIcons.getCount() == 0) - return; - - int iOldSlot = -1; - for (auto &it : arIcons) - if (it->getSlot() > iOldSlot) - iOldSlot = it->getSlot() + 1; - - LIST<ExtraIconGroup> groups(1); - LoadGroups(groups); - RebuildListsBasedOnGroups(groups); - ResetSlots(0, 0, iOldSlot); - - for (auto &it : arIcons) - delete it; -} - -/////////////////////////////////////////////////////////////////////////////// - -int ClistExtraListRebuild(WPARAM, LPARAM) -{ - clistRebuildAlreadyCalled = true; - - ResetIcons(); - - for (auto &it : extraIconsBySlot) - it->rebuildIcons(); - - return 0; -} - -int ClistExtraImageApply(WPARAM hContact, LPARAM) -{ - if (hContact == 0) - return 0; - - clistApplyAlreadyCalled = true; - - for (auto &it : extraIconsBySlot) - it->doApply(hContact); - - return 0; -} - -int ClistExtraClick(WPARAM hContact, LPARAM lParam) -{ - if (hContact == 0) - return 0; - - int clistSlot = (int)lParam; - - for (auto &extra : extraIconsBySlot) { - if (ConvertToClistSlot(extra->getSlot()) == clistSlot) { - extra->onClick(hContact); - break; - } - } - - return 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// Extra image list functions - -HANDLE hEventExtraImageListRebuilding, hEventExtraImageApplying, hEventExtraClick; - -static bool bImageCreated = false; -static HIMAGELIST hExtraImageList = nullptr; - -MIR_APP_DLL(HANDLE) ExtraIcon_AddIcon(HICON hIcon) -{ - if (hExtraImageList == nullptr || hIcon == nullptr) - return INVALID_HANDLE_VALUE; - - int res = ImageList_AddIcon(hExtraImageList, hIcon); - return (res > 0xFFFE) ? INVALID_HANDLE_VALUE : (HANDLE)res; -} - -MIR_APP_DLL(void) ExtraIcon_Reload() -{ - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRASPACE, db_get_b(0, "CLUI", "ExtraColumnSpace", 18), 0); - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, 0); - - if (hExtraImageList) - ImageList_Destroy(hExtraImageList); - - hExtraImageList = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, 1, 256); - - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hExtraImageList); - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0); - NotifyEventHooks(hEventExtraImageListRebuilding, 0, 0); - bImageCreated = true; -} - -MIR_APP_DLL(void) ExtraIcon_SetAll(MCONTACT hContact) -{ - if (g_clistApi.hwndContactTree == nullptr) - return; - - if (!bImageCreated) - ExtraIcon_Reload(); - - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0); - - if (hContact == 0) { - for (auto &it : Contacts()) - NotifyEventHooks(hEventExtraImageApplying, it, 0); - } - else NotifyEventHooks(hEventExtraImageApplying, hContact, 0); - - g_clistApi.pfnInvalidateRect(g_clistApi.hwndContactTree, nullptr, FALSE); -} - -/////////////////////////////////////////////////////////////////////////////// -// external functions - -static void EI_PostCreate(BaseExtraIcon *extra, int flags) -{ - char setting[512]; - mir_snprintf(setting, "Position_%s", extra->getName()); - extra->setPosition(db_get_w(0, EI_MODULE_NAME, setting, 1000)); - - mir_snprintf(setting, "Slot_%s", extra->getName()); - int slot = db_get_w(0, EI_MODULE_NAME, setting, -100); - if (slot == EMPTY_EXTRA_ICON) - slot = -1; - else if (slot == -100) { - if (flags & EIF_DISABLED_BY_DEFAULT) { - db_set_w(0, EI_MODULE_NAME, setting, EMPTY_EXTRA_ICON); - slot = -1; - } - else slot = 1; - } - extra->setSlot(slot); - - registeredExtraIcons.insert(extra); - - LIST<ExtraIconGroup> groups(1); - LoadGroups(groups); - - ExtraIconGroup *group = IsInGroup(groups, extra); - if (group != nullptr) - RebuildListsBasedOnGroups(groups); - else { - for (auto &it : groups) - delete it; - - extraIconsBySlot.insert(extra); - } - - if (slot >= 0 || group != nullptr) { - if (clistRebuildAlreadyCalled) - extra->rebuildIcons(); - - ResetSlots(extra, group); - } -} - -EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterCallback(const char *name, const char *description, HANDLE descIcon, - MIRANDAHOOK RebuildIcons, MIRANDAHOOK ApplyIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags) -{ - // EXTRAICON_TYPE_CALLBACK - if (IsEmpty(name) || IsEmpty(description)) - return nullptr; - - if (ApplyIcon == nullptr || RebuildIcons == nullptr) - return nullptr; - - // no way to merge - if (GetExtraIconByName(name) != nullptr) - return nullptr; - - ptrW tszDesc(mir_a2u(description)); - - BaseExtraIcon *extra = new CallbackExtraIcon(name, tszDesc, descIcon, RebuildIcons, ApplyIcon, OnClick, onClickParam); - extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress(RebuildIcons)); - EI_PostCreate(extra, flags); - return extra; -} - -EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterIcolib(const char *name, const char *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags) -{ - if (IsEmpty(name) || IsEmpty(description)) - return nullptr; - - ptrW tszDesc(mir_a2u(description)); - - BaseExtraIcon *extra = GetExtraIconByName(name); - if (extra != nullptr) { - if (extra->getType() != EXTRAICON_TYPE_ICOLIB) - return nullptr; - - // Found one, now merge it - if (descIcon) - extra->setDescIcon(descIcon); - - if (OnClick != nullptr) - extra->setOnClick(OnClick, onClickParam); - - if (extra->getSlot() > 0) { - if (clistRebuildAlreadyCalled) - extra->rebuildIcons(); - if (clistApplyAlreadyCalled) - extra->applyIcons(); - } - } - else { - extra = new IcolibExtraIcon(name, tszDesc, descIcon, OnClick, onClickParam); - extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress((void*)name)); - EI_PostCreate(extra, flags); - } - - return extra; -} - -/////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) ExtraIcon_SetIcon(HANDLE hExtraIcon, MCONTACT hContact, HANDLE hImage) -{ - if (hExtraIcon == nullptr || hContact == 0) - return -1; - - BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); - if (extra == nullptr) - return -1; - - if (extra->getParent()) - return extra->getParent()->internalSetIcon(extra, hContact, hImage, false); - - return extra->setIcon(hContact, hImage); -} - -MIR_APP_DLL(int) ExtraIcon_SetIconByName(HANDLE hExtraIcon, MCONTACT hContact, const char *icoName) -{ - if (hExtraIcon == nullptr || hContact == 0) - return -1; - - BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); - if (extra == nullptr) - return -1; - - if (extra->getParent()) - return extra->getParent()->internalSetIcon(extra, hContact, (HANDLE)icoName, true); - - return extra->setIconByName(hContact, icoName); -} - -MIR_APP_DLL(int) ExtraIcon_Clear(HANDLE hExtraIcon, MCONTACT hContact) -{ - if (hExtraIcon == nullptr || hContact == 0) - return -1; - - BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); - if (extra == nullptr) - return -1; - - if (extra->getParent()) - return extra->getParent()->internalSetIcon(extra, hContact, nullptr, false); - - return extra->setIcon(hContact, nullptr); -} - -/////////////////////////////////////////////////////////////////////////////// - -static IconItem iconList[] = -{ - { LPGEN("Chat activity"), "ChatActivity", IDI_CHAT }, - { LPGEN("Mute chat"), "ChatMute", IDI_OFF }, - { LPGEN("Male"), "gender_male", IDI_MALE }, - { LPGEN("Female"), "gender_female", IDI_FEMALE }, - { LPGEN("Database"), "database", IDI_DATABASE }, -}; - -void LoadExtraIconsModule() -{ - // Events - hEventExtraClick = CreateHookableEvent(ME_CLIST_EXTRA_CLICK); - hEventExtraImageApplying = CreateHookableEvent(ME_CLIST_EXTRA_IMAGE_APPLY); - hEventExtraImageListRebuilding = CreateHookableEvent(ME_CLIST_EXTRA_LIST_REBUILD); - - // Icons - g_plugin.registerIcon(LPGEN("Contact list"), iconList); - - // Hooks - HookEvent(ME_OPT_INITIALISE, InitOptionsCallback); - - HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, ClistExtraListRebuild); - HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, ClistExtraImageApply); - HookEvent(ME_CLIST_EXTRA_CLICK, ClistExtraClick); - - DefaultExtraIcons_Load(); -} - -void UnloadExtraIconsModule(void) -{ - for (auto &extra : extraIconsBySlot) - if (extra->getType() == EXTRAICON_TYPE_GROUP) - delete extra; - - for (auto &it : registeredExtraIcons) - delete it; - - if (hExtraImageList) { - ImageList_Destroy(hExtraImageList); - hExtraImageList = nullptr; - } -} +/*
+
+Copyright (C) 2009 Ricardo Pescuma Domenecci
+Copyright (C) 2012-23 Miranda NG team
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "m_cluiframes.h"
+
+#include "extraicons.h"
+#include "usedIcons.h"
+#include "clc.h"
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2)
+{
+ int ret = p1->getPosition() - p2->getPosition();
+ if (ret != 0)
+ return ret;
+
+ return p1->getID() - p2->getID();
+}
+
+LIST<ExtraIcon> extraIconsBySlot(10, SortFunc);
+LIST<BaseExtraIcon> registeredExtraIcons(10, PtrKeySortT);
+
+static bool clistRebuildAlreadyCalled = false, clistApplyAlreadyCalled = false;
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+int InitOptionsCallback(WPARAM wParam, LPARAM lParam);
+
+int ConvertToClistSlot(int slot)
+{
+ if (slot < 0)
+ return slot;
+
+ return slot + 1;
+}
+
+int ExtraImage_ExtraIDToColumnNum(int extra)
+{
+ return (extra < 1 || extra > EXTRA_ICON_COUNT) ? -1 : extra - 1;
+}
+
+int Clist_SetExtraIcon(MCONTACT hContact, int slot, HANDLE hImage)
+{
+ if (g_clistApi.hwndContactTree == nullptr)
+ return -1;
+
+ int icol = ExtraImage_ExtraIDToColumnNum(ConvertToClistSlot(slot));
+ if (icol == -1)
+ return -1;
+
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGE, hContact, MAKELPARAM(icol, hImage));
+ return 0;
+}
+
+BaseExtraIcon* GetExtraIconByName(const char *name)
+{
+ for (auto &extra : registeredExtraIcons)
+ if (mir_strcmp(name, extra->getName()) == 0)
+ return extra;
+
+ return nullptr;
+}
+
+static void LoadGroups(LIST<ExtraIconGroup> &groups)
+{
+ int count = db_get_w(0, EI_MODULE_NAME "Groups", "Count", 0);
+ for (int i = 0; i < count; i++) {
+ char setting[512];
+ mir_snprintf(setting, "%d_count", i);
+ unsigned int items = db_get_w(0, EI_MODULE_NAME "Groups", setting, 0);
+ if (items < 1)
+ continue;
+
+ mir_snprintf(setting, "__group_%d", i);
+ ExtraIconGroup *group = new ExtraIconGroup(setting);
+
+ for (unsigned int j = 0; j < items; j++) {
+ mir_snprintf(setting, "%d_%d", i, j);
+ ptrA szIconName(db_get_sa(0, EI_MODULE_NAME "Groups", setting));
+ if (IsEmpty(szIconName))
+ continue;
+
+ BaseExtraIcon *extra = GetExtraIconByName(szIconName);
+ if (extra == nullptr)
+ continue;
+
+ group->m_items.insert(extra);
+ if (extra->getSlot() >= 0)
+ group->setSlot(extra->getSlot());
+ }
+
+ if (group->m_items.getCount() < 2) {
+ delete group;
+ continue;
+ }
+
+ groups.insert(group);
+ }
+}
+
+static ExtraIconGroup* IsInGroup(LIST<ExtraIconGroup> &groups, BaseExtraIcon *extra)
+{
+ for (auto &group : groups)
+ for (auto &it : group->m_items)
+ if (extra == it)
+ return group;
+
+ return nullptr;
+}
+
+void RebuildListsBasedOnGroups(LIST<ExtraIconGroup> &groups)
+{
+ for (auto &extra : registeredExtraIcons)
+ extra->setParent(nullptr);
+
+ for (auto &extra : extraIconsBySlot)
+ if (extra->getType() == EXTRAICON_TYPE_GROUP)
+ delete extra;
+ extraIconsBySlot.destroy();
+
+ for (auto &group : groups) {
+ for (auto &it : group->m_items)
+ it->setParent(group);
+
+ extraIconsBySlot.insert(group);
+ }
+
+ for (auto &extra : registeredExtraIcons)
+ if (extra->getParent() == nullptr)
+ extraIconsBySlot.insert(extra);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void ResetSlots(BaseExtraIcon *extra, ExtraIconGroup *group, int iOldSlot = -1)
+{
+ int slot = 0, oldMaxSlot = -1;
+ for (auto &ex : extraIconsBySlot) {
+ if (ex->getSlot() < 0)
+ continue;
+
+ int oldSlot = ex->getSlot();
+ if (oldSlot > oldMaxSlot)
+ oldMaxSlot = oldSlot+1;
+
+ ex->setSlot(slot++);
+
+ if (clistApplyAlreadyCalled && (ex == group || ex == extra || oldSlot != slot))
+ ex->applyIcons();
+ }
+
+ if (iOldSlot > oldMaxSlot)
+ oldMaxSlot = iOldSlot + 1;
+
+ // slots were freed, we need to clear one or more items
+ if (extra == nullptr)
+ for (int i = slot; i < oldMaxSlot; i++)
+ for (auto &hContact : Contacts())
+ Clist_SetExtraIcon(hContact, i, INVALID_HANDLE_VALUE);
+
+ if (!Miranda_IsTerminated()) {
+ Clist_InitAutoRebuild(g_clistApi.hwndContactTree);
+ eiOptionsRefresh();
+ }
+}
+
+void KillModuleExtraIcons(CMPluginBase *pPlugin)
+{
+ LIST<ExtraIcon> arIcons(1);
+
+ for (auto &it : registeredExtraIcons.rev_iter())
+ if (it->m_pPlugin == pPlugin) {
+ arIcons.insert(it);
+ registeredExtraIcons.removeItem(&it);
+ }
+
+ if (arIcons.getCount() == 0)
+ return;
+
+ int iOldSlot = -1;
+ for (auto &it : arIcons)
+ if (it->getSlot() > iOldSlot)
+ iOldSlot = it->getSlot() + 1;
+
+ LIST<ExtraIconGroup> groups(1);
+ LoadGroups(groups);
+ RebuildListsBasedOnGroups(groups);
+ ResetSlots(0, 0, iOldSlot);
+
+ for (auto &it : arIcons)
+ delete it;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ClistExtraListRebuild(WPARAM, LPARAM)
+{
+ clistRebuildAlreadyCalled = true;
+
+ ResetIcons();
+
+ for (auto &it : extraIconsBySlot)
+ it->rebuildIcons();
+
+ return 0;
+}
+
+int ClistExtraImageApply(WPARAM hContact, LPARAM)
+{
+ if (hContact == 0)
+ return 0;
+
+ clistApplyAlreadyCalled = true;
+
+ for (auto &it : extraIconsBySlot)
+ it->doApply(hContact);
+
+ return 0;
+}
+
+int ClistExtraClick(WPARAM hContact, LPARAM lParam)
+{
+ if (hContact == 0)
+ return 0;
+
+ int clistSlot = (int)lParam;
+
+ for (auto &extra : extraIconsBySlot) {
+ if (ConvertToClistSlot(extra->getSlot()) == clistSlot) {
+ extra->onClick(hContact);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Extra image list functions
+
+HANDLE hEventExtraImageListRebuilding, hEventExtraImageApplying, hEventExtraClick;
+
+static bool bImageCreated = false;
+static HIMAGELIST hExtraImageList = nullptr;
+
+MIR_APP_DLL(HANDLE) ExtraIcon_AddIcon(HICON hIcon)
+{
+ if (hExtraImageList == nullptr || hIcon == nullptr)
+ return INVALID_HANDLE_VALUE;
+
+ int res = ImageList_AddIcon(hExtraImageList, hIcon);
+ return (res > 0xFFFE) ? INVALID_HANDLE_VALUE : (HANDLE)res;
+}
+
+MIR_APP_DLL(void) ExtraIcon_Reload()
+{
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRASPACE, db_get_b(0, "CLUI", "ExtraColumnSpace", 18), 0);
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, 0);
+
+ if (hExtraImageList)
+ ImageList_Destroy(hExtraImageList);
+
+ hExtraImageList = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, 1, 256);
+
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hExtraImageList);
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0);
+ NotifyEventHooks(hEventExtraImageListRebuilding, 0, 0);
+ bImageCreated = true;
+}
+
+MIR_APP_DLL(void) ExtraIcon_SetAll(MCONTACT hContact)
+{
+ if (g_clistApi.hwndContactTree == nullptr)
+ return;
+
+ if (!bImageCreated)
+ ExtraIcon_Reload();
+
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0);
+
+ if (hContact == 0) {
+ for (auto &it : Contacts())
+ NotifyEventHooks(hEventExtraImageApplying, it, 0);
+ }
+ else NotifyEventHooks(hEventExtraImageApplying, hContact, 0);
+
+ g_clistApi.pfnInvalidateRect(g_clistApi.hwndContactTree, nullptr, FALSE);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// external functions
+
+static void EI_PostCreate(BaseExtraIcon *extra, int flags)
+{
+ char setting[512];
+ mir_snprintf(setting, "Position_%s", extra->getName());
+ extra->setPosition(db_get_w(0, EI_MODULE_NAME, setting, 1000));
+
+ mir_snprintf(setting, "Slot_%s", extra->getName());
+ int slot = db_get_w(0, EI_MODULE_NAME, setting, -100);
+ if (slot == EMPTY_EXTRA_ICON)
+ slot = -1;
+ else if (slot == -100) {
+ if (flags & EIF_DISABLED_BY_DEFAULT) {
+ db_set_w(0, EI_MODULE_NAME, setting, EMPTY_EXTRA_ICON);
+ slot = -1;
+ }
+ else slot = 1;
+ }
+ extra->setSlot(slot);
+
+ registeredExtraIcons.insert(extra);
+
+ LIST<ExtraIconGroup> groups(1);
+ LoadGroups(groups);
+
+ ExtraIconGroup *group = IsInGroup(groups, extra);
+ if (group != nullptr)
+ RebuildListsBasedOnGroups(groups);
+ else {
+ for (auto &it : groups)
+ delete it;
+
+ extraIconsBySlot.insert(extra);
+ }
+
+ if (slot >= 0 || group != nullptr) {
+ if (clistRebuildAlreadyCalled)
+ extra->rebuildIcons();
+
+ ResetSlots(extra, group);
+ }
+}
+
+EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterCallback(const char *name, const char *description, HANDLE descIcon,
+ MIRANDAHOOK RebuildIcons, MIRANDAHOOK ApplyIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags)
+{
+ // EXTRAICON_TYPE_CALLBACK
+ if (IsEmpty(name) || IsEmpty(description))
+ return nullptr;
+
+ if (ApplyIcon == nullptr || RebuildIcons == nullptr)
+ return nullptr;
+
+ // no way to merge
+ if (GetExtraIconByName(name) != nullptr)
+ return nullptr;
+
+ ptrW tszDesc(mir_a2u(description));
+
+ BaseExtraIcon *extra = new CallbackExtraIcon(name, tszDesc, descIcon, RebuildIcons, ApplyIcon, OnClick, onClickParam);
+ extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress(RebuildIcons));
+ EI_PostCreate(extra, flags);
+ return extra;
+}
+
+EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterIcolib(const char *name, const char *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags)
+{
+ if (IsEmpty(name) || IsEmpty(description))
+ return nullptr;
+
+ ptrW tszDesc(mir_a2u(description));
+
+ BaseExtraIcon *extra = GetExtraIconByName(name);
+ if (extra != nullptr) {
+ if (extra->getType() != EXTRAICON_TYPE_ICOLIB)
+ return nullptr;
+
+ // Found one, now merge it
+ if (descIcon)
+ extra->setDescIcon(descIcon);
+
+ if (OnClick != nullptr)
+ extra->setOnClick(OnClick, onClickParam);
+
+ if (extra->getSlot() > 0) {
+ if (clistRebuildAlreadyCalled)
+ extra->rebuildIcons();
+ if (clistApplyAlreadyCalled)
+ extra->applyIcons();
+ }
+ }
+ else {
+ extra = new IcolibExtraIcon(name, tszDesc, descIcon, OnClick, onClickParam);
+ extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress((void*)name));
+ EI_PostCreate(extra, flags);
+ }
+
+ return extra;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) ExtraIcon_SetIcon(HANDLE hExtraIcon, MCONTACT hContact, HANDLE hImage)
+{
+ if (hExtraIcon == nullptr || hContact == 0)
+ return -1;
+
+ BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon);
+ if (extra == nullptr)
+ return -1;
+
+ if (extra->getParent())
+ return extra->getParent()->internalSetIcon(extra, hContact, hImage, false);
+
+ return extra->setIcon(hContact, hImage);
+}
+
+MIR_APP_DLL(int) ExtraIcon_SetIconByName(HANDLE hExtraIcon, MCONTACT hContact, const char *icoName)
+{
+ if (hExtraIcon == nullptr || hContact == 0)
+ return -1;
+
+ BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon);
+ if (extra == nullptr)
+ return -1;
+
+ if (extra->getParent())
+ return extra->getParent()->internalSetIcon(extra, hContact, (HANDLE)icoName, true);
+
+ return extra->setIconByName(hContact, icoName);
+}
+
+MIR_APP_DLL(int) ExtraIcon_Clear(HANDLE hExtraIcon, MCONTACT hContact)
+{
+ if (hExtraIcon == nullptr || hContact == 0)
+ return -1;
+
+ BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon);
+ if (extra == nullptr)
+ return -1;
+
+ if (extra->getParent())
+ return extra->getParent()->internalSetIcon(extra, hContact, nullptr, false);
+
+ return extra->setIcon(hContact, nullptr);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static IconItem iconList[] =
+{
+ { LPGEN("Chat activity"), "ChatActivity", IDI_CHAT },
+ { LPGEN("Mute chat"), "ChatMute", IDI_OFF },
+ { LPGEN("Male"), "gender_male", IDI_MALE },
+ { LPGEN("Female"), "gender_female", IDI_FEMALE },
+ { LPGEN("Database"), "database", IDI_DATABASE },
+};
+
+void LoadExtraIconsModule()
+{
+ // Events
+ hEventExtraClick = CreateHookableEvent(ME_CLIST_EXTRA_CLICK);
+ hEventExtraImageApplying = CreateHookableEvent(ME_CLIST_EXTRA_IMAGE_APPLY);
+ hEventExtraImageListRebuilding = CreateHookableEvent(ME_CLIST_EXTRA_LIST_REBUILD);
+
+ // Icons
+ g_plugin.registerIcon(LPGEN("Contact list"), iconList);
+
+ // Hooks
+ HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+
+ HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, ClistExtraListRebuild);
+ HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, ClistExtraImageApply);
+ HookEvent(ME_CLIST_EXTRA_CLICK, ClistExtraClick);
+
+ DefaultExtraIcons_Load();
+}
+
+void UnloadExtraIconsModule(void)
+{
+ for (auto &extra : extraIconsBySlot)
+ if (extra->getType() == EXTRAICON_TYPE_GROUP)
+ delete extra;
+
+ for (auto &it : registeredExtraIcons)
+ delete it;
+
+ if (hExtraImageList) {
+ ImageList_Destroy(hExtraImageList);
+ hExtraImageList = nullptr;
+ }
+}
diff --git a/src/mir_app/src/encrypt.cpp b/src/mir_app/src/encrypt.cpp index 79298a9221..d08d8e24c6 100644 --- a/src/mir_app/src/encrypt.cpp +++ b/src/mir_app/src/encrypt.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/encrypt.h b/src/mir_app/src/encrypt.h index bcb37cd799..034e38804e 100644 --- a/src/mir_app/src/encrypt.h +++ b/src/mir_app/src/encrypt.h @@ -1,47 +1,47 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -struct MCryptoProvider : public CRYPTO_PROVIDER -{ - MCryptoProvider(const CRYPTO_PROVIDER *pProvider) - { - memcpy(this, pProvider, sizeof(*pProvider)); - pszName = mir_strdup(pszName); - if (dwFlags & CPF_UNICODE) - szDescr.w = mir_wstrdup(TranslateW_LP(pProvider->szDescr.w, pProvider->pPlugin)); - else - szDescr.w = mir_a2u(TranslateA_LP(pProvider->szDescr.a, pProvider->pPlugin)); - } - - ~MCryptoProvider() - { - mir_free(pszName); - mir_free(szDescr.w); - } -}; - -extern OBJLIST<MCryptoProvider> arCryptoProviders; - -void InitCryptMenuItem(CMenuItem &mi); +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#pragma once
+
+struct MCryptoProvider : public CRYPTO_PROVIDER
+{
+ MCryptoProvider(const CRYPTO_PROVIDER *pProvider)
+ {
+ memcpy(this, pProvider, sizeof(*pProvider));
+ pszName = mir_strdup(pszName);
+ if (dwFlags & CPF_UNICODE)
+ szDescr.w = mir_wstrdup(TranslateW_LP(pProvider->szDescr.w, pProvider->pPlugin));
+ else
+ szDescr.w = mir_a2u(TranslateA_LP(pProvider->szDescr.a, pProvider->pPlugin));
+ }
+
+ ~MCryptoProvider()
+ {
+ mir_free(pszName);
+ mir_free(szDescr.w);
+ }
+};
+
+extern OBJLIST<MCryptoProvider> arCryptoProviders;
+
+void InitCryptMenuItem(CMenuItem &mi);
diff --git a/src/mir_app/src/enterstring.cpp b/src/mir_app/src/enterstring.cpp index adfed7e805..69897231c1 100644 --- a/src/mir_app/src/enterstring.cpp +++ b/src/mir_app/src/enterstring.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/extracticon.cpp b/src/mir_app/src/extracticon.cpp index 5bd7a1cd76..415eaee2f1 100644 --- a/src/mir_app/src/extracticon.cpp +++ b/src/mir_app/src/extracticon.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/extraicons.h b/src/mir_app/src/extraicons.h index d0b4b8a711..b22075a467 100644 --- a/src/mir_app/src/extraicons.h +++ b/src/mir_app/src/extraicons.h @@ -1,7 +1,7 @@ /*
Copyright (C) 2009 Ricardo Pescuma Domenecci
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/src/mir_app/src/filter.cpp b/src/mir_app/src/filter.cpp index 8e6af687b5..27bf9cd3b0 100644 --- a/src/mir_app/src/filter.cpp +++ b/src/mir_app/src/filter.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/filter.h b/src/mir_app/src/filter.h index a583915eba..8c8964bfa1 100644 --- a/src/mir_app/src/filter.h +++ b/src/mir_app/src/filter.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/findadd.cpp b/src/mir_app/src/findadd.cpp index 88850481d8..bd7613b444 100644 --- a/src/mir_app/src/findadd.cpp +++ b/src/mir_app/src/findadd.cpp @@ -1,1035 +1,1035 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "findadd.h" - -#define TIMERID_THROBBER 111 - -#define HM_SEARCHACK (WM_USER+10) -#define M_SETGROUPVISIBILITIES (WM_USER+11) - -static HWND hwndFindAdd = nullptr; -static HANDLE hHookModulesLoaded = nullptr; -static HGENMENU hMainMenuItem = nullptr; -static int OnSystemModulesLoaded(WPARAM wParam, LPARAM lParam); - -static int FindAddDlgResizer(HWND, LPARAM lParam, UTILRESIZECONTROL *urc) -{ - static int y, nextY, oldTop; - FindAddDlgData *dat = (FindAddDlgData*)lParam; - - switch (urc->wId) { - case IDC_RESULTS: - return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; - - case IDOK: - dat->minDlgHeight = nextY + urc->rcItem.bottom - urc->rcItem.top; - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; - - case IDC_ADD: - case IDC_MOREOPTIONS: - return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; - - case IDC_STATUSBAR: - return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; - - case IDC_PROTOIDGROUP: //the resize is always processed in template order - nextY = y = urc->rcItem.top; - if (dat->showProtoId) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - break; - - case IDC_EMAILGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showEmail) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_NAMEGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showName) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_ADVANCEDGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showAdvanced) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_TINYEXTENDEDGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showTiny) { - int height = urc->dlgNewSize.cy - y - (urc->dlgOriginalSize.cy - urc->rcItem.bottom); - nextY = y + 200; //min height for custom dialog - urc->rcItem.top = urc->rcItem.bottom - height; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; - - case IDC_BYEMAIL: - case IDC_EMAIL: - case IDC_BYNAME: - case IDC_STNAMENICK: - case IDC_STNAMEFIRST: - case IDC_STNAMELAST: - case IDC_NAMENICK: - case IDC_NAMEFIRST: - case IDC_NAMELAST: - case IDC_BYADVANCED: - case IDC_BYCUSTOM: - case IDC_ADVANCED: - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_HEADERBAR: - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -static void RenderThrobber(HDC hdc, RECT *rcItem, int *throbbing, int *pivot) -{ - InflateRect(rcItem, -1, 0); - int width = rcItem->right - rcItem->left; - int height = rcItem->bottom - rcItem->top; - int height2 = height / 2; - - if (*throbbing) { - /* create memdc */ - HDC hMemDC = CreateCompatibleDC(nullptr); - HBITMAP hBitmap = (HBITMAP)SelectObject(hMemDC, CreateCompatibleBitmap(hdc, width, height)); - /* flush it */ - RECT rc; - rc.left = rc.top = 0; - rc.right = width; - rc.bottom = height; - HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE); - FillRect(hMemDC, &rc, hBr); - DeleteObject(hBr); - /* set up the pen */ - HPEN hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 4, GetSysColor(COLOR_BTNSHADOW))); - /* draw everything before the pivot */ - int x = *pivot; - while (x > (-height)) { - MoveToEx(hMemDC, x + height2, 0, nullptr); - LineTo(hMemDC, x - height2, height); - x -= 12; - } - - /* draw everything after the pivot */ - x = *pivot; - while (x < width + height) { - MoveToEx(hMemDC, x + height2, 0, nullptr); - LineTo(hMemDC, x - height2, height); - x += 12; - } - - /* move the pivot */ - *pivot += 2; - /* reset the pivot point if it gets past the rect */ - if (*pivot > width) *pivot = 0; - /* put back the old pen and delete the new one */ - DeleteObject(SelectObject(hMemDC, hPen)); - /* cap the top and bottom */ - hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNFACE))); - MoveToEx(hMemDC, 0, 0, nullptr); - LineTo(hMemDC, width, 0); - MoveToEx(hMemDC, 0, height - 1, nullptr); - LineTo(hMemDC, width, height - 1); - /* select in the old pen and delete the new pen */ - DeleteObject(SelectObject(hMemDC, hPen)); - /* paint to screen */ - BitBlt(hdc, rcItem->left, rcItem->top, width, height, hMemDC, 0, 0, SRCCOPY); - /* select back in the old bitmap and delete the created one, as well as freeing the mem dc. */ - hBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); - DeleteObject(hBitmap); - DeleteDC(hMemDC); - } - else { - /* just flush the DC */ - HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE); - FillRect(hdc, rcItem, hBr); - DeleteObject(hBr); - } -} - -static void StartThrobber(HWND hwndDlg, FindAddDlgData *dat) -{ - dat->throbbing = 1; - SetTimer(hwndDlg, TIMERID_THROBBER, 25, nullptr); -} - -static void StopThrobber(HWND hwndDlg, FindAddDlgData *dat) -{ - KillTimer(hwndDlg, TIMERID_THROBBER); - dat->throbbing = 0; - dat->pivot = 0; - InvalidateRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), nullptr, FALSE); -} - -static LRESULT CALLBACK AdvancedSearchDlgSubclassProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_COMMAND) { - HWND parentHwnd = GetParent(hwndDlg); - switch (LOWORD(wParam)) { - case IDOK: - SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDOK, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDOK)); - SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED)); - break; - - case IDCANCEL: - CheckDlgButton(parentHwnd, IDC_ADVANCED, BST_UNCHECKED); - SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDC_ADVANCED, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDC_ADVANCED)); - SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED)); - break; - } - } - return mir_callNextSubclass(hwndDlg, AdvancedSearchDlgSubclassProc, msg, wParam, lParam); -} - -static void ShowAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (szProto == nullptr) - return; - - if (dat->hwndAdvSearch == nullptr) { - RECT rc; - dat->hwndAdvSearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)hwndDlg); - if (dat->hwndAdvSearch != nullptr) - mir_subclassWindow(dat->hwndAdvSearch, AdvancedSearchDlgSubclassProc); - GetWindowRect(GetDlgItem(hwndDlg, IDC_RESULTS), &rc); - SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - } - - AnimateWindow(dat->hwndAdvSearch, 150, AW_ACTIVATE | AW_SLIDE | AW_HOR_POSITIVE); - RedrawWindow(dat->hwndAdvSearch, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); - - CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_CHECKED); -} - -static void ReposTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - if (dat->hwndTinySearch == nullptr) - return; - - RECT rc; - RECT clientRect; - POINT pt = { 0, 0 }; - GetWindowRect(GetDlgItem(hwndDlg, IDC_TINYEXTENDEDGROUP), &rc); - GetWindowRect(dat->hwndTinySearch, &clientRect); - pt.x = rc.left; - pt.y = rc.top; - ScreenToClient(hwndDlg, &pt); - SetWindowPos(dat->hwndTinySearch, nullptr, pt.x + 5, pt.y + 15, rc.right - rc.left - 10, rc.bottom - rc.top - 30, SWP_NOZORDER); -} - -static void ShowTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (szProto == nullptr) - return; - - if (dat->hwndTinySearch == nullptr) { - dat->hwndTinySearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)/*GetDlgItem(*/hwndDlg/*, IDC_TINYEXTENDEDGROUP)*/); - if (dat->hwndTinySearch) - ReposTinySearchDlg(hwndDlg, dat); - else - dat->showTiny = false; - } - ShowWindow(dat->hwndTinySearch, SW_SHOW); -} - -static void HideAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - if (dat->hwndAdvSearch == nullptr) - return; - - AnimateWindow(dat->hwndAdvSearch, 150, AW_HIDE | AW_BLEND); - CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_UNCHECKED); -} - -void EnableResultButtons(HWND hwndDlg, int enable) -{ - EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), enable); - EnableWindow(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), enable); -} - -static const int controls[] = { IDC_BYPROTOID, IDC_BYEMAIL, IDC_BYNAME, IDC_BYADVANCED, IDC_BYCUSTOM }; - -static void CheckSearchTypeRadioButton(HWND hwndDlg, int idControl) -{ - for (auto &it : controls) - CheckDlgButton(hwndDlg, it, idControl == it ? BST_CHECKED : BST_UNCHECKED); -} - -#define sttErrMsg TranslateT("You haven't filled in the search field. Please enter a search term and try again.") -#define sttErrTitle TranslateT("Search") - -static void SetListItemText(HWND hwndList, int idx, int col, wchar_t *szText) -{ - if (szText == nullptr || *szText == 0) - szText = TranslateT("<not specified>"); - - ListView_SetItemText(hwndList, idx, col, szText); -} - -static wchar_t* sttDecodeString(uint32_t dwFlags, MAllStrings &src) -{ - if (dwFlags & PSR_UNICODE) - return mir_wstrdup(src.w); - - if (dwFlags & PSR_UTF8) - return mir_utf8decodeW(src.a); - - return mir_a2u(src.a); -} - -static INT_PTR CALLBACK DlgProcFindAdd(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - FindAddDlgData *dat = (FindAddDlgData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - HWND hwndList = GetDlgItem(hwndDlg, IDC_RESULTS); - RECT rc, rc2; - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - Window_SetSkinIcon_IcoLib(hwndDlg, SKINICON_OTHER_FINDUSER); - dat = (FindAddDlgData*)mir_calloc(sizeof(FindAddDlgData)); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - dat->notSearchedYet = 1; - dat->iLastColumnSortIndex = 1; - dat->bSortAscending = 1; - SendDlgItemMessage(hwndDlg, IDC_MOREOPTIONS, BUTTONSETARROW, 1, 0); - SendDlgItemMessage(hwndDlg, IDOK, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Ctrl+Search add contact"), BATF_UNICODE); - - ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP); - - GetClientRect(hwndList, &rc); - { - LVCOLUMN lvc; - lvc.mask = LVCF_TEXT | LVCF_WIDTH; - lvc.pszText = TranslateT("Results"); - lvc.cx = rc.right - 1; - ListView_InsertColumn(hwndList, 0, &lvc); - - LVITEM lvi; - lvi.mask = LVIF_TEXT; - lvi.iItem = 0; - lvi.iSubItem = 0; - lvi.pszText = TranslateT("There are no results to display."); - ListView_InsertItem(hwndList, &lvi); - - // Allocate a reasonable amount of space in the status bar - HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR)); - SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_GETFONT, 0, 0)); - - SIZE textSize; - GetTextExtentPoint32(hdc, TranslateT("Searching"), (int)mir_wstrlen(TranslateT("Searching")), &textSize); - - int partWidth[3]; - partWidth[0] = textSize.cx; - GetTextExtentPoint32(hdc, L"01234567890123456789", 20, &textSize); - partWidth[0] += textSize.cx; - ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc); - partWidth[1] = partWidth[0] + 150; - partWidth[2] = -1; - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETPARTS, _countof(partWidth), (LPARAM)partWidth); - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETTEXT, 1 | SBT_OWNERDRAW, 0); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - - wchar_t *szProto = nullptr; - ptrW tszLast(db_get_wsa(0, "FindAdd", "LastSearched")); - if (tszLast) - szProto = NEWWSTR_ALLOCA(tszLast); - - int index = 0, cbwidth = 0, netProtoCount = 0; - for (auto &pa : g_arAccounts) { - if (!pa->IsEnabled()) - continue; - - uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (caps & PF1_ANYSEARCH) - netProtoCount++; - } - dat->himlComboIcons = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, netProtoCount + 1, netProtoCount + 1); - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_SETIMAGELIST, 0, (LPARAM)dat->himlComboIcons); - - COMBOBOXEXITEM cbei; - cbei.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM; - cbei.iItem = 0; - hdc = GetDC(hwndDlg); - SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, WM_GETFONT, 0, 0)); - if (netProtoCount > 1) { - cbei.pszText = TranslateT("All networks"); - GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize); - if (textSize.cx > cbwidth) - cbwidth = textSize.cx; - cbei.iImage = cbei.iSelectedImage = ImageList_AddSkinIcon(dat->himlComboIcons, SKINICON_OTHER_SEARCHALL); - cbei.lParam = 0; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei); - cbei.iItem++; - } - - for (auto &pa : g_arAccounts) { - if (!pa->IsEnabled()) - continue; - - uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (!(caps & PF1_ANYSEARCH)) - continue; - - cbei.pszText = pa->tszAccountName; - GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize); - if (textSize.cx > cbwidth) - cbwidth = textSize.cx; - - HICON hIcon = (HICON)CallProtoServiceInt(0, pa->szModuleName, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0); - cbei.iImage = cbei.iSelectedImage = ImageList_AddIcon(dat->himlComboIcons, hIcon); - DestroyIcon(hIcon); - cbei.lParam = (LPARAM)pa->szModuleName; - SendDlgItemMessageA(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei); - if (szProto && cbei.pszText && !mir_wstrcmp(szProto, pa->tszAccountName)) - index = cbei.iItem; - cbei.iItem++; - } - cbwidth += 32; - - RECT rect; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rect); - if ((rect.right - rect.left) < cbwidth) - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETDROPPEDWIDTH, cbwidth, 0); - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETCURSEL, index, 0); - - SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0); - Utils_RestoreWindowPosition(hwndDlg, 0, "FindAdd", ""); - } - return TRUE; - - case WM_SIZE: - Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FINDADD), FindAddDlgResizer, (LPARAM)dat); - ReposTinySearchDlg(hwndDlg, dat); - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_SIZE, 0, 0); - if (dat->notSearchedYet) { - GetClientRect(hwndList, &rc); - ListView_SetColumnWidth(hwndList, 0, rc.right); - } - __fallthrough; - - case WM_MOVE: - if (dat && dat->hwndAdvSearch) { - GetWindowRect(hwndList, &rc); - SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - } - break; - - case WM_GETMINMAXINFO: - GetWindowRect(hwndList, &rc); - GetWindowRect(hwndDlg, &rc2); - { - MINMAXINFO *mmi = (MINMAXINFO*)lParam; - mmi->ptMinTrackSize.x = rc.left - rc2.left + 10 + GetSystemMetrics(SM_CXFRAME); - GetClientRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc); - mmi->ptMinTrackSize.x += rc.right + 5; - GetClientRect(GetDlgItem(hwndDlg, IDC_ADD), &rc); - mmi->ptMinTrackSize.x += rc.right + 5; - GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc); - mmi->ptMinTrackSize.y = dat->minDlgHeight + 20 + GetSystemMetrics(SM_CYCAPTION) + 2 * GetSystemMetrics(SM_CYFRAME); - GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc); - mmi->ptMinTrackSize.y += rc.bottom; - } - return 0; - - case M_SETGROUPVISIBILITIES: - dat->showAdvanced = dat->showEmail = dat->showName = dat->showProtoId = dat->showTiny = 0; - { - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (szProto == (char *)CB_ERR) - break; - if (szProto == nullptr) { - for (auto &pa : g_arAccounts) { - if (pa->IsEnabled()) { - uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1; - if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1; - } - } - } - else { - uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_BASICSEARCH) dat->showProtoId = 1; - if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1; - if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1; - - if (protoCaps & PF1_EXTSEARCHUI) dat->showAdvanced = 1; - else if (protoCaps & PF1_EXTSEARCH) dat->showTiny = 1; - - if (protoCaps & PF1_USERIDISEMAIL && dat->showProtoId) { dat->showProtoId = 0; dat->showEmail = 1; } - if (dat->showProtoId) { - wchar_t *wszUniqueId = (wchar_t *)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAG_UNIQUEIDTEXT, 0); - if (wszUniqueId) - SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, wszUniqueId); - else - SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, TranslateT("Handle")); - - if (protoCaps & PF1_NUMERICUSERID) - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE) | ES_NUMBER); - else - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE)&~ES_NUMBER); - } - } - - if (dat->showTiny) - ShowTinySearchDlg(hwndDlg, dat); - else if (dat->hwndTinySearch) { - DestroyWindow(dat->hwndTinySearch); - dat->hwndTinySearch = nullptr; - } - -#define en(id, t) ShowWindow( GetDlgItem(hwndDlg, IDC_##id), dat->show##t?SW_SHOW:SW_HIDE) - en(PROTOIDGROUP, ProtoId); en(BYPROTOID, ProtoId); en(PROTOID, ProtoId); - en(EMAILGROUP, Email); en(BYEMAIL, Email); en(EMAIL, Email); - en(NAMEGROUP, Name); en(BYNAME, Name); - en(STNAMENICK, Name); en(NAMENICK, Name); - en(STNAMEFIRST, Name); en(NAMEFIRST, Name); - en(STNAMELAST, Name); en(NAMELAST, Name); - en(ADVANCEDGROUP, Advanced); en(BYADVANCED, Advanced); en(ADVANCED, Advanced); - en(BYCUSTOM, Tiny); en(TINYEXTENDEDGROUP, Tiny); -#undef en - int checkmarkVisible = (dat->showAdvanced && IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) || - (dat->showEmail && IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) || - (dat->showTiny && IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) || - (dat->showName && IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) || - (dat->showProtoId && IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID)); - if (!checkmarkVisible) { - if (dat->showProtoId) CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID); - else if (dat->showEmail) CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL); - else if (dat->showName) CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME); - else if (dat->showAdvanced) CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED); - else if (dat->showTiny) CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM); - } - - SendMessage(hwndDlg, WM_SIZE, 0, 0); - - MINMAXINFO mmi; - SendMessage(hwndDlg, WM_GETMINMAXINFO, 0, (LPARAM)&mmi); - - GetWindowRect(hwndDlg, &rc); - if (rc.bottom - rc.top < mmi.ptMinTrackSize.y) - SetWindowPos(hwndDlg, nullptr, 0, 0, rc.right - rc.left, mmi.ptMinTrackSize.y, SWP_NOZORDER | SWP_NOMOVE); - } - break; - - case WM_TIMER: - if (wParam == TIMERID_THROBBER) { - int borders[3]; - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETBORDERS, 0, (LPARAM)borders); - - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETRECT, 1, (LPARAM)&rc); - InflateRect(&rc, -borders[2] / 2, -borders[1] / 2); - HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR)); - RenderThrobber(hdc, &rc, &dat->throbbing, &dat->pivot); - ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc); - } - break; - - case WM_DRAWITEM: - { - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)lParam; - if (dis->CtlID == IDC_STATUSBAR && dis->itemID == 1) { - RenderThrobber(dis->hDC, &dis->rcItem, &dat->throbbing, &dat->pivot); - return TRUE; - } - } - break; - - case WM_NOTIFY: - if (wParam == IDC_RESULTS) { - switch (((LPNMHDR)lParam)->code) { - case LVN_ITEMCHANGED: - { - int count = ListView_GetSelectedCount(hwndList); - if (dat->notSearchedYet) - count = 0; - EnableResultButtons(hwndDlg, count); - } - break; - - case LVN_COLUMNCLICK: - HDITEM hdi; - hdi.mask = HDI_FORMAT; - hdi.fmt = HDF_LEFT | HDF_STRING; - Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi); - - LPNMLISTVIEW nmlv = (LPNMLISTVIEW)lParam; - if (nmlv->iSubItem != dat->iLastColumnSortIndex) { - dat->bSortAscending = TRUE; - dat->iLastColumnSortIndex = nmlv->iSubItem; - } - else dat->bSortAscending = !dat->bSortAscending; - - hdi.fmt = HDF_LEFT | HDF_STRING | (dat->bSortAscending ? HDF_SORTDOWN : HDF_SORTUP); - Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi); - - ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); - } - } - break; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_PROTOLIST: - if (HIWORD(wParam) == CBN_SELCHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - if (dat->hwndAdvSearch) { - DestroyWindow(dat->hwndAdvSearch); - dat->hwndAdvSearch = nullptr; - } - if (dat->hwndTinySearch) { - DestroyWindow(dat->hwndTinySearch); - dat->hwndTinySearch = nullptr; - } - SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0); - } - break; - - case IDC_BYPROTOID: - case IDC_BYEMAIL: - case IDC_BYNAME: - { - int count = ListView_GetSelectedCount(hwndList); - if (dat->notSearchedYet) - count = 0; - EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), count); - HideAdvancedSearchDlg(hwndDlg, dat); - } - break; - - case IDC_PROTOID: - if (HIWORD(wParam) == EN_CHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID); - } - break; - - case IDC_EMAIL: - if (HIWORD(wParam) == EN_CHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL); - } - break; - - case IDC_NAMENICK: - case IDC_NAMEFIRST: - case IDC_NAMELAST: - if (HIWORD(wParam) == EN_CHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME); - } - break; - - case IDC_ADVANCED: - EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), ListView_GetSelectedCount(hwndList) > 0); - if (IsDlgButtonChecked(hwndDlg, IDC_ADVANCED)) - ShowAdvancedSearchDlg(hwndDlg, dat); - else - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED); - break; - - case IDCANCEL: - DestroyWindow(hwndDlg); - break; - - case IDOK: - HideAdvancedSearchDlg(hwndDlg, dat); - if (dat->searchCount) { //cancel search - SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search")); - if (dat->hResultHook) { UnhookEvent(dat->hResultHook); dat->hResultHook = nullptr; } - if (dat->search) { mir_free(dat->search); dat->search = nullptr; } - dat->searchCount = 0; - StopThrobber(hwndDlg, dat); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - } - else { - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (dat->search) - mir_free(dat->search), dat->search = nullptr; - dat->searchCount = 0; - dat->hResultHook = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_SEARCHACK); - if (IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndTinySearch); - else if (IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID)) { - wchar_t str[256]; - GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str)); - rtrimw(str); - if (str[0] == 0) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_BASICSEARCH, PF1_BASICSEARCH, str); - } - else if (IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) { - wchar_t str[256]; - GetDlgItemText(hwndDlg, IDC_EMAIL, str, _countof(str)); - rtrimw(str); - if (str[0] == 0) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYEMAIL, PF1_SEARCHBYEMAIL, str); - } - else if (IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) { - wchar_t nick[256], first[256], last[256]; - PROTOSEARCHBYNAME psbn; - GetDlgItemText(hwndDlg, IDC_NAMENICK, nick, _countof(nick)); - GetDlgItemText(hwndDlg, IDC_NAMEFIRST, first, _countof(first)); - GetDlgItemText(hwndDlg, IDC_NAMELAST, last, _countof(last)); - psbn.pszFirstName = first; - psbn.pszLastName = last; - psbn.pszNick = nick; - if (nick[0] == 0 && first[0] == 0 && last[0] == 0) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYNAME, PF1_SEARCHBYNAME, &psbn); - } - else if (IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) { - if (dat->hwndAdvSearch == nullptr) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndAdvSearch); - } - - if (dat->searchCount == 0) { - if (dat->hResultHook) { - UnhookEvent(dat->hResultHook); - dat->hResultHook = nullptr; - } - break; - } - - dat->notSearchedYet = 0; - FreeSearchResults(hwndList); - - CreateResultsColumns(hwndList, dat, szProto); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - SetStatusBarResultInfo(hwndDlg); - StartThrobber(hwndDlg, dat); - SetDlgItemText(hwndDlg, IDOK, TranslateT("Cancel")); - } - break; - - case IDC_ADD: - if (ListView_GetSelectedCount(hwndList) == 1) { - LVITEM lvi; - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_ALL | LVNI_SELECTED); - ListView_GetItem(hwndList, &lvi); - ListSearchResult *lsr = (ListSearchResult*)lvi.lParam; - Contact::AddBySearch(lsr->szProto, &lsr->psr, hwndDlg); - } - else { - wchar_t str[256]; - GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str)); - if (*rtrimw(str) == 0) - break; - - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - - PROTOSEARCHRESULT psr = { 0 }; - psr.cbSize = sizeof(psr); - psr.flags = PSR_UNICODE; - psr.id.w = str; - Contact::AddBySearch(szProto, &psr, hwndDlg); - } - break; - - case IDC_MOREOPTIONS: - GetWindowRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc); - ShowMoreOptionsMenu(hwndDlg, rc.left, rc.bottom); - break; - } - - if (lParam && dat->hwndTinySearch == (HWND)lParam && - HIWORD(wParam) == EN_SETFOCUS && LOWORD(wParam) == 0 && - BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) { - CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM); - } - break; - - case WM_CONTEXTMENU: - { - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - - LVHITTESTINFO lvhti; - lvhti.pt = pt; - ScreenToClient(hwndDlg, &pt); - switch (GetDlgCtrlID(ChildWindowFromPoint(hwndDlg, pt))) { - case IDC_RESULTS: - if (dat->notSearchedYet) - return TRUE; - ScreenToClient(hwndList, &lvhti.pt); - if (ListView_HitTest(hwndList, &lvhti) == -1) - break; - ShowMoreOptionsMenu(hwndDlg, (short)LOWORD(lParam), (short)HIWORD(lParam)); - return TRUE; - } - } - break; - - case HM_SEARCHACK: - { - ACKDATA *ack = (ACKDATA*)lParam; - if (ack->type != ACKTYPE_SEARCH) - break; - - int i; - for (i = 0; i < dat->searchCount; i++) - if (dat->search[i].hProcess == ack->hProcess && dat->search[i].hProcess != nullptr && !mir_strcmp(dat->search[i].szProto, ack->szModule)) break; - if (i == dat->searchCount) - break; - - if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED) { - dat->searchCount--; - memmove(dat->search + i, dat->search + i + 1, sizeof(struct ProtoSearchInfo)*(dat->searchCount - i)); - if (dat->searchCount == 0) { - mir_free(dat->search); - dat->search = nullptr; - UnhookEvent(dat->hResultHook); - dat->hResultHook = nullptr; - SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search")); - StopThrobber(hwndDlg, dat); - } - ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - } - else if (ack->result == ACKRESULT_SEARCHRESULT && ack->lParam) { - CUSTOMSEARCHRESULTS *csr = (CUSTOMSEARCHRESULTS*)ack->lParam; - dat->bFlexSearchResult = TRUE; - PROTOSEARCHRESULT *psr = &csr->psr; - // check if this is column names data (psr->cbSize == 0) - if (psr->cbSize == 0) { // blob contain info about columns - // first remove all exist items - FreeSearchResults(hwndList); - ListView_DeleteAllItems(hwndList); //not sure if previous delete list items too - - //second remove all columns - while (ListView_DeleteColumn(hwndList, 1)); //will delete fist column till it possible - - // now will add columns and captions; - LVCOLUMN lvc = { 0 }; - lvc.mask = LVCF_TEXT; - for (int iColumn = 0; iColumn < csr->nFieldCount; iColumn++) { - lvc.pszText = TranslateW(csr->pszFields[iColumn]); - ListView_InsertColumn(hwndList, iColumn + 1, &lvc); - } - } - else { // blob contain info about found contacts - ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize); - lsr->szProto = ack->szModule; - memcpy(&lsr->psr, psr, psr->cbSize); - - /* Next block is not needed but behavior will be kept */ - lsr->psr.id.w = sttDecodeString(psr->flags, psr->id); - lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick); - lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName); - lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName); - lsr->psr.email.w = sttDecodeString(psr->flags, psr->email); - lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE; - - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_IMAGE; - lvi.lParam = (LPARAM)lsr; - for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) { - char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0); - if (szComboProto == nullptr) continue; - if (!mir_strcmp(szComboProto, ack->szModule)) { - COMBOBOXEXITEM cbei = { 0 }; - cbei.mask = CBEIF_IMAGE; - cbei.iItem = i; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei); - lvi.iImage = cbei.iImage; - } - } - int iItem = ListView_InsertItem(hwndList, &lvi); - for (int col = 0; col < csr->nFieldCount; col++) - SetListItemText(hwndList, iItem, col + 1, csr->pszFields[col]); - - ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); - iItem = 0; - while (ListView_SetColumnWidth(hwndList, iItem++, LVSCW_AUTOSIZE_USEHEADER)); - SetStatusBarResultInfo(hwndDlg); - } - break; - } - else if (ack->result == ACKRESULT_DATA) { - PROTOSEARCHRESULT *psr = (PROTOSEARCHRESULT*)ack->lParam; - ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize); - lsr->szProto = ack->szModule; - - dat->bFlexSearchResult = FALSE; - - memcpy(&lsr->psr, psr, psr->cbSize); - lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick); - lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName); - lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName); - lsr->psr.email.w = sttDecodeString(psr->flags, psr->email); - lsr->psr.id.w = sttDecodeString(psr->flags, psr->id); - lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE; - - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_IMAGE; - lvi.lParam = (LPARAM)lsr; - for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) { - char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0); - if (szComboProto == nullptr) continue; - if (!mir_strcmp(szComboProto, ack->szModule)) { - COMBOBOXEXITEM cbei = { 0 }; - cbei.mask = CBEIF_IMAGE; - cbei.iItem = i; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei); - lvi.iImage = cbei.iImage; - break; - } - } - - int iItem = ListView_InsertItem(hwndList, &lvi); - SetListItemText(hwndList, iItem, 1, lsr->psr.id.w); - SetListItemText(hwndList, iItem, 2, lsr->psr.nick.w); - SetListItemText(hwndList, iItem, 3, lsr->psr.firstName.w); - SetListItemText(hwndList, iItem, 4, lsr->psr.lastName.w); - SetListItemText(hwndList, iItem, 5, lsr->psr.email.w); - SetStatusBarResultInfo(hwndDlg); - } - } - break; - - case WM_CLOSE: - DestroyWindow(hwndDlg); - break; - - case WM_DESTROY: - int len = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXTLEN, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - wchar_t *szProto = (wchar_t*)alloca(sizeof(wchar_t)*(len + 1)); - if (szProto != nullptr) { - *szProto = '\0'; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXT, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), (LPARAM)szProto); - db_set_ws(0, "FindAdd", "LastSearched", szProto); - } - - SaveColumnSizes(hwndList); - if (dat->hResultHook != nullptr) - UnhookEvent(dat->hResultHook); - FreeSearchResults(hwndList); - ImageList_Destroy(dat->himlComboIcons); - mir_free(dat->search); - if (dat->hwndAdvSearch) { - DestroyWindow(dat->hwndAdvSearch); - dat->hwndAdvSearch = nullptr; - } - if (dat->hwndTinySearch) { - DestroyWindow(dat->hwndTinySearch); - dat->hwndTinySearch = nullptr; - } - mir_free(dat); - Window_FreeIcon_IcoLib(hwndDlg); - Utils_SaveWindowPosition(hwndDlg, 0, "FindAdd", ""); - break; - } - return FALSE; -} - -static INT_PTR FindAddCommand(WPARAM, LPARAM) -{ - if (IsWindow(hwndFindAdd)) { - ShowWindow(hwndFindAdd, SW_SHOWNORMAL); - SetForegroundWindow(hwndFindAdd); - SetFocus(hwndFindAdd); - } - else { - int netProtoCount = 0; - - // Make sure we have some networks to search on. This is not ideal since - // this check will be repeated every time the dialog is requested, but it - // must be done since this service can be called from other places than the menu. - // One alternative would be to only create the service if we have network - // protocols loaded but that would delay the creation until MODULE_LOADED and - // that is not good either... - for (auto &pa : g_arAccounts) { - if (!pa->IsEnabled()) - continue; - - int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_ANYSEARCH) - netProtoCount++; - } - if (netProtoCount > 0) - hwndFindAdd = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FINDADD), nullptr, DlgProcFindAdd); - } - return 0; -} - -int FindAddPreShutdown(WPARAM, LPARAM) -{ - if (IsWindow(hwndFindAdd)) - DestroyWindow(hwndFindAdd); - hwndFindAdd = nullptr; - return 0; -} - -int LoadFindAddModule(void) -{ - CreateServiceFunction(MS_FINDADD_FINDADD, FindAddCommand); - - HookEvent(ME_SYSTEM_MODULESLOADED, OnSystemModulesLoaded); - HookEvent(ME_PROTO_ACCLISTCHANGED, OnSystemModulesLoaded); - HookEvent(ME_SYSTEM_PRESHUTDOWN, FindAddPreShutdown); - - CMenuItem mi(&g_plugin); - SET_UID(mi, 0x860556b9, 0x1577, 0x4f6f, 0x8c, 0xb0, 0x93, 0x24, 0xa8, 0x2e, 0x20, 0x92); - mi.position = 500020000; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_FINDUSER); - mi.name.a = LPGEN("&Find/add contacts..."); - mi.pszService = MS_FINDADD_FINDADD; - hMainMenuItem = Menu_AddMainMenuItem(&mi); - return 0; -} - -static int OnSystemModulesLoaded(WPARAM, LPARAM) -{ - int netProtoCount = 0; - - // Make sure we have some networks to search on. - for (auto &pa : g_arAccounts) { - int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_ANYSEARCH) - netProtoCount++; - } - - Menu_ShowItem(hMainMenuItem, netProtoCount != 0); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "findadd.h"
+
+#define TIMERID_THROBBER 111
+
+#define HM_SEARCHACK (WM_USER+10)
+#define M_SETGROUPVISIBILITIES (WM_USER+11)
+
+static HWND hwndFindAdd = nullptr;
+static HANDLE hHookModulesLoaded = nullptr;
+static HGENMENU hMainMenuItem = nullptr;
+static int OnSystemModulesLoaded(WPARAM wParam, LPARAM lParam);
+
+static int FindAddDlgResizer(HWND, LPARAM lParam, UTILRESIZECONTROL *urc)
+{
+ static int y, nextY, oldTop;
+ FindAddDlgData *dat = (FindAddDlgData*)lParam;
+
+ switch (urc->wId) {
+ case IDC_RESULTS:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+
+ case IDOK:
+ dat->minDlgHeight = nextY + urc->rcItem.bottom - urc->rcItem.top;
+ return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM;
+
+ case IDC_ADD:
+ case IDC_MOREOPTIONS:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM;
+
+ case IDC_STATUSBAR:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM;
+
+ case IDC_PROTOIDGROUP: //the resize is always processed in template order
+ nextY = y = urc->rcItem.top;
+ if (dat->showProtoId) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7;
+ break;
+
+ case IDC_EMAILGROUP:
+ oldTop = urc->rcItem.top;
+ y = nextY;
+ if (dat->showEmail) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7;
+ OffsetRect(&urc->rcItem, 0, y - oldTop);
+ return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM;
+
+ case IDC_NAMEGROUP:
+ oldTop = urc->rcItem.top;
+ y = nextY;
+ if (dat->showName) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7;
+ OffsetRect(&urc->rcItem, 0, y - oldTop);
+ return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM;
+
+ case IDC_ADVANCEDGROUP:
+ oldTop = urc->rcItem.top;
+ y = nextY;
+ if (dat->showAdvanced) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7;
+ OffsetRect(&urc->rcItem, 0, y - oldTop);
+ return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM;
+
+ case IDC_TINYEXTENDEDGROUP:
+ oldTop = urc->rcItem.top;
+ y = nextY;
+ if (dat->showTiny) {
+ int height = urc->dlgNewSize.cy - y - (urc->dlgOriginalSize.cy - urc->rcItem.bottom);
+ nextY = y + 200; //min height for custom dialog
+ urc->rcItem.top = urc->rcItem.bottom - height;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM;
+
+ case IDC_BYEMAIL:
+ case IDC_EMAIL:
+ case IDC_BYNAME:
+ case IDC_STNAMENICK:
+ case IDC_STNAMEFIRST:
+ case IDC_STNAMELAST:
+ case IDC_NAMENICK:
+ case IDC_NAMEFIRST:
+ case IDC_NAMELAST:
+ case IDC_BYADVANCED:
+ case IDC_BYCUSTOM:
+ case IDC_ADVANCED:
+ OffsetRect(&urc->rcItem, 0, y - oldTop);
+ return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM;
+
+ case IDC_HEADERBAR:
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+static void RenderThrobber(HDC hdc, RECT *rcItem, int *throbbing, int *pivot)
+{
+ InflateRect(rcItem, -1, 0);
+ int width = rcItem->right - rcItem->left;
+ int height = rcItem->bottom - rcItem->top;
+ int height2 = height / 2;
+
+ if (*throbbing) {
+ /* create memdc */
+ HDC hMemDC = CreateCompatibleDC(nullptr);
+ HBITMAP hBitmap = (HBITMAP)SelectObject(hMemDC, CreateCompatibleBitmap(hdc, width, height));
+ /* flush it */
+ RECT rc;
+ rc.left = rc.top = 0;
+ rc.right = width;
+ rc.bottom = height;
+ HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE);
+ FillRect(hMemDC, &rc, hBr);
+ DeleteObject(hBr);
+ /* set up the pen */
+ HPEN hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 4, GetSysColor(COLOR_BTNSHADOW)));
+ /* draw everything before the pivot */
+ int x = *pivot;
+ while (x > (-height)) {
+ MoveToEx(hMemDC, x + height2, 0, nullptr);
+ LineTo(hMemDC, x - height2, height);
+ x -= 12;
+ }
+
+ /* draw everything after the pivot */
+ x = *pivot;
+ while (x < width + height) {
+ MoveToEx(hMemDC, x + height2, 0, nullptr);
+ LineTo(hMemDC, x - height2, height);
+ x += 12;
+ }
+
+ /* move the pivot */
+ *pivot += 2;
+ /* reset the pivot point if it gets past the rect */
+ if (*pivot > width) *pivot = 0;
+ /* put back the old pen and delete the new one */
+ DeleteObject(SelectObject(hMemDC, hPen));
+ /* cap the top and bottom */
+ hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNFACE)));
+ MoveToEx(hMemDC, 0, 0, nullptr);
+ LineTo(hMemDC, width, 0);
+ MoveToEx(hMemDC, 0, height - 1, nullptr);
+ LineTo(hMemDC, width, height - 1);
+ /* select in the old pen and delete the new pen */
+ DeleteObject(SelectObject(hMemDC, hPen));
+ /* paint to screen */
+ BitBlt(hdc, rcItem->left, rcItem->top, width, height, hMemDC, 0, 0, SRCCOPY);
+ /* select back in the old bitmap and delete the created one, as well as freeing the mem dc. */
+ hBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
+ DeleteObject(hBitmap);
+ DeleteDC(hMemDC);
+ }
+ else {
+ /* just flush the DC */
+ HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE);
+ FillRect(hdc, rcItem, hBr);
+ DeleteObject(hBr);
+ }
+}
+
+static void StartThrobber(HWND hwndDlg, FindAddDlgData *dat)
+{
+ dat->throbbing = 1;
+ SetTimer(hwndDlg, TIMERID_THROBBER, 25, nullptr);
+}
+
+static void StopThrobber(HWND hwndDlg, FindAddDlgData *dat)
+{
+ KillTimer(hwndDlg, TIMERID_THROBBER);
+ dat->throbbing = 0;
+ dat->pivot = 0;
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), nullptr, FALSE);
+}
+
+static LRESULT CALLBACK AdvancedSearchDlgSubclassProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_COMMAND) {
+ HWND parentHwnd = GetParent(hwndDlg);
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDOK, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDOK));
+ SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED));
+ break;
+
+ case IDCANCEL:
+ CheckDlgButton(parentHwnd, IDC_ADVANCED, BST_UNCHECKED);
+ SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDC_ADVANCED, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDC_ADVANCED));
+ SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED));
+ break;
+ }
+ }
+ return mir_callNextSubclass(hwndDlg, AdvancedSearchDlgSubclassProc, msg, wParam, lParam);
+}
+
+static void ShowAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat)
+{
+ char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+ if (szProto == nullptr)
+ return;
+
+ if (dat->hwndAdvSearch == nullptr) {
+ RECT rc;
+ dat->hwndAdvSearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)hwndDlg);
+ if (dat->hwndAdvSearch != nullptr)
+ mir_subclassWindow(dat->hwndAdvSearch, AdvancedSearchDlgSubclassProc);
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_RESULTS), &rc);
+ SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+ }
+
+ AnimateWindow(dat->hwndAdvSearch, 150, AW_ACTIVATE | AW_SLIDE | AW_HOR_POSITIVE);
+ RedrawWindow(dat->hwndAdvSearch, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+
+ CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_CHECKED);
+}
+
+static void ReposTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat)
+{
+ if (dat->hwndTinySearch == nullptr)
+ return;
+
+ RECT rc;
+ RECT clientRect;
+ POINT pt = { 0, 0 };
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_TINYEXTENDEDGROUP), &rc);
+ GetWindowRect(dat->hwndTinySearch, &clientRect);
+ pt.x = rc.left;
+ pt.y = rc.top;
+ ScreenToClient(hwndDlg, &pt);
+ SetWindowPos(dat->hwndTinySearch, nullptr, pt.x + 5, pt.y + 15, rc.right - rc.left - 10, rc.bottom - rc.top - 30, SWP_NOZORDER);
+}
+
+static void ShowTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat)
+{
+ char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+ if (szProto == nullptr)
+ return;
+
+ if (dat->hwndTinySearch == nullptr) {
+ dat->hwndTinySearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)/*GetDlgItem(*/hwndDlg/*, IDC_TINYEXTENDEDGROUP)*/);
+ if (dat->hwndTinySearch)
+ ReposTinySearchDlg(hwndDlg, dat);
+ else
+ dat->showTiny = false;
+ }
+ ShowWindow(dat->hwndTinySearch, SW_SHOW);
+}
+
+static void HideAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat)
+{
+ if (dat->hwndAdvSearch == nullptr)
+ return;
+
+ AnimateWindow(dat->hwndAdvSearch, 150, AW_HIDE | AW_BLEND);
+ CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_UNCHECKED);
+}
+
+void EnableResultButtons(HWND hwndDlg, int enable)
+{
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), enable);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), enable);
+}
+
+static const int controls[] = { IDC_BYPROTOID, IDC_BYEMAIL, IDC_BYNAME, IDC_BYADVANCED, IDC_BYCUSTOM };
+
+static void CheckSearchTypeRadioButton(HWND hwndDlg, int idControl)
+{
+ for (auto &it : controls)
+ CheckDlgButton(hwndDlg, it, idControl == it ? BST_CHECKED : BST_UNCHECKED);
+}
+
+#define sttErrMsg TranslateT("You haven't filled in the search field. Please enter a search term and try again.")
+#define sttErrTitle TranslateT("Search")
+
+static void SetListItemText(HWND hwndList, int idx, int col, wchar_t *szText)
+{
+ if (szText == nullptr || *szText == 0)
+ szText = TranslateT("<not specified>");
+
+ ListView_SetItemText(hwndList, idx, col, szText);
+}
+
+static wchar_t* sttDecodeString(uint32_t dwFlags, MAllStrings &src)
+{
+ if (dwFlags & PSR_UNICODE)
+ return mir_wstrdup(src.w);
+
+ if (dwFlags & PSR_UTF8)
+ return mir_utf8decodeW(src.a);
+
+ return mir_a2u(src.a);
+}
+
+static INT_PTR CALLBACK DlgProcFindAdd(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ FindAddDlgData *dat = (FindAddDlgData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_RESULTS);
+ RECT rc, rc2;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ Window_SetSkinIcon_IcoLib(hwndDlg, SKINICON_OTHER_FINDUSER);
+ dat = (FindAddDlgData*)mir_calloc(sizeof(FindAddDlgData));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ dat->notSearchedYet = 1;
+ dat->iLastColumnSortIndex = 1;
+ dat->bSortAscending = 1;
+ SendDlgItemMessage(hwndDlg, IDC_MOREOPTIONS, BUTTONSETARROW, 1, 0);
+ SendDlgItemMessage(hwndDlg, IDOK, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Ctrl+Search add contact"), BATF_UNICODE);
+
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP);
+
+ GetClientRect(hwndList, &rc);
+ {
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+ lvc.pszText = TranslateT("Results");
+ lvc.cx = rc.right - 1;
+ ListView_InsertColumn(hwndList, 0, &lvc);
+
+ LVITEM lvi;
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = 0;
+ lvi.iSubItem = 0;
+ lvi.pszText = TranslateT("There are no results to display.");
+ ListView_InsertItem(hwndList, &lvi);
+
+ // Allocate a reasonable amount of space in the status bar
+ HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR));
+ SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_GETFONT, 0, 0));
+
+ SIZE textSize;
+ GetTextExtentPoint32(hdc, TranslateT("Searching"), (int)mir_wstrlen(TranslateT("Searching")), &textSize);
+
+ int partWidth[3];
+ partWidth[0] = textSize.cx;
+ GetTextExtentPoint32(hdc, L"01234567890123456789", 20, &textSize);
+ partWidth[0] += textSize.cx;
+ ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc);
+ partWidth[1] = partWidth[0] + 150;
+ partWidth[2] = -1;
+ SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETPARTS, _countof(partWidth), (LPARAM)partWidth);
+ SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETTEXT, 1 | SBT_OWNERDRAW, 0);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat);
+
+ wchar_t *szProto = nullptr;
+ ptrW tszLast(db_get_wsa(0, "FindAdd", "LastSearched"));
+ if (tszLast)
+ szProto = NEWWSTR_ALLOCA(tszLast);
+
+ int index = 0, cbwidth = 0, netProtoCount = 0;
+ for (auto &pa : g_arAccounts) {
+ if (!pa->IsEnabled())
+ continue;
+
+ uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (caps & PF1_ANYSEARCH)
+ netProtoCount++;
+ }
+ dat->himlComboIcons = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, netProtoCount + 1, netProtoCount + 1);
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_SETIMAGELIST, 0, (LPARAM)dat->himlComboIcons);
+
+ COMBOBOXEXITEM cbei;
+ cbei.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM;
+ cbei.iItem = 0;
+ hdc = GetDC(hwndDlg);
+ SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, WM_GETFONT, 0, 0));
+ if (netProtoCount > 1) {
+ cbei.pszText = TranslateT("All networks");
+ GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize);
+ if (textSize.cx > cbwidth)
+ cbwidth = textSize.cx;
+ cbei.iImage = cbei.iSelectedImage = ImageList_AddSkinIcon(dat->himlComboIcons, SKINICON_OTHER_SEARCHALL);
+ cbei.lParam = 0;
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei);
+ cbei.iItem++;
+ }
+
+ for (auto &pa : g_arAccounts) {
+ if (!pa->IsEnabled())
+ continue;
+
+ uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (!(caps & PF1_ANYSEARCH))
+ continue;
+
+ cbei.pszText = pa->tszAccountName;
+ GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize);
+ if (textSize.cx > cbwidth)
+ cbwidth = textSize.cx;
+
+ HICON hIcon = (HICON)CallProtoServiceInt(0, pa->szModuleName, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0);
+ cbei.iImage = cbei.iSelectedImage = ImageList_AddIcon(dat->himlComboIcons, hIcon);
+ DestroyIcon(hIcon);
+ cbei.lParam = (LPARAM)pa->szModuleName;
+ SendDlgItemMessageA(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei);
+ if (szProto && cbei.pszText && !mir_wstrcmp(szProto, pa->tszAccountName))
+ index = cbei.iItem;
+ cbei.iItem++;
+ }
+ cbwidth += 32;
+
+ RECT rect;
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rect);
+ if ((rect.right - rect.left) < cbwidth)
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETDROPPEDWIDTH, cbwidth, 0);
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETCURSEL, index, 0);
+
+ SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0);
+ Utils_RestoreWindowPosition(hwndDlg, 0, "FindAdd", "");
+ }
+ return TRUE;
+
+ case WM_SIZE:
+ Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FINDADD), FindAddDlgResizer, (LPARAM)dat);
+ ReposTinySearchDlg(hwndDlg, dat);
+ SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_SIZE, 0, 0);
+ if (dat->notSearchedYet) {
+ GetClientRect(hwndList, &rc);
+ ListView_SetColumnWidth(hwndList, 0, rc.right);
+ }
+ __fallthrough;
+
+ case WM_MOVE:
+ if (dat && dat->hwndAdvSearch) {
+ GetWindowRect(hwndList, &rc);
+ SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+ }
+ break;
+
+ case WM_GETMINMAXINFO:
+ GetWindowRect(hwndList, &rc);
+ GetWindowRect(hwndDlg, &rc2);
+ {
+ MINMAXINFO *mmi = (MINMAXINFO*)lParam;
+ mmi->ptMinTrackSize.x = rc.left - rc2.left + 10 + GetSystemMetrics(SM_CXFRAME);
+ GetClientRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc);
+ mmi->ptMinTrackSize.x += rc.right + 5;
+ GetClientRect(GetDlgItem(hwndDlg, IDC_ADD), &rc);
+ mmi->ptMinTrackSize.x += rc.right + 5;
+ GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc);
+ mmi->ptMinTrackSize.y = dat->minDlgHeight + 20 + GetSystemMetrics(SM_CYCAPTION) + 2 * GetSystemMetrics(SM_CYFRAME);
+ GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc);
+ mmi->ptMinTrackSize.y += rc.bottom;
+ }
+ return 0;
+
+ case M_SETGROUPVISIBILITIES:
+ dat->showAdvanced = dat->showEmail = dat->showName = dat->showProtoId = dat->showTiny = 0;
+ {
+ char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+ if (szProto == (char *)CB_ERR)
+ break;
+ if (szProto == nullptr) {
+ for (auto &pa : g_arAccounts) {
+ if (pa->IsEnabled()) {
+ uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1;
+ if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1;
+ }
+ }
+ }
+ else {
+ uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (protoCaps & PF1_BASICSEARCH) dat->showProtoId = 1;
+ if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1;
+ if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1;
+
+ if (protoCaps & PF1_EXTSEARCHUI) dat->showAdvanced = 1;
+ else if (protoCaps & PF1_EXTSEARCH) dat->showTiny = 1;
+
+ if (protoCaps & PF1_USERIDISEMAIL && dat->showProtoId) { dat->showProtoId = 0; dat->showEmail = 1; }
+ if (dat->showProtoId) {
+ wchar_t *wszUniqueId = (wchar_t *)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAG_UNIQUEIDTEXT, 0);
+ if (wszUniqueId)
+ SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, wszUniqueId);
+ else
+ SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, TranslateT("Handle"));
+
+ if (protoCaps & PF1_NUMERICUSERID)
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE) | ES_NUMBER);
+ else
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE)&~ES_NUMBER);
+ }
+ }
+
+ if (dat->showTiny)
+ ShowTinySearchDlg(hwndDlg, dat);
+ else if (dat->hwndTinySearch) {
+ DestroyWindow(dat->hwndTinySearch);
+ dat->hwndTinySearch = nullptr;
+ }
+
+#define en(id, t) ShowWindow( GetDlgItem(hwndDlg, IDC_##id), dat->show##t?SW_SHOW:SW_HIDE)
+ en(PROTOIDGROUP, ProtoId); en(BYPROTOID, ProtoId); en(PROTOID, ProtoId);
+ en(EMAILGROUP, Email); en(BYEMAIL, Email); en(EMAIL, Email);
+ en(NAMEGROUP, Name); en(BYNAME, Name);
+ en(STNAMENICK, Name); en(NAMENICK, Name);
+ en(STNAMEFIRST, Name); en(NAMEFIRST, Name);
+ en(STNAMELAST, Name); en(NAMELAST, Name);
+ en(ADVANCEDGROUP, Advanced); en(BYADVANCED, Advanced); en(ADVANCED, Advanced);
+ en(BYCUSTOM, Tiny); en(TINYEXTENDEDGROUP, Tiny);
+#undef en
+ int checkmarkVisible = (dat->showAdvanced && IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) ||
+ (dat->showEmail && IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) ||
+ (dat->showTiny && IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) ||
+ (dat->showName && IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) ||
+ (dat->showProtoId && IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID));
+ if (!checkmarkVisible) {
+ if (dat->showProtoId) CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID);
+ else if (dat->showEmail) CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL);
+ else if (dat->showName) CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME);
+ else if (dat->showAdvanced) CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED);
+ else if (dat->showTiny) CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM);
+ }
+
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+
+ MINMAXINFO mmi;
+ SendMessage(hwndDlg, WM_GETMINMAXINFO, 0, (LPARAM)&mmi);
+
+ GetWindowRect(hwndDlg, &rc);
+ if (rc.bottom - rc.top < mmi.ptMinTrackSize.y)
+ SetWindowPos(hwndDlg, nullptr, 0, 0, rc.right - rc.left, mmi.ptMinTrackSize.y, SWP_NOZORDER | SWP_NOMOVE);
+ }
+ break;
+
+ case WM_TIMER:
+ if (wParam == TIMERID_THROBBER) {
+ int borders[3];
+ SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETBORDERS, 0, (LPARAM)borders);
+
+ SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETRECT, 1, (LPARAM)&rc);
+ InflateRect(&rc, -borders[2] / 2, -borders[1] / 2);
+ HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR));
+ RenderThrobber(hdc, &rc, &dat->throbbing, &dat->pivot);
+ ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc);
+ }
+ break;
+
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)lParam;
+ if (dis->CtlID == IDC_STATUSBAR && dis->itemID == 1) {
+ RenderThrobber(dis->hDC, &dis->rcItem, &dat->throbbing, &dat->pivot);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (wParam == IDC_RESULTS) {
+ switch (((LPNMHDR)lParam)->code) {
+ case LVN_ITEMCHANGED:
+ {
+ int count = ListView_GetSelectedCount(hwndList);
+ if (dat->notSearchedYet)
+ count = 0;
+ EnableResultButtons(hwndDlg, count);
+ }
+ break;
+
+ case LVN_COLUMNCLICK:
+ HDITEM hdi;
+ hdi.mask = HDI_FORMAT;
+ hdi.fmt = HDF_LEFT | HDF_STRING;
+ Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi);
+
+ LPNMLISTVIEW nmlv = (LPNMLISTVIEW)lParam;
+ if (nmlv->iSubItem != dat->iLastColumnSortIndex) {
+ dat->bSortAscending = TRUE;
+ dat->iLastColumnSortIndex = nmlv->iSubItem;
+ }
+ else dat->bSortAscending = !dat->bSortAscending;
+
+ hdi.fmt = HDF_LEFT | HDF_STRING | (dat->bSortAscending ? HDF_SORTDOWN : HDF_SORTUP);
+ Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi);
+
+ ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg);
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_PROTOLIST:
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ if (dat->hwndAdvSearch) {
+ DestroyWindow(dat->hwndAdvSearch);
+ dat->hwndAdvSearch = nullptr;
+ }
+ if (dat->hwndTinySearch) {
+ DestroyWindow(dat->hwndTinySearch);
+ dat->hwndTinySearch = nullptr;
+ }
+ SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0);
+ }
+ break;
+
+ case IDC_BYPROTOID:
+ case IDC_BYEMAIL:
+ case IDC_BYNAME:
+ {
+ int count = ListView_GetSelectedCount(hwndList);
+ if (dat->notSearchedYet)
+ count = 0;
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), count);
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ }
+ break;
+
+ case IDC_PROTOID:
+ if (HIWORD(wParam) == EN_CHANGE) {
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID);
+ }
+ break;
+
+ case IDC_EMAIL:
+ if (HIWORD(wParam) == EN_CHANGE) {
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL);
+ }
+ break;
+
+ case IDC_NAMENICK:
+ case IDC_NAMEFIRST:
+ case IDC_NAMELAST:
+ if (HIWORD(wParam) == EN_CHANGE) {
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME);
+ }
+ break;
+
+ case IDC_ADVANCED:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), ListView_GetSelectedCount(hwndList) > 0);
+ if (IsDlgButtonChecked(hwndDlg, IDC_ADVANCED))
+ ShowAdvancedSearchDlg(hwndDlg, dat);
+ else
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED);
+ break;
+
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case IDOK:
+ HideAdvancedSearchDlg(hwndDlg, dat);
+ if (dat->searchCount) { //cancel search
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search"));
+ if (dat->hResultHook) { UnhookEvent(dat->hResultHook); dat->hResultHook = nullptr; }
+ if (dat->search) { mir_free(dat->search); dat->search = nullptr; }
+ dat->searchCount = 0;
+ StopThrobber(hwndDlg, dat);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat);
+ }
+ else {
+ char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+ if (dat->search)
+ mir_free(dat->search), dat->search = nullptr;
+ dat->searchCount = 0;
+ dat->hResultHook = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_SEARCHACK);
+ if (IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM))
+ BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndTinySearch);
+ else if (IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID)) {
+ wchar_t str[256];
+ GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str));
+ rtrimw(str);
+ if (str[0] == 0)
+ MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK);
+ else
+ BeginSearch(hwndDlg, dat, szProto, PS_BASICSEARCH, PF1_BASICSEARCH, str);
+ }
+ else if (IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) {
+ wchar_t str[256];
+ GetDlgItemText(hwndDlg, IDC_EMAIL, str, _countof(str));
+ rtrimw(str);
+ if (str[0] == 0)
+ MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK);
+ else
+ BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYEMAIL, PF1_SEARCHBYEMAIL, str);
+ }
+ else if (IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) {
+ wchar_t nick[256], first[256], last[256];
+ PROTOSEARCHBYNAME psbn;
+ GetDlgItemText(hwndDlg, IDC_NAMENICK, nick, _countof(nick));
+ GetDlgItemText(hwndDlg, IDC_NAMEFIRST, first, _countof(first));
+ GetDlgItemText(hwndDlg, IDC_NAMELAST, last, _countof(last));
+ psbn.pszFirstName = first;
+ psbn.pszLastName = last;
+ psbn.pszNick = nick;
+ if (nick[0] == 0 && first[0] == 0 && last[0] == 0)
+ MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK);
+ else
+ BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYNAME, PF1_SEARCHBYNAME, &psbn);
+ }
+ else if (IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) {
+ if (dat->hwndAdvSearch == nullptr)
+ MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK);
+ else
+ BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndAdvSearch);
+ }
+
+ if (dat->searchCount == 0) {
+ if (dat->hResultHook) {
+ UnhookEvent(dat->hResultHook);
+ dat->hResultHook = nullptr;
+ }
+ break;
+ }
+
+ dat->notSearchedYet = 0;
+ FreeSearchResults(hwndList);
+
+ CreateResultsColumns(hwndList, dat, szProto);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat);
+ SetStatusBarResultInfo(hwndDlg);
+ StartThrobber(hwndDlg, dat);
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("Cancel"));
+ }
+ break;
+
+ case IDC_ADD:
+ if (ListView_GetSelectedCount(hwndList) == 1) {
+ LVITEM lvi;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_ALL | LVNI_SELECTED);
+ ListView_GetItem(hwndList, &lvi);
+ ListSearchResult *lsr = (ListSearchResult*)lvi.lParam;
+ Contact::AddBySearch(lsr->szProto, &lsr->psr, hwndDlg);
+ }
+ else {
+ wchar_t str[256];
+ GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str));
+ if (*rtrimw(str) == 0)
+ break;
+
+ char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA,
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+
+ PROTOSEARCHRESULT psr = { 0 };
+ psr.cbSize = sizeof(psr);
+ psr.flags = PSR_UNICODE;
+ psr.id.w = str;
+ Contact::AddBySearch(szProto, &psr, hwndDlg);
+ }
+ break;
+
+ case IDC_MOREOPTIONS:
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc);
+ ShowMoreOptionsMenu(hwndDlg, rc.left, rc.bottom);
+ break;
+ }
+
+ if (lParam && dat->hwndTinySearch == (HWND)lParam &&
+ HIWORD(wParam) == EN_SETFOCUS && LOWORD(wParam) == 0 &&
+ BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) {
+ CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM);
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+
+ LVHITTESTINFO lvhti;
+ lvhti.pt = pt;
+ ScreenToClient(hwndDlg, &pt);
+ switch (GetDlgCtrlID(ChildWindowFromPoint(hwndDlg, pt))) {
+ case IDC_RESULTS:
+ if (dat->notSearchedYet)
+ return TRUE;
+ ScreenToClient(hwndList, &lvhti.pt);
+ if (ListView_HitTest(hwndList, &lvhti) == -1)
+ break;
+ ShowMoreOptionsMenu(hwndDlg, (short)LOWORD(lParam), (short)HIWORD(lParam));
+ return TRUE;
+ }
+ }
+ break;
+
+ case HM_SEARCHACK:
+ {
+ ACKDATA *ack = (ACKDATA*)lParam;
+ if (ack->type != ACKTYPE_SEARCH)
+ break;
+
+ int i;
+ for (i = 0; i < dat->searchCount; i++)
+ if (dat->search[i].hProcess == ack->hProcess && dat->search[i].hProcess != nullptr && !mir_strcmp(dat->search[i].szProto, ack->szModule)) break;
+ if (i == dat->searchCount)
+ break;
+
+ if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED) {
+ dat->searchCount--;
+ memmove(dat->search + i, dat->search + i + 1, sizeof(struct ProtoSearchInfo)*(dat->searchCount - i));
+ if (dat->searchCount == 0) {
+ mir_free(dat->search);
+ dat->search = nullptr;
+ UnhookEvent(dat->hResultHook);
+ dat->hResultHook = nullptr;
+ SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search"));
+ StopThrobber(hwndDlg, dat);
+ }
+ ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg);
+ SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat);
+ }
+ else if (ack->result == ACKRESULT_SEARCHRESULT && ack->lParam) {
+ CUSTOMSEARCHRESULTS *csr = (CUSTOMSEARCHRESULTS*)ack->lParam;
+ dat->bFlexSearchResult = TRUE;
+ PROTOSEARCHRESULT *psr = &csr->psr;
+ // check if this is column names data (psr->cbSize == 0)
+ if (psr->cbSize == 0) { // blob contain info about columns
+ // first remove all exist items
+ FreeSearchResults(hwndList);
+ ListView_DeleteAllItems(hwndList); //not sure if previous delete list items too
+
+ //second remove all columns
+ while (ListView_DeleteColumn(hwndList, 1)); //will delete fist column till it possible
+
+ // now will add columns and captions;
+ LVCOLUMN lvc = { 0 };
+ lvc.mask = LVCF_TEXT;
+ for (int iColumn = 0; iColumn < csr->nFieldCount; iColumn++) {
+ lvc.pszText = TranslateW(csr->pszFields[iColumn]);
+ ListView_InsertColumn(hwndList, iColumn + 1, &lvc);
+ }
+ }
+ else { // blob contain info about found contacts
+ ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize);
+ lsr->szProto = ack->szModule;
+ memcpy(&lsr->psr, psr, psr->cbSize);
+
+ /* Next block is not needed but behavior will be kept */
+ lsr->psr.id.w = sttDecodeString(psr->flags, psr->id);
+ lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick);
+ lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName);
+ lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName);
+ lsr->psr.email.w = sttDecodeString(psr->flags, psr->email);
+ lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE;
+
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM | LVIF_IMAGE;
+ lvi.lParam = (LPARAM)lsr;
+ for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) {
+ char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0);
+ if (szComboProto == nullptr) continue;
+ if (!mir_strcmp(szComboProto, ack->szModule)) {
+ COMBOBOXEXITEM cbei = { 0 };
+ cbei.mask = CBEIF_IMAGE;
+ cbei.iItem = i;
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei);
+ lvi.iImage = cbei.iImage;
+ }
+ }
+ int iItem = ListView_InsertItem(hwndList, &lvi);
+ for (int col = 0; col < csr->nFieldCount; col++)
+ SetListItemText(hwndList, iItem, col + 1, csr->pszFields[col]);
+
+ ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg);
+ iItem = 0;
+ while (ListView_SetColumnWidth(hwndList, iItem++, LVSCW_AUTOSIZE_USEHEADER));
+ SetStatusBarResultInfo(hwndDlg);
+ }
+ break;
+ }
+ else if (ack->result == ACKRESULT_DATA) {
+ PROTOSEARCHRESULT *psr = (PROTOSEARCHRESULT*)ack->lParam;
+ ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize);
+ lsr->szProto = ack->szModule;
+
+ dat->bFlexSearchResult = FALSE;
+
+ memcpy(&lsr->psr, psr, psr->cbSize);
+ lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick);
+ lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName);
+ lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName);
+ lsr->psr.email.w = sttDecodeString(psr->flags, psr->email);
+ lsr->psr.id.w = sttDecodeString(psr->flags, psr->id);
+ lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE;
+
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM | LVIF_IMAGE;
+ lvi.lParam = (LPARAM)lsr;
+ for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) {
+ char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0);
+ if (szComboProto == nullptr) continue;
+ if (!mir_strcmp(szComboProto, ack->szModule)) {
+ COMBOBOXEXITEM cbei = { 0 };
+ cbei.mask = CBEIF_IMAGE;
+ cbei.iItem = i;
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei);
+ lvi.iImage = cbei.iImage;
+ break;
+ }
+ }
+
+ int iItem = ListView_InsertItem(hwndList, &lvi);
+ SetListItemText(hwndList, iItem, 1, lsr->psr.id.w);
+ SetListItemText(hwndList, iItem, 2, lsr->psr.nick.w);
+ SetListItemText(hwndList, iItem, 3, lsr->psr.firstName.w);
+ SetListItemText(hwndList, iItem, 4, lsr->psr.lastName.w);
+ SetListItemText(hwndList, iItem, 5, lsr->psr.email.w);
+ SetStatusBarResultInfo(hwndDlg);
+ }
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ int len = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXTLEN, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0);
+ wchar_t *szProto = (wchar_t*)alloca(sizeof(wchar_t)*(len + 1));
+ if (szProto != nullptr) {
+ *szProto = '\0';
+ SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXT, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), (LPARAM)szProto);
+ db_set_ws(0, "FindAdd", "LastSearched", szProto);
+ }
+
+ SaveColumnSizes(hwndList);
+ if (dat->hResultHook != nullptr)
+ UnhookEvent(dat->hResultHook);
+ FreeSearchResults(hwndList);
+ ImageList_Destroy(dat->himlComboIcons);
+ mir_free(dat->search);
+ if (dat->hwndAdvSearch) {
+ DestroyWindow(dat->hwndAdvSearch);
+ dat->hwndAdvSearch = nullptr;
+ }
+ if (dat->hwndTinySearch) {
+ DestroyWindow(dat->hwndTinySearch);
+ dat->hwndTinySearch = nullptr;
+ }
+ mir_free(dat);
+ Window_FreeIcon_IcoLib(hwndDlg);
+ Utils_SaveWindowPosition(hwndDlg, 0, "FindAdd", "");
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR FindAddCommand(WPARAM, LPARAM)
+{
+ if (IsWindow(hwndFindAdd)) {
+ ShowWindow(hwndFindAdd, SW_SHOWNORMAL);
+ SetForegroundWindow(hwndFindAdd);
+ SetFocus(hwndFindAdd);
+ }
+ else {
+ int netProtoCount = 0;
+
+ // Make sure we have some networks to search on. This is not ideal since
+ // this check will be repeated every time the dialog is requested, but it
+ // must be done since this service can be called from other places than the menu.
+ // One alternative would be to only create the service if we have network
+ // protocols loaded but that would delay the creation until MODULE_LOADED and
+ // that is not good either...
+ for (auto &pa : g_arAccounts) {
+ if (!pa->IsEnabled())
+ continue;
+
+ int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (protoCaps & PF1_ANYSEARCH)
+ netProtoCount++;
+ }
+ if (netProtoCount > 0)
+ hwndFindAdd = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FINDADD), nullptr, DlgProcFindAdd);
+ }
+ return 0;
+}
+
+int FindAddPreShutdown(WPARAM, LPARAM)
+{
+ if (IsWindow(hwndFindAdd))
+ DestroyWindow(hwndFindAdd);
+ hwndFindAdd = nullptr;
+ return 0;
+}
+
+int LoadFindAddModule(void)
+{
+ CreateServiceFunction(MS_FINDADD_FINDADD, FindAddCommand);
+
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnSystemModulesLoaded);
+ HookEvent(ME_PROTO_ACCLISTCHANGED, OnSystemModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, FindAddPreShutdown);
+
+ CMenuItem mi(&g_plugin);
+ SET_UID(mi, 0x860556b9, 0x1577, 0x4f6f, 0x8c, 0xb0, 0x93, 0x24, 0xa8, 0x2e, 0x20, 0x92);
+ mi.position = 500020000;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_FINDUSER);
+ mi.name.a = LPGEN("&Find/add contacts...");
+ mi.pszService = MS_FINDADD_FINDADD;
+ hMainMenuItem = Menu_AddMainMenuItem(&mi);
+ return 0;
+}
+
+static int OnSystemModulesLoaded(WPARAM, LPARAM)
+{
+ int netProtoCount = 0;
+
+ // Make sure we have some networks to search on.
+ for (auto &pa : g_arAccounts) {
+ int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (protoCaps & PF1_ANYSEARCH)
+ netProtoCount++;
+ }
+
+ Menu_ShowItem(hMainMenuItem, netProtoCount != 0);
+ return 0;
+}
diff --git a/src/mir_app/src/findadd.h b/src/mir_app/src/findadd.h index 81a546fa1a..e0993d696b 100644 --- a/src/mir_app/src/findadd.h +++ b/src/mir_app/src/findadd.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/genmenu.h b/src/mir_app/src/genmenu.h index 693a5bf9a7..8ed7a83359 100644 --- a/src/mir_app/src/genmenu.h +++ b/src/mir_app/src/genmenu.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/headerbar.cpp b/src/mir_app/src/headerbar.cpp index 319334f7a9..f312b22145 100644 --- a/src/mir_app/src/headerbar.cpp +++ b/src/mir_app/src/headerbar.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
Copyright (c) 2007 Artem Shpynov
all portions of this codebase are copyrighted to the people
diff --git a/src/mir_app/src/help.cpp b/src/mir_app/src/help.cpp index 30bd95bcbf..dca9b784b6 100644 --- a/src/mir_app/src/help.cpp +++ b/src/mir_app/src/help.cpp @@ -1,189 +1,189 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "resource.h" - -static class CAboutDlg *pAboutDialog; - -class CAboutDlg : public CDlgBase -{ - int m_iState = 0; - - CCtrlBase ctrlHeaderBar, ctrlDevelopers, ctrlCredits, ctrlWhiteRect; - CCtrlButton btnLink; - -public: - CAboutDlg() : - CDlgBase(g_plugin, IDD_ABOUT), - btnLink(this, IDC_CONTRIBLINK), - ctrlCredits(this, IDC_CREDITSFILE), - ctrlHeaderBar(this, IDC_HEADERBAR), - ctrlWhiteRect(this, IDC_WHITERECT), - ctrlDevelopers(this, IDC_DEVS) - { - btnLink.OnClick = Callback(this, &CAboutDlg::onClick); - - ctrlCredits.UseSystemColors(); - ctrlWhiteRect.UseSystemColors(); - ctrlDevelopers.UseSystemColors(); - } - - bool OnInitDialog() override - { - ptrW wszCopyright(mir_utf8decodeW(LEGAL_COPYRIGHT)); - if (wszCopyright == nullptr) - wszCopyright = mir_a2u(LEGAL_COPYRIGHT); - ctrlDevelopers.SetText(wszCopyright); - - char productVersion[56]; - Miranda_GetVersionText(productVersion, _countof(productVersion)); - ctrlHeaderBar.SetText(CMStringW(FORMAT, L"Miranda NG\nv%S", productVersion)); - - HRSRC hResInfo = FindResource(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CREDITS), L"TEXT"); - uint32_t ResSize = SizeofResource(g_plugin.getInst(), hResInfo); - HGLOBAL hRes = LoadResource(g_plugin.getInst(), hResInfo); - char *pszMsg = (char*)LockResource(hRes); - if (pszMsg) { - char *pszMsgt = (char*)alloca(ResSize + 1); - memcpy(pszMsgt, pszMsg, ResSize); pszMsgt[ResSize] = 0; - - ptrW ptszMsg; - if (ResSize >= 3 && pszMsgt[0] == '\xef' && pszMsgt[1] == '\xbb' && pszMsgt[2] == '\xbf') - ptszMsg = mir_utf8decodeW(pszMsgt + 3); - else - ptszMsg = mir_a2u_cp(pszMsgt, 1252); - ctrlCredits.SetText(ptszMsg); - - UnlockResource(pszMsg); - } - FreeResource(hRes); - ctrlCredits.Hide(); - - Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_OTHER_MIRANDA); - return true; - } - - void OnDestroy() override - { - pAboutDialog = nullptr; - Window_FreeIcon_IcoLib(m_hwnd); - } - - void onClick(CCtrlButton*) - { - if (m_iState) { - btnLink.SetText(TranslateT("Credits >")); - ctrlDevelopers.Show(); - ctrlCredits.Hide(); - } - else { - btnLink.SetText(TranslateT("< Copyright")); - ctrlDevelopers.Hide(); - ctrlCredits.Show(); - } - m_iState = !m_iState; - } -}; - -static INT_PTR AboutCommand(WPARAM wParam, LPARAM) -{ - if (pAboutDialog) { - SetForegroundWindow(pAboutDialog->GetHwnd()); - SetFocus(pAboutDialog->GetHwnd()); - } - else { - pAboutDialog = new CAboutDlg(); - pAboutDialog->SetParent((HWND)wParam); - pAboutDialog->Show(); - } - return 0; -} - -static INT_PTR IndexCommand(WPARAM, LPARAM) -{ - Utils_OpenUrl("https://wiki.miranda-ng.org"); - return 0; -} - -static INT_PTR WebsiteCommand(WPARAM, LPARAM) -{ - Utils_OpenUrl("https://miranda-ng.org"); - return 0; -} - -static INT_PTR BugCommand(WPARAM, LPARAM) -{ - Utils_OpenUrl("https://github.com/miranda-ng/miranda-ng/issues/new"); - return 0; -} - -int ShutdownHelpModule(WPARAM, LPARAM) -{ - if (pAboutDialog) - pAboutDialog->Close(); - return 0; -} - -int LoadHelpModule(void) -{ - HookEvent(ME_SYSTEM_PRESHUTDOWN, ShutdownHelpModule); - - CMenuItem mi(&g_plugin); - mi.root = g_plugin.addRootMenu(MO_MAIN, LPGENW("&Help"), 2000090000); - Menu_ConfigureItem(mi.root, MCI_OPT_UID, "8824ECA5-6942-46D7-9D07-1BA600E0D02E"); - - SET_UID(mi, 0xf3ebf1fa, 0x587c, 0x494d, 0xbd, 0x33, 0x7f, 0x88, 0xb3, 0x61, 0x1e, 0xd3); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA); - mi.position = 2000090000; - mi.name.a = LPGEN("&About..."); - mi.pszService = "Help/AboutCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, AboutCommand); - - SET_UID(mi, 0x495df66f, 0x844e, 0x479a, 0xaf, 0x21, 0x3e, 0x42, 0xc5, 0x14, 0x7c, 0x7e); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HELP); - mi.position = -500050000; - mi.name.a = LPGEN("&Support"); - mi.pszService = "Help/IndexCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, IndexCommand); - - SET_UID(mi, 0x15e18b58, 0xec73, 0x45c2, 0xb9, 0xf4, 0x2a, 0xfe, 0xc2, 0xb7, 0xd3, 0x25); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDAWEB); - mi.position = 2000050000; - mi.name.a = LPGEN("&Miranda NG homepage"); - mi.pszService = "Help/WebsiteCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, WebsiteCommand); - - SET_UID(mi, 0xe7d0fe8b, 0xfdeb, 0x45b3, 0xba, 0x83, 0x3, 0x1e, 0x15, 0xda, 0x7e, 0x52); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_EVENT_URL); - mi.position = 2000040000; - mi.name.a = LPGEN("&Report bug"); - mi.pszService = "Help/BugCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, BugCommand); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "resource.h"
+
+static class CAboutDlg *pAboutDialog;
+
+class CAboutDlg : public CDlgBase
+{
+ int m_iState = 0;
+
+ CCtrlBase ctrlHeaderBar, ctrlDevelopers, ctrlCredits, ctrlWhiteRect;
+ CCtrlButton btnLink;
+
+public:
+ CAboutDlg() :
+ CDlgBase(g_plugin, IDD_ABOUT),
+ btnLink(this, IDC_CONTRIBLINK),
+ ctrlCredits(this, IDC_CREDITSFILE),
+ ctrlHeaderBar(this, IDC_HEADERBAR),
+ ctrlWhiteRect(this, IDC_WHITERECT),
+ ctrlDevelopers(this, IDC_DEVS)
+ {
+ btnLink.OnClick = Callback(this, &CAboutDlg::onClick);
+
+ ctrlCredits.UseSystemColors();
+ ctrlWhiteRect.UseSystemColors();
+ ctrlDevelopers.UseSystemColors();
+ }
+
+ bool OnInitDialog() override
+ {
+ ptrW wszCopyright(mir_utf8decodeW(LEGAL_COPYRIGHT));
+ if (wszCopyright == nullptr)
+ wszCopyright = mir_a2u(LEGAL_COPYRIGHT);
+ ctrlDevelopers.SetText(wszCopyright);
+
+ char productVersion[56];
+ Miranda_GetVersionText(productVersion, _countof(productVersion));
+ ctrlHeaderBar.SetText(CMStringW(FORMAT, L"Miranda NG\nv%S", productVersion));
+
+ HRSRC hResInfo = FindResource(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CREDITS), L"TEXT");
+ uint32_t ResSize = SizeofResource(g_plugin.getInst(), hResInfo);
+ HGLOBAL hRes = LoadResource(g_plugin.getInst(), hResInfo);
+ char *pszMsg = (char*)LockResource(hRes);
+ if (pszMsg) {
+ char *pszMsgt = (char*)alloca(ResSize + 1);
+ memcpy(pszMsgt, pszMsg, ResSize); pszMsgt[ResSize] = 0;
+
+ ptrW ptszMsg;
+ if (ResSize >= 3 && pszMsgt[0] == '\xef' && pszMsgt[1] == '\xbb' && pszMsgt[2] == '\xbf')
+ ptszMsg = mir_utf8decodeW(pszMsgt + 3);
+ else
+ ptszMsg = mir_a2u_cp(pszMsgt, 1252);
+ ctrlCredits.SetText(ptszMsg);
+
+ UnlockResource(pszMsg);
+ }
+ FreeResource(hRes);
+ ctrlCredits.Hide();
+
+ Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_OTHER_MIRANDA);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ pAboutDialog = nullptr;
+ Window_FreeIcon_IcoLib(m_hwnd);
+ }
+
+ void onClick(CCtrlButton*)
+ {
+ if (m_iState) {
+ btnLink.SetText(TranslateT("Credits >"));
+ ctrlDevelopers.Show();
+ ctrlCredits.Hide();
+ }
+ else {
+ btnLink.SetText(TranslateT("< Copyright"));
+ ctrlDevelopers.Hide();
+ ctrlCredits.Show();
+ }
+ m_iState = !m_iState;
+ }
+};
+
+static INT_PTR AboutCommand(WPARAM wParam, LPARAM)
+{
+ if (pAboutDialog) {
+ SetForegroundWindow(pAboutDialog->GetHwnd());
+ SetFocus(pAboutDialog->GetHwnd());
+ }
+ else {
+ pAboutDialog = new CAboutDlg();
+ pAboutDialog->SetParent((HWND)wParam);
+ pAboutDialog->Show();
+ }
+ return 0;
+}
+
+static INT_PTR IndexCommand(WPARAM, LPARAM)
+{
+ Utils_OpenUrl("https://wiki.miranda-ng.org");
+ return 0;
+}
+
+static INT_PTR WebsiteCommand(WPARAM, LPARAM)
+{
+ Utils_OpenUrl("https://miranda-ng.org");
+ return 0;
+}
+
+static INT_PTR BugCommand(WPARAM, LPARAM)
+{
+ Utils_OpenUrl("https://github.com/miranda-ng/miranda-ng/issues/new");
+ return 0;
+}
+
+int ShutdownHelpModule(WPARAM, LPARAM)
+{
+ if (pAboutDialog)
+ pAboutDialog->Close();
+ return 0;
+}
+
+int LoadHelpModule(void)
+{
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, ShutdownHelpModule);
+
+ CMenuItem mi(&g_plugin);
+ mi.root = g_plugin.addRootMenu(MO_MAIN, LPGENW("&Help"), 2000090000);
+ Menu_ConfigureItem(mi.root, MCI_OPT_UID, "8824ECA5-6942-46D7-9D07-1BA600E0D02E");
+
+ SET_UID(mi, 0xf3ebf1fa, 0x587c, 0x494d, 0xbd, 0x33, 0x7f, 0x88, 0xb3, 0x61, 0x1e, 0xd3);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA);
+ mi.position = 2000090000;
+ mi.name.a = LPGEN("&About...");
+ mi.pszService = "Help/AboutCommand";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, AboutCommand);
+
+ SET_UID(mi, 0x495df66f, 0x844e, 0x479a, 0xaf, 0x21, 0x3e, 0x42, 0xc5, 0x14, 0x7c, 0x7e);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HELP);
+ mi.position = -500050000;
+ mi.name.a = LPGEN("&Support");
+ mi.pszService = "Help/IndexCommand";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, IndexCommand);
+
+ SET_UID(mi, 0x15e18b58, 0xec73, 0x45c2, 0xb9, 0xf4, 0x2a, 0xfe, 0xc2, 0xb7, 0xd3, 0x25);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDAWEB);
+ mi.position = 2000050000;
+ mi.name.a = LPGEN("&Miranda NG homepage");
+ mi.pszService = "Help/WebsiteCommand";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, WebsiteCommand);
+
+ SET_UID(mi, 0xe7d0fe8b, 0xfdeb, 0x45b3, 0xba, 0x83, 0x3, 0x1e, 0x15, 0xda, 0x7e, 0x52);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_EVENT_URL);
+ mi.position = 2000040000;
+ mi.name.a = LPGEN("&Report bug");
+ mi.pszService = "Help/BugCommand";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, BugCommand);
+ return 0;
+}
diff --git a/src/mir_app/src/hotkey_opts.cpp b/src/mir_app/src/hotkey_opts.cpp index 7221e4df60..04d78d2bf8 100644 --- a/src/mir_app/src/hotkey_opts.cpp +++ b/src/mir_app/src/hotkey_opts.cpp @@ -1,1026 +1,1026 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ -#include "stdafx.h" - -#include <m_hotkeys.h> -#include "skin.h" - -static wchar_t *sttHokeyVkToName(uint16_t vkKey) -{ - static wchar_t buf[256] = {}; - uint32_t code = MapVirtualKey(vkKey, 0) << 16; - - switch (vkKey) { - case 0: - case VK_CONTROL: - case VK_SHIFT: - case VK_MENU: - case VK_LWIN: - case VK_RWIN: - case VK_PAUSE: - case VK_CANCEL: - case VK_NUMLOCK: - case VK_CAPITAL: - case VK_SCROLL: - return L""; - case VK_BROWSER_BACK: - return TranslateT("Browser: Back"); - case VK_BROWSER_FORWARD: - return TranslateT("Browser: Forward"); - case VK_BROWSER_REFRESH: - return TranslateT("Browser: Refresh"); - case VK_BROWSER_STOP: - return TranslateT("Browser: Stop"); - case VK_BROWSER_SEARCH: - return TranslateT("Browser: Search"); - case VK_BROWSER_FAVORITES: - return TranslateT("Browser: Fav"); - case VK_BROWSER_HOME: - return TranslateT("Browser: Home"); - case VK_VOLUME_MUTE: - return TranslateT("Mute"); - case VK_VOLUME_DOWN: - return TranslateT("Vol-"); - case VK_VOLUME_UP: - return TranslateT("Vol+"); - case VK_MEDIA_NEXT_TRACK: - return TranslateT("Media: Next Track"); - case VK_MEDIA_PREV_TRACK: - return TranslateT("Media: Prev. Track"); - case VK_MEDIA_STOP: - return TranslateT("Media: Stop"); - case VK_MEDIA_PLAY_PAUSE: - return TranslateT("Media: Play/Pause"); - case VK_LAUNCH_MAIL: - return TranslateT("Mail"); - case VK_LAUNCH_MEDIA_SELECT: - return TranslateT("Media: Select"); - case VK_LAUNCH_APP1: - return TranslateT("App 1"); - case VK_LAUNCH_APP2: - return TranslateT("App 2"); - - case VK_DIVIDE: - case VK_INSERT: - case VK_HOME: - case VK_PRIOR: - case VK_DELETE: - case VK_END: - case VK_NEXT: - case VK_LEFT: - case VK_RIGHT: - case VK_UP: - case VK_DOWN: - code |= (1UL << 24); - } - - GetKeyNameText(code, buf, 256); - return buf; -} - -void HotkeyToName(wchar_t *buf, int size, uint8_t shift, uint8_t key) -{ - mir_snwprintf(buf, size, L"%s%s%s%s%s", - (shift & HOTKEYF_CONTROL) ? TranslateT("Ctrl + ") : L"", - (shift & HOTKEYF_ALT) ? TranslateT("Alt + ") : L"", - (shift & HOTKEYF_SHIFT) ? TranslateT("Shift + ") : L"", - (shift & HOTKEYF_EXT) ? TranslateT("Win + ") : L"", - sttHokeyVkToName(key)); -} - -/////////////////////////////////////////////////////////////////////////////// -// Hotkey control - -static LRESULT CALLBACK sttHotkeyEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (!data) - return 0; - - bool bKeyDown = false; - - switch (msg) { - case HKM_GETHOTKEY: - return data->key ? MAKEWORD(data->key, data->shift) : 0; - - case HKM_SETHOTKEY: - { - wchar_t buf[256] = {}; - data->key = (uint8_t)LOWORD(wParam); - data->shift = (uint8_t)HIWORD(wParam); - HotkeyToName(buf, _countof(buf), data->shift, data->key); - SetWindowText(hwnd, buf); - } - return 0; - - case WM_GETDLGCODE: - return DLGC_WANTALLKEYS; - - case WM_KILLFOCUS: - break; - - case WM_CHAR: - case WM_SYSCHAR: - case WM_PASTE: - case WM_CONTEXTMENU: - return TRUE; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - bKeyDown = true; - - case WM_KEYUP: - case WM_SYSKEYUP: - { - wchar_t buf[256] = {}; - - uint8_t shift = 0; - uint8_t key = wParam; - wchar_t *name = sttHokeyVkToName(key); - if (!*name || !bKeyDown) - key = 0; - - if (GetAsyncKeyState(VK_CONTROL)) shift |= HOTKEYF_CONTROL; - if (GetAsyncKeyState(VK_MENU)) shift |= HOTKEYF_ALT; - if (GetAsyncKeyState(VK_SHIFT)) shift |= HOTKEYF_SHIFT; - if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) shift |= HOTKEYF_EXT; - - if (bKeyDown || !data->key) { - data->shift = shift; - data->key = key; - } - - HotkeyToName(buf, _countof(buf), data->shift, data->key); - SetWindowText(hwnd, buf); - - if (bKeyDown && data->key) - SendMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(GetWindowLongPtr(hwnd, GWL_ID), 0), (LPARAM)hwnd); - } - return TRUE; - - case WM_DESTROY: - SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); - mir_free(data); - } - - return mir_callNextSubclass(hwnd, sttHotkeyEditProc, msg, wParam, lParam); -} - -MIR_APP_DLL(void) Hotkey_Subclass(HWND hwnd) -{ - THotkeyBoxData *data = (THotkeyBoxData *)mir_alloc(sizeof(THotkeyBoxData)); - SetWindowLongPtr(hwnd, GWLP_USERDATA, (ULONG_PTR)data); - mir_subclassWindow(hwnd, sttHotkeyEditProc); -} - -MIR_APP_DLL(void) Hotkey_Unsubclass(HWND hwnd) -{ - THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); - mir_free(data); -} - -/////////////////////////////////////////////////////////////////////////////// -// Options - -enum { COL_NAME, COL_TYPE, COL_KEY, COL_RESET, COL_ADDREMOVE }; - -static void sttOptionsSetupItem(HWND hwndList, int idx, THotkeyItem *item) -{ - wchar_t buf[256]; - LVITEM lvi = {}; - lvi.iItem = idx; - - if (!item->rootHotkey) { - lvi.mask = LVIF_TEXT | LVIF_IMAGE; - lvi.iSubItem = COL_NAME; - lvi.pszText = item->getDescr(); - lvi.iImage = item->OptType; - ListView_SetItem(hwndList, &lvi); - - ListView_SetCheckState(hwndList, lvi.iItem, item->Enabled); - } - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = COL_KEY; - HotkeyToName(buf, _countof(buf), HIBYTE(item->OptHotkey), LOBYTE(item->OptHotkey)); - lvi.pszText = buf; - ListView_SetItem(hwndList, &lvi); - - if (item->rootHotkey) { - lvi.mask = LVIF_IMAGE; - lvi.iSubItem = COL_TYPE; - lvi.iImage = item->OptType; - ListView_SetItem(hwndList, &lvi); - } - - lvi.mask = LVIF_IMAGE; - lvi.iSubItem = COL_RESET; - lvi.iImage = (item->Hotkey != item->OptHotkey) ? 5 : -1; - ListView_SetItem(hwndList, &lvi); - - lvi.mask = LVIF_IMAGE | LVIF_TEXT; - lvi.iSubItem = COL_ADDREMOVE; - if (item->rootHotkey) { - lvi.iImage = 4; - lvi.pszText = TranslateT("Remove shortcut"); - } - else { - lvi.iImage = 3; - lvi.pszText = TranslateT("Add another shortcut"); - } - ListView_SetItem(hwndList, &lvi); -} - -static void sttOptionsDeleteHotkey(HWND hwndList, int idx, THotkeyItem *item) -{ - item->OptDeleted = true; - ListView_DeleteItem(hwndList, idx); - if (item->rootHotkey) - item->rootHotkey->OptChanged = true; -} - -static int CALLBACK sttOptionsSortList(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) -{ - wchar_t title1[256] = {}, title2[256] = {}; - THotkeyItem *item1 = nullptr, *item2 = nullptr; - LVITEM lvi = {}; - int res; - - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = lParam1; - lvi.pszText = title1; - lvi.cchTextMax = _countof(title1); - if (ListView_GetItem((HWND)lParamSort, &lvi)) - item1 = (THotkeyItem *)lvi.lParam; - - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = lParam2; - lvi.pszText = title2; - lvi.cchTextMax = _countof(title2); - if (ListView_GetItem((HWND)lParamSort, &lvi)) - item2 = (THotkeyItem *)lvi.lParam; - - if (!item1 && !item2) - return mir_wstrcmp(title1, title2); - - if (!item1 && item2) { - if (res = mir_wstrcmp(title1, item2->getSection())) - return res; - return -1; - } - - if (!item2 && item1) { - if (res = mir_wstrcmp(item1->getSection(), title2)) - return res; - return 1; - } - /* item1 != nullptr && item2 != nullptr */ - - if (res = mir_wstrcmp(item1->getSection(), item2->getSection())) return res; - if (res = mir_wstrcmp(item1->getDescr(), item2->getDescr())) return res; - if (!item1->rootHotkey && item2->rootHotkey) return -1; - if (item1->rootHotkey && !item2->rootHotkey) return 1; - return 0; -} - -static void sttOptionsAddHotkey(HWND hwndList, THotkeyItem *item) -{ - char buf[256]; - mir_snprintf(buf, "mir_hotkey_%d_%d", g_pid, g_hkid++); - - THotkeyItem *newItem = (THotkeyItem *)mir_calloc(sizeof(THotkeyItem)); - newItem->pPlugin = item->pPlugin; - newItem->pszService = mir_strdup(item->pszService); - newItem->pwszSection = mir_wstrdup(item->pwszSection); - newItem->pwszDescription = mir_wstrdup(item->pwszDescription); - newItem->lParam = item->lParam; - newItem->idHotkey = GlobalAddAtomA(buf); - newItem->rootHotkey = item; - newItem->type = newItem->OptType = item->OptType; - newItem->Enabled = newItem->OptEnabled = newItem->OptNew = true; - hotkeys.insert(newItem); - - SendMessage(hwndList, WM_SETREDRAW, FALSE, 0); - - LVITEM lvi = {}; - lvi.mask |= LVIF_PARAM; - lvi.lParam = (LPARAM)newItem; - sttOptionsSetupItem(hwndList, ListView_InsertItem(hwndList, &lvi), newItem); - ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList); - - SendMessage(hwndList, WM_SETREDRAW, TRUE, 0); - RedrawWindow(hwndList, nullptr, nullptr, RDW_INVALIDATE); - - item->OptChanged = true; -} - -static void sttOptionsSetChanged(THotkeyItem *item) -{ - item->OptChanged = true; - if (item->rootHotkey) - item->rootHotkey->OptChanged = true; -} - -static void sttOptionsSaveItem(THotkeyItem *item) -{ - char buf[MAXMODULELABELLENGTH]; - - if (item->rootHotkey) return; - if (!item->OptChanged) return; - - item->Hotkey = item->OptHotkey; - item->type = item->OptType; - item->Enabled = item->OptEnabled; - - db_set_w(0, DBMODULENAME, item->pszName, item->Hotkey); - db_set_b(0, DBMODULENAME "Off", item->pszName, (uint8_t)!item->Enabled); - if (item->type != HKT_MANUAL) - db_set_b(0, DBMODULENAME "Types", item->pszName, (uint8_t)item->type); - - item->nSubHotkeys = 0; - for (auto &it : hotkeys) { - if (it->rootHotkey == item) { - it->Hotkey = it->OptHotkey; - it->type = it->OptType; - - mir_snprintf(buf, "%s$%d", item->pszName, item->nSubHotkeys); - db_set_w(0, DBMODULENAME, buf, it->Hotkey); - if (it->type != HKT_MANUAL) - db_set_b(0, DBMODULENAME "Types", buf, (uint8_t)it->type); - - ++item->nSubHotkeys; - } - } - - mir_snprintf(buf, "%s$count", item->pszName); - db_set_dw(0, DBMODULENAME, buf, item->nSubHotkeys); -} - -static void sttBuildHotkeyList(HWND hwndList) -{ - ListView_DeleteAllItems(hwndList); - - int nItems = 0; - THotkeyItem *prevItem = nullptr; - for (auto &item : hotkeys) { - LVITEM lvi = {}; - - if (!item->OptDeleted) { - if (!prevItem || mir_wstrcmp(item->pwszSection, prevItem->pwszSection)) { - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = nItems++; - lvi.iSubItem = 0; - lvi.lParam = 0; - lvi.pszText = item->getSection(); - ListView_InsertItem(hwndList, &lvi); - ListView_SetCheckState(hwndList, lvi.iItem, TRUE); - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = 1; - lvi.pszText = item->pwszSection; - ListView_SetItem(hwndList, &lvi); - - lvi.iSubItem = 0; - } - - lvi.mask = LVIF_PARAM | LVIF_INDENT; - lvi.iIndent = 1; - lvi.iItem = nItems++; - lvi.lParam = (LPARAM)item; - ListView_InsertItem(hwndList, &lvi); - sttOptionsSetupItem(hwndList, nItems - 1, item); - } - - prevItem = item; - } - - ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList); -} - -static void sttOptionsStartEdit(HWND hwndDlg, HWND hwndHotkey) -{ - LVITEM lvi; - THotkeyItem *item; - int iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); - if (iItem < 0) - return; - - lvi.mask = LVIF_PARAM; - lvi.iItem = iItem; - ListView_GetItem(hwndHotkey, &lvi); - - if (item = (THotkeyItem *)lvi.lParam) { - RECT rc; - ListView_GetSubItemRect(hwndHotkey, iItem, COL_KEY, LVIR_BOUNDS, &rc); - MapWindowPoints(hwndHotkey, hwndDlg, (LPPOINT)&rc, 2); - SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_SETHOTKEY, MAKELONG(LOBYTE(item->OptHotkey), HIBYTE(item->OptHotkey)), 0); - - SetWindowPos(hwndHotkey, HWND_BOTTOM, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); - SetWindowPos(GetDlgItem(hwndDlg, IDC_HOTKEY), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW); - RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE); - - SetFocus(GetDlgItem(hwndDlg, IDC_HOTKEY)); - RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE); - } -} - -static void sttOptionsDrawTextChunk(HDC hdc, wchar_t *text, RECT *rc) -{ - DrawText(hdc, text, -1, rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS); - - SIZE sz; - GetTextExtentPoint32(hdc, text, (int)mir_wstrlen(text), &sz); - rc->left += sz.cx; -} - -static INT_PTR CALLBACK sttOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - static bool initialized = false; - static int colWidth = 0; - static uint16_t currentLanguage = 0; - - HWND hwndHotkey = GetDlgItem(hwndDlg, IDC_LV_HOTKEYS); - - switch (msg) { - case WM_INITDIALOG: - initialized = false; - - TranslateDialogDefault(hwndDlg); - - Hotkey_Subclass(GetDlgItem(hwndDlg, IDC_HOTKEY)); - { - HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 3, 1); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOWS); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_MIRANDA); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOW); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_ADDCONTACT); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_DELETE); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_UNDO); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPOPEN); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPSHUT); - ListView_SetImageList(hwndHotkey, hIml, LVSIL_SMALL); - } - ListView_SetExtendedListViewStyle(hwndHotkey, LVS_EX_CHECKBOXES | LVS_EX_SUBITEMIMAGES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP); - { - RECT rc; - GetClientRect(hwndHotkey, &rc); - colWidth = rc.right - GetSystemMetrics(SM_CXHTHUMB) - 3 * g_iIconSX - 5; - - LVCOLUMN lvc; - lvc.mask = LVCF_WIDTH; - lvc.cx = colWidth * 2 / 3; - ListView_InsertColumn(hwndHotkey, COL_NAME, &lvc); - lvc.cx = g_iIconSX; - ListView_InsertColumn(hwndHotkey, COL_TYPE, &lvc); - lvc.cx = colWidth / 3; - ListView_InsertColumn(hwndHotkey, COL_KEY, &lvc); - lvc.cx = g_iIconSX; - ListView_InsertColumn(hwndHotkey, COL_RESET, &lvc); - lvc.cx = g_iIconSX; - ListView_InsertColumn(hwndHotkey, COL_ADDREMOVE, &lvc); - - for (auto &it : hotkeys) { - it->OptChanged = false; - it->OptDeleted = it->OptNew = false; - it->OptEnabled = it->Enabled; - it->OptHotkey = it->Hotkey; - it->OptType = it->type; - } - - currentLanguage = LOWORD(GetKeyboardLayout(0)); - sttBuildHotkeyList(hwndHotkey); - } - SetTimer(hwndDlg, 1024, 1000, nullptr); - initialized = TRUE; - { - /* load group states */ - int count = ListView_GetItemCount(hwndHotkey); - wchar_t buf[128]; - LVITEM lvi = {}; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - lvi.mask = LVIF_PARAM; - lvi.iSubItem = 0; - ListView_GetItem(hwndHotkey, &lvi); - if (lvi.lParam) - continue; - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = 1; - ListView_GetItem(hwndHotkey, &lvi); - - ListView_SetCheckState(hwndHotkey, lvi.iItem, db_get_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), TRUE)); - } - } - - g_hwndHkOptions = hwndDlg; - break; - - case WM_TIMER: - if (initialized) { - uint16_t newLanguage = LOWORD(GetKeyboardLayout(0)); - if (newLanguage == currentLanguage) - break; - - int count = ListView_GetItemCount(hwndHotkey); - - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - ListView_GetItem(hwndHotkey, &lvi); - if (lvi.lParam) - sttOptionsSetupItem(hwndHotkey, lvi.iItem, (THotkeyItem *)lvi.lParam); - } - currentLanguage = newLanguage; - } - break; - - case WM_HOTKEYUNREGISTERED: - { - int count = ListView_GetItemCount(hwndHotkey); - - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - ListView_GetItem(hwndHotkey, &lvi); - if (!lvi.lParam) continue; - - if (((THotkeyItem *)lvi.lParam)->UnregisterHotkey) { - ListView_DeleteItem(hwndHotkey, lvi.iItem); - --lvi.iItem; - --count; - } - } - } - break; - - case WM_DRAWITEM: - { - LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam; - RECT rc = lpdis->rcItem; - int prefix = 65; - int width = (lpdis->rcItem.right - lpdis->rcItem.left - prefix) / 3; - rc.left += 5; - - HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL); - if (lpdis->CtlID == IDC_CANVAS2) { - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Scope:"), &rc); - - rc.left = prefix; - ImageList_Draw(hIml, 0, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("System"), &rc); - - rc.left = prefix + width; - ImageList_Draw(hIml, 1, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Miranda"), &rc); - - rc.left = prefix + width * 2; - ImageList_Draw(hIml, 2, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Window"), &rc); - return TRUE; - } - - if (lpdis->CtlID == IDC_CANVAS) { - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Actions:"), &rc); - - rc.left = prefix; - ImageList_Draw(hIml, 5, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Undo"), &rc); - - rc.left = prefix + width * 1; - ImageList_Draw(hIml, 3, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Add binding"), &rc); - - rc.left = prefix + width * 2; - ImageList_Draw(hIml, 4, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Remove"), &rc); - return TRUE; - } - } - break; - - case WM_COMMAND: - if ((LOWORD(wParam) == IDC_HOTKEY) && ((HIWORD(wParam) == EN_KILLFOCUS) || (HIWORD(wParam) == 0))) { - LVITEM lvi; - THotkeyItem *item; - uint16_t wHotkey = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0); - - ShowWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), SW_HIDE); - SetFocus(hwndHotkey); - if (!wHotkey || (wHotkey == VK_ESCAPE) || (HIWORD(wParam) != 0)) - break; - - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); - if (lvi.iItem >= 0) { - ListView_GetItem(hwndHotkey, &lvi); - if (item = (THotkeyItem *)lvi.lParam) { - item->OptHotkey = wHotkey; - - sttOptionsSetupItem(hwndHotkey, lvi.iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - } - } - break; - - case WM_CONTEXTMENU: - if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_LV_HOTKEYS) { - HWND hwndList = (HWND)wParam; - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - LVITEM lvi = {}; - THotkeyItem *item = nullptr; - - lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); - if (lvi.iItem < 0) - return FALSE; - - lvi.mask = LVIF_PARAM; - ListView_GetItem(hwndList, &lvi); - if (!(item = (THotkeyItem *)lvi.lParam)) - return FALSE; - - if (pt.x == -1 && pt.y == -1) { - RECT rc; - ListView_GetItemRect(hwndList, lvi.iItem, &rc, LVIR_LABEL); - pt.x = rc.left; - pt.y = rc.bottom; - ClientToScreen(hwndList, &pt); - } - - enum { MI_CANCEL, MI_CHANGE, MI_SYSTEM, MI_LOCAL, MI_ADD, MI_REMOVE, MI_REVERT }; - - MENUITEMINFO mii = {}; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STATE; - mii.fState = MFS_DEFAULT; - - HMENU hMenu = CreatePopupMenu(); - AppendMenu(hMenu, MF_STRING, MI_CHANGE, TranslateT("Modify")); - SetMenuItemInfo(hMenu, MI_CHANGE, FALSE, &mii); - if (item->type != HKT_MANUAL) { - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - AppendMenu(hMenu, MF_STRING | - ((item->OptType == HKT_GLOBAL) ? MF_CHECKED : 0), - (UINT_PTR)MI_SYSTEM, TranslateT("System scope")); - AppendMenu(hMenu, MF_STRING | - ((item->OptType == HKT_LOCAL) ? MF_CHECKED : 0), - (UINT_PTR)MI_LOCAL, TranslateT("Miranda scope")); - } - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - if (!item->rootHotkey) - AppendMenu(hMenu, MF_STRING, MI_ADD, TranslateT("Add binding")); - else - AppendMenu(hMenu, MF_STRING, MI_REMOVE, TranslateT("Remove")); - if (item->Hotkey != item->OptHotkey) { - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - AppendMenu(hMenu, MF_STRING, MI_REVERT, TranslateT("Undo")); - } - - switch (TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, nullptr)) { - case MI_CHANGE: - sttOptionsStartEdit(hwndDlg, hwndHotkey); - break; - case MI_SYSTEM: - item->OptType = HKT_GLOBAL; - sttOptionsSetupItem(hwndList, lvi.iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - case MI_LOCAL: - item->OptType = HKT_LOCAL; - sttOptionsSetupItem(hwndList, lvi.iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - case MI_ADD: - initialized = false; - sttOptionsAddHotkey(hwndList, item); - initialized = true; - break; - case MI_REMOVE: - sttOptionsDeleteHotkey(hwndList, lvi.iItem, item); - break; - case MI_REVERT: - item->OptHotkey = item->Hotkey; - sttOptionsSetupItem(hwndList, lvi.iItem, item); - break; - } - DestroyMenu(hMenu); - break; - } - break; - - case WM_NOTIFY: - { - LPNMHDR lpnmhdr = (LPNMHDR)lParam; - switch (lpnmhdr->idFrom) { - case 0: - if ((lpnmhdr->code != PSN_APPLY) && (lpnmhdr->code != PSN_RESET)) - break; - - UnregisterHotkeys(); - - for (auto &p : hotkeys.rev_iter()) - if (p->OptNew && p->OptDeleted || p->rootHotkey && !p->OptHotkey || (lpnmhdr->code == PSN_APPLY) && p->OptDeleted || (lpnmhdr->code == PSN_RESET) && p->OptNew) - FreeHotkey(hotkeys.removeItem(&p)); - - if (lpnmhdr->code == PSN_APPLY) { - LVITEM lvi = {}; - int count = ListView_GetItemCount(hwndHotkey); - - for (auto &it : hotkeys) - sttOptionsSaveItem(it); - - lvi.mask = LVIF_IMAGE; - lvi.iSubItem = COL_RESET; - lvi.iImage = -1; - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) - ListView_SetItem(hwndHotkey, &lvi); - } - - RegisterHotkeys(); - - NotifyEventHooks(hEvChanged, 0, 0); - break; - - case IDC_LV_HOTKEYS: - switch (lpnmhdr->code) { - case NM_CLICK: - { - LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam; - - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM | LVIF_IMAGE; - lvi.iItem = lpnmia->iItem; - ListView_GetItem(lpnmia->hdr.hwndFrom, &lvi); - - auto *item = (THotkeyItem *)lvi.lParam; - if (item == nullptr) - break; - - LVHITTESTINFO lvhti = {}; - lvhti.pt = lpnmia->ptAction; - lvhti.iItem = lpnmia->iItem; - lvhti.iSubItem = lpnmia->iSubItem; - ListView_HitTest(lpnmia->hdr.hwndFrom, &lvhti); - - if ((!item->rootHotkey && (lpnmia->iSubItem == COL_NAME) && ((lvhti.flags & LVHT_ONITEM) == LVHT_ONITEMICON) || - item->rootHotkey && (lpnmia->iSubItem == COL_TYPE)) && - ((item->OptType == HKT_GLOBAL) || (item->OptType == HKT_LOCAL))) { - item->OptType = (item->OptType == HKT_GLOBAL) ? HKT_LOCAL : HKT_GLOBAL; - sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - else if (lpnmia->iSubItem == COL_RESET) { - item->OptHotkey = item->Hotkey; - sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); - } - else if (lpnmia->iSubItem == COL_ADDREMOVE) { - if (item->rootHotkey) - sttOptionsDeleteHotkey(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); - else { - initialized = false; - sttOptionsAddHotkey(lpnmia->hdr.hwndFrom, item); - initialized = true; - } - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - } - break; - - case LVN_KEYDOWN: - { - LPNMLVKEYDOWN param = (LPNMLVKEYDOWN)lParam; - if (param->wVKey == VK_SUBTRACT || param->wVKey == VK_LEFT || param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) { - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED); - if (lvi.iItem < 0) - break; - - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - if (lvi.lParam) - break; - - if (param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) { - ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, TRUE); - } - else { - ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, FALSE); - } - } - else if (param->wVKey == VK_F2) - sttOptionsStartEdit(hwndDlg, hwndHotkey); - } - break; - - case LVN_ITEMACTIVATE: - { - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED); - if (lvi.iItem < 0) break; - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - - if (lvi.lParam) - sttOptionsStartEdit(hwndDlg, hwndHotkey); - else - ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, !ListView_GetCheckState(lpnmhdr->hwndFrom, lvi.iItem)); - } - break; - - case LVN_ITEMCHANGED: - if (initialized) { - LPNMLISTVIEW param = (LPNMLISTVIEW)lParam; - THotkeyItem *item = (THotkeyItem *)param->lParam; - if (param->uNewState >> 12 == param->uOldState >> 12) - break; - - if (item && !item->rootHotkey) { - item->OptEnabled = ListView_GetCheckState(lpnmhdr->hwndFrom, param->iItem) ? 1 : 0; - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - else if (!item) { - wchar_t buf[256]; - LVITEM lvi = {}; - lvi.mask = LVIF_TEXT; - lvi.iItem = param->iItem; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - - if (param->uNewState >> 12 == 1) { - int count = ListView_GetItemCount(lpnmhdr->hwndFrom); - LVITEM lvi2 = {}; - lvi2.mask = LVIF_PARAM; - for (lvi2.iItem = 0; lvi2.iItem < count; ++lvi2.iItem) { - ListView_GetItem(lpnmhdr->hwndFrom, &lvi2); - item = (THotkeyItem *)lvi2.lParam; - if (!item) continue; - if (!mir_wstrcmp(item->getSection(), buf)) { - ListView_DeleteItem(lpnmhdr->hwndFrom, lvi2.iItem); - --lvi2.iItem; - --count; - } - } - } - else if (param->uNewState >> 12 == 2) { - int nItems = ListView_GetItemCount(lpnmhdr->hwndFrom); - initialized = false; - for (auto &it : hotkeys) { - LVITEM lvi2 = {}; - if (it->OptDeleted || mir_wstrcmp(buf, it->getSection())) - continue; - - lvi2.mask = LVIF_PARAM | LVIF_INDENT; - lvi2.iIndent = 1; - lvi2.iItem = nItems++; - lvi2.lParam = (LPARAM)it; - ListView_InsertItem(lpnmhdr->hwndFrom, &lvi2); - sttOptionsSetupItem(lpnmhdr->hwndFrom, nItems - 1, it); - } - ListView_SortItemsEx(lpnmhdr->hwndFrom, sttOptionsSortList, (LPARAM)lpnmhdr->hwndFrom); - initialized = TRUE; - } - } - } - break; - - case NM_CUSTOMDRAW: - { - NMLVCUSTOMDRAW *param = (NMLVCUSTOMDRAW *)lParam; - switch (param->nmcd.dwDrawStage) { - case CDDS_PREPAINT: - case CDDS_ITEMPREPAINT: - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW); - return TRUE; - - case CDDS_SUBITEM | CDDS_ITEMPREPAINT: - { - THotkeyItem *item; - wchar_t buf[256]; - LVITEM lvi = {}; - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = param->nmcd.dwItemSpec; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - - item = (THotkeyItem *)lvi.lParam; - if (!item) { - RECT rc; - HFONT hfnt; - - ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc); - FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)); - SetTextColor(param->nmcd.hdc, GetSysColor(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); - - if (param->iSubItem == 0) { - rc.left += 3; - HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL); - ImageList_Draw(hIml, - ListView_GetCheckState(hwndHotkey, lvi.iItem) ? 6 : 7, - param->nmcd.hdc, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 18; - hfnt = (HFONT)SelectObject(param->nmcd.hdc, (HFONT)SendMessage(GetParent(hwndDlg), PSM_GETBOLDFONT, 0, 0)); - DrawText(param->nmcd.hdc, buf, -1, &rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER); - SelectObject(param->nmcd.hdc, hfnt); - } - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); - return TRUE; - } - - if (item->rootHotkey && (param->iSubItem == 0)) { - RECT rc; - ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc); - FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW)); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); - return TRUE; - } - break; - } - } - break; - } - break; - } - } - } - break; - - case WM_DESTROY: - { - int count = ListView_GetItemCount(hwndHotkey); - - g_hwndHkOptions = nullptr; - - KillTimer(hwndDlg, 1024); - - wchar_t buf[128]; - LVITEM lvi = {}; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - lvi.mask = LVIF_PARAM; - lvi.iSubItem = 0; - ListView_GetItem(hwndHotkey, &lvi); - if (lvi.lParam) continue; - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = 1; - ListView_GetItem(hwndHotkey, &lvi); - - db_set_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), ListView_GetCheckState(hwndHotkey, lvi.iItem)); - } - } - } - - return FALSE; -} - -int HotkeyOptionsInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.flags = ODPF_BOLDGROUPS; - odp.position = -180000000; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_HOTKEYS); - odp.szTitle.a = LPGEN("Hotkeys"); - odp.szGroup.a = LPGEN("Customize"); - odp.pfnDlgProc = sttOptionsDlgProc; - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+#include <m_hotkeys.h>
+#include "skin.h"
+
+static wchar_t *sttHokeyVkToName(uint16_t vkKey)
+{
+ static wchar_t buf[256] = {};
+ uint32_t code = MapVirtualKey(vkKey, 0) << 16;
+
+ switch (vkKey) {
+ case 0:
+ case VK_CONTROL:
+ case VK_SHIFT:
+ case VK_MENU:
+ case VK_LWIN:
+ case VK_RWIN:
+ case VK_PAUSE:
+ case VK_CANCEL:
+ case VK_NUMLOCK:
+ case VK_CAPITAL:
+ case VK_SCROLL:
+ return L"";
+ case VK_BROWSER_BACK:
+ return TranslateT("Browser: Back");
+ case VK_BROWSER_FORWARD:
+ return TranslateT("Browser: Forward");
+ case VK_BROWSER_REFRESH:
+ return TranslateT("Browser: Refresh");
+ case VK_BROWSER_STOP:
+ return TranslateT("Browser: Stop");
+ case VK_BROWSER_SEARCH:
+ return TranslateT("Browser: Search");
+ case VK_BROWSER_FAVORITES:
+ return TranslateT("Browser: Fav");
+ case VK_BROWSER_HOME:
+ return TranslateT("Browser: Home");
+ case VK_VOLUME_MUTE:
+ return TranslateT("Mute");
+ case VK_VOLUME_DOWN:
+ return TranslateT("Vol-");
+ case VK_VOLUME_UP:
+ return TranslateT("Vol+");
+ case VK_MEDIA_NEXT_TRACK:
+ return TranslateT("Media: Next Track");
+ case VK_MEDIA_PREV_TRACK:
+ return TranslateT("Media: Prev. Track");
+ case VK_MEDIA_STOP:
+ return TranslateT("Media: Stop");
+ case VK_MEDIA_PLAY_PAUSE:
+ return TranslateT("Media: Play/Pause");
+ case VK_LAUNCH_MAIL:
+ return TranslateT("Mail");
+ case VK_LAUNCH_MEDIA_SELECT:
+ return TranslateT("Media: Select");
+ case VK_LAUNCH_APP1:
+ return TranslateT("App 1");
+ case VK_LAUNCH_APP2:
+ return TranslateT("App 2");
+
+ case VK_DIVIDE:
+ case VK_INSERT:
+ case VK_HOME:
+ case VK_PRIOR:
+ case VK_DELETE:
+ case VK_END:
+ case VK_NEXT:
+ case VK_LEFT:
+ case VK_RIGHT:
+ case VK_UP:
+ case VK_DOWN:
+ code |= (1UL << 24);
+ }
+
+ GetKeyNameText(code, buf, 256);
+ return buf;
+}
+
+void HotkeyToName(wchar_t *buf, int size, uint8_t shift, uint8_t key)
+{
+ mir_snwprintf(buf, size, L"%s%s%s%s%s",
+ (shift & HOTKEYF_CONTROL) ? TranslateT("Ctrl + ") : L"",
+ (shift & HOTKEYF_ALT) ? TranslateT("Alt + ") : L"",
+ (shift & HOTKEYF_SHIFT) ? TranslateT("Shift + ") : L"",
+ (shift & HOTKEYF_EXT) ? TranslateT("Win + ") : L"",
+ sttHokeyVkToName(key));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Hotkey control
+
+static LRESULT CALLBACK sttHotkeyEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (!data)
+ return 0;
+
+ bool bKeyDown = false;
+
+ switch (msg) {
+ case HKM_GETHOTKEY:
+ return data->key ? MAKEWORD(data->key, data->shift) : 0;
+
+ case HKM_SETHOTKEY:
+ {
+ wchar_t buf[256] = {};
+ data->key = (uint8_t)LOWORD(wParam);
+ data->shift = (uint8_t)HIWORD(wParam);
+ HotkeyToName(buf, _countof(buf), data->shift, data->key);
+ SetWindowText(hwnd, buf);
+ }
+ return 0;
+
+ case WM_GETDLGCODE:
+ return DLGC_WANTALLKEYS;
+
+ case WM_KILLFOCUS:
+ break;
+
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_PASTE:
+ case WM_CONTEXTMENU:
+ return TRUE;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ bKeyDown = true;
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ {
+ wchar_t buf[256] = {};
+
+ uint8_t shift = 0;
+ uint8_t key = wParam;
+ wchar_t *name = sttHokeyVkToName(key);
+ if (!*name || !bKeyDown)
+ key = 0;
+
+ if (GetAsyncKeyState(VK_CONTROL)) shift |= HOTKEYF_CONTROL;
+ if (GetAsyncKeyState(VK_MENU)) shift |= HOTKEYF_ALT;
+ if (GetAsyncKeyState(VK_SHIFT)) shift |= HOTKEYF_SHIFT;
+ if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) shift |= HOTKEYF_EXT;
+
+ if (bKeyDown || !data->key) {
+ data->shift = shift;
+ data->key = key;
+ }
+
+ HotkeyToName(buf, _countof(buf), data->shift, data->key);
+ SetWindowText(hwnd, buf);
+
+ if (bKeyDown && data->key)
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(GetWindowLongPtr(hwnd, GWL_ID), 0), (LPARAM)hwnd);
+ }
+ return TRUE;
+
+ case WM_DESTROY:
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ mir_free(data);
+ }
+
+ return mir_callNextSubclass(hwnd, sttHotkeyEditProc, msg, wParam, lParam);
+}
+
+MIR_APP_DLL(void) Hotkey_Subclass(HWND hwnd)
+{
+ THotkeyBoxData *data = (THotkeyBoxData *)mir_alloc(sizeof(THotkeyBoxData));
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (ULONG_PTR)data);
+ mir_subclassWindow(hwnd, sttHotkeyEditProc);
+}
+
+MIR_APP_DLL(void) Hotkey_Unsubclass(HWND hwnd)
+{
+ THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ mir_free(data);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Options
+
+enum { COL_NAME, COL_TYPE, COL_KEY, COL_RESET, COL_ADDREMOVE };
+
+static void sttOptionsSetupItem(HWND hwndList, int idx, THotkeyItem *item)
+{
+ wchar_t buf[256];
+ LVITEM lvi = {};
+ lvi.iItem = idx;
+
+ if (!item->rootHotkey) {
+ lvi.mask = LVIF_TEXT | LVIF_IMAGE;
+ lvi.iSubItem = COL_NAME;
+ lvi.pszText = item->getDescr();
+ lvi.iImage = item->OptType;
+ ListView_SetItem(hwndList, &lvi);
+
+ ListView_SetCheckState(hwndList, lvi.iItem, item->Enabled);
+ }
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = COL_KEY;
+ HotkeyToName(buf, _countof(buf), HIBYTE(item->OptHotkey), LOBYTE(item->OptHotkey));
+ lvi.pszText = buf;
+ ListView_SetItem(hwndList, &lvi);
+
+ if (item->rootHotkey) {
+ lvi.mask = LVIF_IMAGE;
+ lvi.iSubItem = COL_TYPE;
+ lvi.iImage = item->OptType;
+ ListView_SetItem(hwndList, &lvi);
+ }
+
+ lvi.mask = LVIF_IMAGE;
+ lvi.iSubItem = COL_RESET;
+ lvi.iImage = (item->Hotkey != item->OptHotkey) ? 5 : -1;
+ ListView_SetItem(hwndList, &lvi);
+
+ lvi.mask = LVIF_IMAGE | LVIF_TEXT;
+ lvi.iSubItem = COL_ADDREMOVE;
+ if (item->rootHotkey) {
+ lvi.iImage = 4;
+ lvi.pszText = TranslateT("Remove shortcut");
+ }
+ else {
+ lvi.iImage = 3;
+ lvi.pszText = TranslateT("Add another shortcut");
+ }
+ ListView_SetItem(hwndList, &lvi);
+}
+
+static void sttOptionsDeleteHotkey(HWND hwndList, int idx, THotkeyItem *item)
+{
+ item->OptDeleted = true;
+ ListView_DeleteItem(hwndList, idx);
+ if (item->rootHotkey)
+ item->rootHotkey->OptChanged = true;
+}
+
+static int CALLBACK sttOptionsSortList(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+ wchar_t title1[256] = {}, title2[256] = {};
+ THotkeyItem *item1 = nullptr, *item2 = nullptr;
+ LVITEM lvi = {};
+ int res;
+
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iItem = lParam1;
+ lvi.pszText = title1;
+ lvi.cchTextMax = _countof(title1);
+ if (ListView_GetItem((HWND)lParamSort, &lvi))
+ item1 = (THotkeyItem *)lvi.lParam;
+
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iItem = lParam2;
+ lvi.pszText = title2;
+ lvi.cchTextMax = _countof(title2);
+ if (ListView_GetItem((HWND)lParamSort, &lvi))
+ item2 = (THotkeyItem *)lvi.lParam;
+
+ if (!item1 && !item2)
+ return mir_wstrcmp(title1, title2);
+
+ if (!item1 && item2) {
+ if (res = mir_wstrcmp(title1, item2->getSection()))
+ return res;
+ return -1;
+ }
+
+ if (!item2 && item1) {
+ if (res = mir_wstrcmp(item1->getSection(), title2))
+ return res;
+ return 1;
+ }
+ /* item1 != nullptr && item2 != nullptr */
+
+ if (res = mir_wstrcmp(item1->getSection(), item2->getSection())) return res;
+ if (res = mir_wstrcmp(item1->getDescr(), item2->getDescr())) return res;
+ if (!item1->rootHotkey && item2->rootHotkey) return -1;
+ if (item1->rootHotkey && !item2->rootHotkey) return 1;
+ return 0;
+}
+
+static void sttOptionsAddHotkey(HWND hwndList, THotkeyItem *item)
+{
+ char buf[256];
+ mir_snprintf(buf, "mir_hotkey_%d_%d", g_pid, g_hkid++);
+
+ THotkeyItem *newItem = (THotkeyItem *)mir_calloc(sizeof(THotkeyItem));
+ newItem->pPlugin = item->pPlugin;
+ newItem->pszService = mir_strdup(item->pszService);
+ newItem->pwszSection = mir_wstrdup(item->pwszSection);
+ newItem->pwszDescription = mir_wstrdup(item->pwszDescription);
+ newItem->lParam = item->lParam;
+ newItem->idHotkey = GlobalAddAtomA(buf);
+ newItem->rootHotkey = item;
+ newItem->type = newItem->OptType = item->OptType;
+ newItem->Enabled = newItem->OptEnabled = newItem->OptNew = true;
+ hotkeys.insert(newItem);
+
+ SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
+
+ LVITEM lvi = {};
+ lvi.mask |= LVIF_PARAM;
+ lvi.lParam = (LPARAM)newItem;
+ sttOptionsSetupItem(hwndList, ListView_InsertItem(hwndList, &lvi), newItem);
+ ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList);
+
+ SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
+ RedrawWindow(hwndList, nullptr, nullptr, RDW_INVALIDATE);
+
+ item->OptChanged = true;
+}
+
+static void sttOptionsSetChanged(THotkeyItem *item)
+{
+ item->OptChanged = true;
+ if (item->rootHotkey)
+ item->rootHotkey->OptChanged = true;
+}
+
+static void sttOptionsSaveItem(THotkeyItem *item)
+{
+ char buf[MAXMODULELABELLENGTH];
+
+ if (item->rootHotkey) return;
+ if (!item->OptChanged) return;
+
+ item->Hotkey = item->OptHotkey;
+ item->type = item->OptType;
+ item->Enabled = item->OptEnabled;
+
+ db_set_w(0, DBMODULENAME, item->pszName, item->Hotkey);
+ db_set_b(0, DBMODULENAME "Off", item->pszName, (uint8_t)!item->Enabled);
+ if (item->type != HKT_MANUAL)
+ db_set_b(0, DBMODULENAME "Types", item->pszName, (uint8_t)item->type);
+
+ item->nSubHotkeys = 0;
+ for (auto &it : hotkeys) {
+ if (it->rootHotkey == item) {
+ it->Hotkey = it->OptHotkey;
+ it->type = it->OptType;
+
+ mir_snprintf(buf, "%s$%d", item->pszName, item->nSubHotkeys);
+ db_set_w(0, DBMODULENAME, buf, it->Hotkey);
+ if (it->type != HKT_MANUAL)
+ db_set_b(0, DBMODULENAME "Types", buf, (uint8_t)it->type);
+
+ ++item->nSubHotkeys;
+ }
+ }
+
+ mir_snprintf(buf, "%s$count", item->pszName);
+ db_set_dw(0, DBMODULENAME, buf, item->nSubHotkeys);
+}
+
+static void sttBuildHotkeyList(HWND hwndList)
+{
+ ListView_DeleteAllItems(hwndList);
+
+ int nItems = 0;
+ THotkeyItem *prevItem = nullptr;
+ for (auto &item : hotkeys) {
+ LVITEM lvi = {};
+
+ if (!item->OptDeleted) {
+ if (!prevItem || mir_wstrcmp(item->pwszSection, prevItem->pwszSection)) {
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iItem = nItems++;
+ lvi.iSubItem = 0;
+ lvi.lParam = 0;
+ lvi.pszText = item->getSection();
+ ListView_InsertItem(hwndList, &lvi);
+ ListView_SetCheckState(hwndList, lvi.iItem, TRUE);
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = 1;
+ lvi.pszText = item->pwszSection;
+ ListView_SetItem(hwndList, &lvi);
+
+ lvi.iSubItem = 0;
+ }
+
+ lvi.mask = LVIF_PARAM | LVIF_INDENT;
+ lvi.iIndent = 1;
+ lvi.iItem = nItems++;
+ lvi.lParam = (LPARAM)item;
+ ListView_InsertItem(hwndList, &lvi);
+ sttOptionsSetupItem(hwndList, nItems - 1, item);
+ }
+
+ prevItem = item;
+ }
+
+ ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList);
+}
+
+static void sttOptionsStartEdit(HWND hwndDlg, HWND hwndHotkey)
+{
+ LVITEM lvi;
+ THotkeyItem *item;
+ int iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED);
+ if (iItem < 0)
+ return;
+
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = iItem;
+ ListView_GetItem(hwndHotkey, &lvi);
+
+ if (item = (THotkeyItem *)lvi.lParam) {
+ RECT rc;
+ ListView_GetSubItemRect(hwndHotkey, iItem, COL_KEY, LVIR_BOUNDS, &rc);
+ MapWindowPoints(hwndHotkey, hwndDlg, (LPPOINT)&rc, 2);
+ SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_SETHOTKEY, MAKELONG(LOBYTE(item->OptHotkey), HIBYTE(item->OptHotkey)), 0);
+
+ SetWindowPos(hwndHotkey, HWND_BOTTOM, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_HOTKEY), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE);
+
+ SetFocus(GetDlgItem(hwndDlg, IDC_HOTKEY));
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE);
+ }
+}
+
+static void sttOptionsDrawTextChunk(HDC hdc, wchar_t *text, RECT *rc)
+{
+ DrawText(hdc, text, -1, rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS);
+
+ SIZE sz;
+ GetTextExtentPoint32(hdc, text, (int)mir_wstrlen(text), &sz);
+ rc->left += sz.cx;
+}
+
+static INT_PTR CALLBACK sttOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static bool initialized = false;
+ static int colWidth = 0;
+ static uint16_t currentLanguage = 0;
+
+ HWND hwndHotkey = GetDlgItem(hwndDlg, IDC_LV_HOTKEYS);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ initialized = false;
+
+ TranslateDialogDefault(hwndDlg);
+
+ Hotkey_Subclass(GetDlgItem(hwndDlg, IDC_HOTKEY));
+ {
+ HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 3, 1);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOWS);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_MIRANDA);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOW);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_ADDCONTACT);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_DELETE);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_UNDO);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPOPEN);
+ ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPSHUT);
+ ListView_SetImageList(hwndHotkey, hIml, LVSIL_SMALL);
+ }
+ ListView_SetExtendedListViewStyle(hwndHotkey, LVS_EX_CHECKBOXES | LVS_EX_SUBITEMIMAGES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP);
+ {
+ RECT rc;
+ GetClientRect(hwndHotkey, &rc);
+ colWidth = rc.right - GetSystemMetrics(SM_CXHTHUMB) - 3 * g_iIconSX - 5;
+
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_WIDTH;
+ lvc.cx = colWidth * 2 / 3;
+ ListView_InsertColumn(hwndHotkey, COL_NAME, &lvc);
+ lvc.cx = g_iIconSX;
+ ListView_InsertColumn(hwndHotkey, COL_TYPE, &lvc);
+ lvc.cx = colWidth / 3;
+ ListView_InsertColumn(hwndHotkey, COL_KEY, &lvc);
+ lvc.cx = g_iIconSX;
+ ListView_InsertColumn(hwndHotkey, COL_RESET, &lvc);
+ lvc.cx = g_iIconSX;
+ ListView_InsertColumn(hwndHotkey, COL_ADDREMOVE, &lvc);
+
+ for (auto &it : hotkeys) {
+ it->OptChanged = false;
+ it->OptDeleted = it->OptNew = false;
+ it->OptEnabled = it->Enabled;
+ it->OptHotkey = it->Hotkey;
+ it->OptType = it->type;
+ }
+
+ currentLanguage = LOWORD(GetKeyboardLayout(0));
+ sttBuildHotkeyList(hwndHotkey);
+ }
+ SetTimer(hwndDlg, 1024, 1000, nullptr);
+ initialized = TRUE;
+ {
+ /* load group states */
+ int count = ListView_GetItemCount(hwndHotkey);
+ wchar_t buf[128];
+ LVITEM lvi = {};
+ lvi.pszText = buf;
+ lvi.cchTextMax = _countof(buf);
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (lvi.lParam)
+ continue;
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = 1;
+ ListView_GetItem(hwndHotkey, &lvi);
+
+ ListView_SetCheckState(hwndHotkey, lvi.iItem, db_get_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), TRUE));
+ }
+ }
+
+ g_hwndHkOptions = hwndDlg;
+ break;
+
+ case WM_TIMER:
+ if (initialized) {
+ uint16_t newLanguage = LOWORD(GetKeyboardLayout(0));
+ if (newLanguage == currentLanguage)
+ break;
+
+ int count = ListView_GetItemCount(hwndHotkey);
+
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM;
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (lvi.lParam)
+ sttOptionsSetupItem(hwndHotkey, lvi.iItem, (THotkeyItem *)lvi.lParam);
+ }
+ currentLanguage = newLanguage;
+ }
+ break;
+
+ case WM_HOTKEYUNREGISTERED:
+ {
+ int count = ListView_GetItemCount(hwndHotkey);
+
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM;
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (!lvi.lParam) continue;
+
+ if (((THotkeyItem *)lvi.lParam)->UnregisterHotkey) {
+ ListView_DeleteItem(hwndHotkey, lvi.iItem);
+ --lvi.iItem;
+ --count;
+ }
+ }
+ }
+ break;
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+ RECT rc = lpdis->rcItem;
+ int prefix = 65;
+ int width = (lpdis->rcItem.right - lpdis->rcItem.left - prefix) / 3;
+ rc.left += 5;
+
+ HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL);
+ if (lpdis->CtlID == IDC_CANVAS2) {
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Scope:"), &rc);
+
+ rc.left = prefix;
+ ImageList_Draw(hIml, 0, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("System"), &rc);
+
+ rc.left = prefix + width;
+ ImageList_Draw(hIml, 1, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Miranda"), &rc);
+
+ rc.left = prefix + width * 2;
+ ImageList_Draw(hIml, 2, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Window"), &rc);
+ return TRUE;
+ }
+
+ if (lpdis->CtlID == IDC_CANVAS) {
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Actions:"), &rc);
+
+ rc.left = prefix;
+ ImageList_Draw(hIml, 5, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Undo"), &rc);
+
+ rc.left = prefix + width * 1;
+ ImageList_Draw(hIml, 3, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Add binding"), &rc);
+
+ rc.left = prefix + width * 2;
+ ImageList_Draw(hIml, 4, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 20;
+ sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Remove"), &rc);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ if ((LOWORD(wParam) == IDC_HOTKEY) && ((HIWORD(wParam) == EN_KILLFOCUS) || (HIWORD(wParam) == 0))) {
+ LVITEM lvi;
+ THotkeyItem *item;
+ uint16_t wHotkey = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0);
+
+ ShowWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), SW_HIDE);
+ SetFocus(hwndHotkey);
+ if (!wHotkey || (wHotkey == VK_ESCAPE) || (HIWORD(wParam) != 0))
+ break;
+
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED);
+ if (lvi.iItem >= 0) {
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (item = (THotkeyItem *)lvi.lParam) {
+ item->OptHotkey = wHotkey;
+
+ sttOptionsSetupItem(hwndHotkey, lvi.iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_LV_HOTKEYS) {
+ HWND hwndList = (HWND)wParam;
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ LVITEM lvi = {};
+ THotkeyItem *item = nullptr;
+
+ lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0)
+ return FALSE;
+
+ lvi.mask = LVIF_PARAM;
+ ListView_GetItem(hwndList, &lvi);
+ if (!(item = (THotkeyItem *)lvi.lParam))
+ return FALSE;
+
+ if (pt.x == -1 && pt.y == -1) {
+ RECT rc;
+ ListView_GetItemRect(hwndList, lvi.iItem, &rc, LVIR_LABEL);
+ pt.x = rc.left;
+ pt.y = rc.bottom;
+ ClientToScreen(hwndList, &pt);
+ }
+
+ enum { MI_CANCEL, MI_CHANGE, MI_SYSTEM, MI_LOCAL, MI_ADD, MI_REMOVE, MI_REVERT };
+
+ MENUITEMINFO mii = {};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE;
+ mii.fState = MFS_DEFAULT;
+
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, MI_CHANGE, TranslateT("Modify"));
+ SetMenuItemInfo(hMenu, MI_CHANGE, FALSE, &mii);
+ if (item->type != HKT_MANUAL) {
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ AppendMenu(hMenu, MF_STRING |
+ ((item->OptType == HKT_GLOBAL) ? MF_CHECKED : 0),
+ (UINT_PTR)MI_SYSTEM, TranslateT("System scope"));
+ AppendMenu(hMenu, MF_STRING |
+ ((item->OptType == HKT_LOCAL) ? MF_CHECKED : 0),
+ (UINT_PTR)MI_LOCAL, TranslateT("Miranda scope"));
+ }
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ if (!item->rootHotkey)
+ AppendMenu(hMenu, MF_STRING, MI_ADD, TranslateT("Add binding"));
+ else
+ AppendMenu(hMenu, MF_STRING, MI_REMOVE, TranslateT("Remove"));
+ if (item->Hotkey != item->OptHotkey) {
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ AppendMenu(hMenu, MF_STRING, MI_REVERT, TranslateT("Undo"));
+ }
+
+ switch (TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, nullptr)) {
+ case MI_CHANGE:
+ sttOptionsStartEdit(hwndDlg, hwndHotkey);
+ break;
+ case MI_SYSTEM:
+ item->OptType = HKT_GLOBAL;
+ sttOptionsSetupItem(hwndList, lvi.iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case MI_LOCAL:
+ item->OptType = HKT_LOCAL;
+ sttOptionsSetupItem(hwndList, lvi.iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case MI_ADD:
+ initialized = false;
+ sttOptionsAddHotkey(hwndList, item);
+ initialized = true;
+ break;
+ case MI_REMOVE:
+ sttOptionsDeleteHotkey(hwndList, lvi.iItem, item);
+ break;
+ case MI_REVERT:
+ item->OptHotkey = item->Hotkey;
+ sttOptionsSetupItem(hwndList, lvi.iItem, item);
+ break;
+ }
+ DestroyMenu(hMenu);
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR lpnmhdr = (LPNMHDR)lParam;
+ switch (lpnmhdr->idFrom) {
+ case 0:
+ if ((lpnmhdr->code != PSN_APPLY) && (lpnmhdr->code != PSN_RESET))
+ break;
+
+ UnregisterHotkeys();
+
+ for (auto &p : hotkeys.rev_iter())
+ if (p->OptNew && p->OptDeleted || p->rootHotkey && !p->OptHotkey || (lpnmhdr->code == PSN_APPLY) && p->OptDeleted || (lpnmhdr->code == PSN_RESET) && p->OptNew)
+ FreeHotkey(hotkeys.removeItem(&p));
+
+ if (lpnmhdr->code == PSN_APPLY) {
+ LVITEM lvi = {};
+ int count = ListView_GetItemCount(hwndHotkey);
+
+ for (auto &it : hotkeys)
+ sttOptionsSaveItem(it);
+
+ lvi.mask = LVIF_IMAGE;
+ lvi.iSubItem = COL_RESET;
+ lvi.iImage = -1;
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem)
+ ListView_SetItem(hwndHotkey, &lvi);
+ }
+
+ RegisterHotkeys();
+
+ NotifyEventHooks(hEvChanged, 0, 0);
+ break;
+
+ case IDC_LV_HOTKEYS:
+ switch (lpnmhdr->code) {
+ case NM_CLICK:
+ {
+ LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam;
+
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM | LVIF_IMAGE;
+ lvi.iItem = lpnmia->iItem;
+ ListView_GetItem(lpnmia->hdr.hwndFrom, &lvi);
+
+ auto *item = (THotkeyItem *)lvi.lParam;
+ if (item == nullptr)
+ break;
+
+ LVHITTESTINFO lvhti = {};
+ lvhti.pt = lpnmia->ptAction;
+ lvhti.iItem = lpnmia->iItem;
+ lvhti.iSubItem = lpnmia->iSubItem;
+ ListView_HitTest(lpnmia->hdr.hwndFrom, &lvhti);
+
+ if ((!item->rootHotkey && (lpnmia->iSubItem == COL_NAME) && ((lvhti.flags & LVHT_ONITEM) == LVHT_ONITEMICON) ||
+ item->rootHotkey && (lpnmia->iSubItem == COL_TYPE)) &&
+ ((item->OptType == HKT_GLOBAL) || (item->OptType == HKT_LOCAL))) {
+ item->OptType = (item->OptType == HKT_GLOBAL) ? HKT_LOCAL : HKT_GLOBAL;
+ sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item);
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ else if (lpnmia->iSubItem == COL_RESET) {
+ item->OptHotkey = item->Hotkey;
+ sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item);
+ }
+ else if (lpnmia->iSubItem == COL_ADDREMOVE) {
+ if (item->rootHotkey)
+ sttOptionsDeleteHotkey(lpnmia->hdr.hwndFrom, lpnmia->iItem, item);
+ else {
+ initialized = false;
+ sttOptionsAddHotkey(lpnmia->hdr.hwndFrom, item);
+ initialized = true;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ break;
+
+ case LVN_KEYDOWN:
+ {
+ LPNMLVKEYDOWN param = (LPNMLVKEYDOWN)lParam;
+ if (param->wVKey == VK_SUBTRACT || param->wVKey == VK_LEFT || param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) {
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0)
+ break;
+
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+ if (lvi.lParam)
+ break;
+
+ if (param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) {
+ ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, TRUE);
+ }
+ else {
+ ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, FALSE);
+ }
+ }
+ else if (param->wVKey == VK_F2)
+ sttOptionsStartEdit(hwndDlg, hwndHotkey);
+ }
+ break;
+
+ case LVN_ITEMACTIVATE:
+ {
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED);
+ if (lvi.iItem < 0) break;
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+
+ if (lvi.lParam)
+ sttOptionsStartEdit(hwndDlg, hwndHotkey);
+ else
+ ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, !ListView_GetCheckState(lpnmhdr->hwndFrom, lvi.iItem));
+ }
+ break;
+
+ case LVN_ITEMCHANGED:
+ if (initialized) {
+ LPNMLISTVIEW param = (LPNMLISTVIEW)lParam;
+ THotkeyItem *item = (THotkeyItem *)param->lParam;
+ if (param->uNewState >> 12 == param->uOldState >> 12)
+ break;
+
+ if (item && !item->rootHotkey) {
+ item->OptEnabled = ListView_GetCheckState(lpnmhdr->hwndFrom, param->iItem) ? 1 : 0;
+ sttOptionsSetChanged(item);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ else if (!item) {
+ wchar_t buf[256];
+ LVITEM lvi = {};
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = param->iItem;
+ lvi.pszText = buf;
+ lvi.cchTextMax = _countof(buf);
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+
+ if (param->uNewState >> 12 == 1) {
+ int count = ListView_GetItemCount(lpnmhdr->hwndFrom);
+ LVITEM lvi2 = {};
+ lvi2.mask = LVIF_PARAM;
+ for (lvi2.iItem = 0; lvi2.iItem < count; ++lvi2.iItem) {
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi2);
+ item = (THotkeyItem *)lvi2.lParam;
+ if (!item) continue;
+ if (!mir_wstrcmp(item->getSection(), buf)) {
+ ListView_DeleteItem(lpnmhdr->hwndFrom, lvi2.iItem);
+ --lvi2.iItem;
+ --count;
+ }
+ }
+ }
+ else if (param->uNewState >> 12 == 2) {
+ int nItems = ListView_GetItemCount(lpnmhdr->hwndFrom);
+ initialized = false;
+ for (auto &it : hotkeys) {
+ LVITEM lvi2 = {};
+ if (it->OptDeleted || mir_wstrcmp(buf, it->getSection()))
+ continue;
+
+ lvi2.mask = LVIF_PARAM | LVIF_INDENT;
+ lvi2.iIndent = 1;
+ lvi2.iItem = nItems++;
+ lvi2.lParam = (LPARAM)it;
+ ListView_InsertItem(lpnmhdr->hwndFrom, &lvi2);
+ sttOptionsSetupItem(lpnmhdr->hwndFrom, nItems - 1, it);
+ }
+ ListView_SortItemsEx(lpnmhdr->hwndFrom, sttOptionsSortList, (LPARAM)lpnmhdr->hwndFrom);
+ initialized = TRUE;
+ }
+ }
+ }
+ break;
+
+ case NM_CUSTOMDRAW:
+ {
+ NMLVCUSTOMDRAW *param = (NMLVCUSTOMDRAW *)lParam;
+ switch (param->nmcd.dwDrawStage) {
+ case CDDS_PREPAINT:
+ case CDDS_ITEMPREPAINT:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW);
+ return TRUE;
+
+ case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
+ {
+ THotkeyItem *item;
+ wchar_t buf[256];
+ LVITEM lvi = {};
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iItem = param->nmcd.dwItemSpec;
+ lvi.pszText = buf;
+ lvi.cchTextMax = _countof(buf);
+ ListView_GetItem(lpnmhdr->hwndFrom, &lvi);
+
+ item = (THotkeyItem *)lvi.lParam;
+ if (!item) {
+ RECT rc;
+ HFONT hfnt;
+
+ ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc);
+ FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
+ SetTextColor(param->nmcd.hdc, GetSysColor(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
+
+ if (param->iSubItem == 0) {
+ rc.left += 3;
+ HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL);
+ ImageList_Draw(hIml,
+ ListView_GetCheckState(hwndHotkey, lvi.iItem) ? 6 : 7,
+ param->nmcd.hdc, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT);
+ rc.left += 18;
+ hfnt = (HFONT)SelectObject(param->nmcd.hdc, (HFONT)SendMessage(GetParent(hwndDlg), PSM_GETBOLDFONT, 0, 0));
+ DrawText(param->nmcd.hdc, buf, -1, &rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
+ SelectObject(param->nmcd.hdc, hfnt);
+ }
+
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT);
+ return TRUE;
+ }
+
+ if (item->rootHotkey && (param->iSubItem == 0)) {
+ RECT rc;
+ ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc);
+ FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW));
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT);
+ return TRUE;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ {
+ int count = ListView_GetItemCount(hwndHotkey);
+
+ g_hwndHkOptions = nullptr;
+
+ KillTimer(hwndDlg, 1024);
+
+ wchar_t buf[128];
+ LVITEM lvi = {};
+ lvi.pszText = buf;
+ lvi.cchTextMax = _countof(buf);
+ for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) {
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ ListView_GetItem(hwndHotkey, &lvi);
+ if (lvi.lParam) continue;
+
+ lvi.mask = LVIF_TEXT;
+ lvi.iSubItem = 1;
+ ListView_GetItem(hwndHotkey, &lvi);
+
+ db_set_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), ListView_GetCheckState(hwndHotkey, lvi.iItem));
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+int HotkeyOptionsInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.position = -180000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_HOTKEYS);
+ odp.szTitle.a = LPGEN("Hotkeys");
+ odp.szGroup.a = LPGEN("Customize");
+ odp.pfnDlgProc = sttOptionsDlgProc;
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/mir_app/src/hotkeys.cpp b/src/mir_app/src/hotkeys.cpp index 336bf3602e..a60cf84fe8 100644 --- a/src/mir_app/src/hotkeys.cpp +++ b/src/mir_app/src/hotkeys.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/icolib.cpp b/src/mir_app/src/icolib.cpp index aaccc00fd1..3327151b41 100644 --- a/src/mir_app/src/icolib.cpp +++ b/src/mir_app/src/icolib.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/idle.cpp b/src/mir_app/src/idle.cpp index cb1c771f11..fdf0f7b49b 100644 --- a/src/mir_app/src/idle.cpp +++ b/src/mir_app/src/idle.cpp @@ -1,87 +1,87 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define IDLE_MODULE "Idle" - -static bool bModuleInitialized = false; - -static int g_idleType; -static int g_bIsIdle; - -static HANDLE hIdleEvent; - -MIR_APP_DLL(void) Idle_Enter(int type) -{ - int flags = 0; - - if (db_get_b(0, IDLE_MODULE, "IdlePrivate")) - flags |= IDF_PRIVACY; - - if (!g_bIsIdle && type != -1) { - g_bIsIdle = true; - g_idleType = type; - NotifyEventHooks(hIdleEvent, 0, IDF_ISIDLE | flags); - } - - if (g_bIsIdle && type == -1) { - g_bIsIdle = false; - g_idleType = 0; - NotifyEventHooks(hIdleEvent, 0, flags); - } -} - -MIR_APP_DLL(void) Idle_GetInfo(MIRANDA_IDLE_INFO &pInfo) -{ - pInfo.idleTime = db_get_dw(0, IDLE_MODULE, "IdleTime1st"); - pInfo.privacy = db_get_b(0, IDLE_MODULE, "IdlePrivate"); - pInfo.aaStatus = db_get_b(0, IDLE_MODULE, "AAEnable", 1) ? db_get_dw(0, IDLE_MODULE, "AAStatus") : 0; - pInfo.aaLock = db_get_b(0, IDLE_MODULE, "IdleStatusLock"); - pInfo.idlesoundsoff = db_get_b(0, IDLE_MODULE, "IdleSoundsOff"); - pInfo.idleType = g_idleType; -} - -int LoadIdleModule(void) -{ - bModuleInitialized = true; - - hIdleEvent = CreateHookableEvent(ME_IDLE_CHANGED); - - g_idleType = g_bIsIdle = 0; - return 0; -} - -void UnloadIdleModule() -{ - if (!bModuleInitialized) return; - - if (g_bIsIdle) { - NotifyEventHooks(hIdleEvent, 0, 0); - g_bIsIdle = false; - } - - DestroyHookableEvent(hIdleEvent); - hIdleEvent = nullptr; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#define IDLE_MODULE "Idle"
+
+static bool bModuleInitialized = false;
+
+static int g_idleType;
+static int g_bIsIdle;
+
+static HANDLE hIdleEvent;
+
+MIR_APP_DLL(void) Idle_Enter(int type)
+{
+ int flags = 0;
+
+ if (db_get_b(0, IDLE_MODULE, "IdlePrivate"))
+ flags |= IDF_PRIVACY;
+
+ if (!g_bIsIdle && type != -1) {
+ g_bIsIdle = true;
+ g_idleType = type;
+ NotifyEventHooks(hIdleEvent, 0, IDF_ISIDLE | flags);
+ }
+
+ if (g_bIsIdle && type == -1) {
+ g_bIsIdle = false;
+ g_idleType = 0;
+ NotifyEventHooks(hIdleEvent, 0, flags);
+ }
+}
+
+MIR_APP_DLL(void) Idle_GetInfo(MIRANDA_IDLE_INFO &pInfo)
+{
+ pInfo.idleTime = db_get_dw(0, IDLE_MODULE, "IdleTime1st");
+ pInfo.privacy = db_get_b(0, IDLE_MODULE, "IdlePrivate");
+ pInfo.aaStatus = db_get_b(0, IDLE_MODULE, "AAEnable", 1) ? db_get_dw(0, IDLE_MODULE, "AAStatus") : 0;
+ pInfo.aaLock = db_get_b(0, IDLE_MODULE, "IdleStatusLock");
+ pInfo.idlesoundsoff = db_get_b(0, IDLE_MODULE, "IdleSoundsOff");
+ pInfo.idleType = g_idleType;
+}
+
+int LoadIdleModule(void)
+{
+ bModuleInitialized = true;
+
+ hIdleEvent = CreateHookableEvent(ME_IDLE_CHANGED);
+
+ g_idleType = g_bIsIdle = 0;
+ return 0;
+}
+
+void UnloadIdleModule()
+{
+ if (!bModuleInitialized) return;
+
+ if (g_bIsIdle) {
+ NotifyEventHooks(hIdleEvent, 0, 0);
+ g_bIsIdle = false;
+ }
+
+ DestroyHookableEvent(hIdleEvent);
+ hIdleEvent = nullptr;
+}
diff --git a/src/mir_app/src/ignore.cpp b/src/mir_app/src/ignore.cpp index 4e5eb804cf..df84141dfc 100644 --- a/src/mir_app/src/ignore.cpp +++ b/src/mir_app/src/ignore.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/keyboard.cpp b/src/mir_app/src/keyboard.cpp index dd5643ae7e..87929e756c 100644 --- a/src/mir_app/src/keyboard.cpp +++ b/src/mir_app/src/keyboard.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/langpack.h b/src/mir_app/src/langpack.h index 04a5abd7d1..1683e3e0ac 100644 --- a/src/mir_app/src/langpack.h +++ b/src/mir_app/src/langpack.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/lpopts.cpp b/src/mir_app/src/lpopts.cpp index 3675694ff9..7444c8e1dc 100644 --- a/src/mir_app/src/lpopts.cpp +++ b/src/mir_app/src/lpopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/mdatabasecache.cpp b/src/mir_app/src/mdatabasecache.cpp index 29cc99cfb5..01ef939bd0 100644 --- a/src/mir_app/src/mdatabasecache.cpp +++ b/src/mir_app/src/mdatabasecache.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/menu_clist.cpp b/src/mir_app/src/menu_clist.cpp index 0a545db4a0..bf94b21017 100644 --- a/src/mir_app/src/menu_clist.cpp +++ b/src/mir_app/src/menu_clist.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/menu_frames.cpp b/src/mir_app/src/menu_frames.cpp index 323cd0c5b0..1b64b9bd0b 100644 --- a/src/mir_app/src/menu_frames.cpp +++ b/src/mir_app/src/menu_frames.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/menu_groups.cpp b/src/mir_app/src/menu_groups.cpp index 4b12e7a0f8..3460d68b1d 100644 --- a/src/mir_app/src/menu_groups.cpp +++ b/src/mir_app/src/menu_groups.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/menu_options.cpp b/src/mir_app/src/menu_options.cpp index 648e3bbc4e..2f5456918b 100644 --- a/src/mir_app/src/menu_options.cpp +++ b/src/mir_app/src/menu_options.cpp @@ -1,588 +1,588 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "genmenu.h" -#include "plugins.h" - -#define STR_SEPARATOR L"-----------------------------------" - -extern bool bIconsDisabled; -extern int DefaultImageListColorDepth; -void RebuildProtoMenus(); - -MIR_APP_DLL(void) Menu_SetVisible(TMO_IntMenuItem *pimi, bool bVisible) -{ - if ((pimi = MO_GetIntMenuItem(pimi)) == nullptr) - return; - - char szModule[256], menuItemName[256]; - mir_snprintf(szModule, "%s_Items", pimi->parent->pszName); - bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName); - - ptrW wszValue(db_get_wsa(0, szModule, menuItemName, L"1;;;")); - wszValue[0] = bVisible ? '1' : '0'; - db_set_ws(0, szModule, menuItemName, wszValue); - - Menu_ShowItem(pimi, bVisible); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct MenuItemOptData : public MZeroedObject -{ - ~MenuItemOptData() {} - - int pos; - - ptrW name; - ptrW defname; - - bool bShow; - int id; - - TMO_IntMenuItem *pimi; -}; - -static int SortMenuItems(const MenuItemOptData *p1, const MenuItemOptData *p2) -{ - if (p1->pos < p2->pos) return -1; - if (p1->pos > p2->pos) return 1; - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CGenMenuOptionsPage : public CDlgBase -{ - int iInitMenuValue; - LIST<TMO_IntMenuItem> m_arDeleted; - - wchar_t idstr[100]; - - void SaveTreeInternal(MenuItemOptData *pParent, HTREEITEM hRootItem, const char *szModule) - { - TVITEMEX tvi; - tvi.hItem = hRootItem; - tvi.cchTextMax = _countof(idstr); - tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE | TVIF_IMAGE; - tvi.pszText = idstr; - - int count = 0, customOrder = 0; - int runtimepos = 100; - - char pszParent[33]; - if (pParent == nullptr) - pszParent[0] = 0; - else - bin2hex(&pParent->pimi->mi.uid, sizeof(MUUID), pszParent); - - while (tvi.hItem != nullptr) { - m_menuItems.GetItem(&tvi); - auto *iod = (MenuItemOptData*)tvi.lParam; - if (TMO_IntMenuItem *pimi = iod->pimi) { - if (pimi->mi.uid != miid_last) { - char menuItemName[256]; - bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName); - - int visible = tvi.iImage != 0; - wchar_t *ptszCustomName; - if (iod->name != nullptr && iod->defname != nullptr && mir_wstrcmp(iod->name, iod->defname) != 0) - ptszCustomName = iod->name; - else - ptszCustomName = L""; - - CMStringW tszValue(FORMAT, L"%d;%d;%S;%s", visible, runtimepos, pszParent, ptszCustomName); - db_set_ws(0, szModule, menuItemName, tszValue); - - if (pimi->mi.flags & CMIF_CUSTOM) - db_set_s(0, szModule, CMStringA(FORMAT, "Custom%d", customOrder++), menuItemName); - } - - HTREEITEM hChild = m_menuItems.GetChild(tvi.hItem); - if (hChild != nullptr) - SaveTreeInternal(iod, hChild, szModule); - - runtimepos += 100; - } - - if (iod->name && !mir_wstrcmp(iod->name, STR_SEPARATOR) && tvi.iImage) - runtimepos += SEPARATORPOSITIONINTERVAL; - - tvi.hItem = m_menuItems.GetNextSibling(tvi.hItem); - count++; - } - } - - void SaveTree() - { - int MenuObjectId; - if (!GetCurrentMenuObjectID(MenuObjectId)) - return; - - TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId); - if (pmo == nullptr) - return; - - char szModule[256]; - mir_snprintf(szModule, "%s_Items", pmo->pszName); - db_delete_module(0, szModule); - SaveTreeInternal(nullptr, m_menuItems.GetRoot(), szModule); - db_set_b(0, szModule, "MenuFormat", 1); - } - - void FreeTreeData() - { - HTREEITEM hItem = m_menuItems.GetRoot(); - while (hItem != nullptr) { - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hItem; - m_menuItems.GetItem(&tvi); - delete (MenuItemOptData *)tvi.lParam; - - tvi.lParam = 0; - m_menuItems.SetItem(&tvi); - - hItem = m_menuItems.GetNextSibling(hItem); - } - } - - void RebuildCurrent() - { - int MenuObjectID; - if (GetCurrentMenuObjectID(MenuObjectID)) - BuildTree(MenuObjectID, true); - } - - void BuildTreeInternal(const char *pszModule, bool bReread, TMO_IntMenuItem *pFirst, HTREEITEM hRoot) - { - LIST<MenuItemOptData> arItems(10, SortMenuItems); - - for (TMO_IntMenuItem *p = pFirst; p != nullptr; p = p->next) { - // filter out items whose presence & position might not be changed - if (p->mi.flags & CMIF_SYSTEM) - continue; - - MenuItemOptData *PD = new MenuItemOptData(); - PD->pimi = p; - PD->defname = mir_wstrdup(GetMenuItemText(p)); - PD->name = mir_wstrdup((bReread && p->ptszCustomName != nullptr) ? p->ptszCustomName : PD->defname); - PD->bShow = (p->mi.flags & CMIF_HIDDEN) == 0; - PD->pos = (bReread) ? p->mi.position : p->originalPosition; - PD->id = p->iCommand; - arItems.insert(PD); - } - - int lastpos = 0; - bool bIsFirst = TRUE; - - TVINSERTSTRUCT tvis; - tvis.hParent = hRoot; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - - for (auto &it : arItems) { - if (it != arItems[0] && it->pos - lastpos >= SEPARATORPOSITIONINTERVAL) { - MenuItemOptData *sep = new MenuItemOptData(); - sep->id = -1; - sep->name = mir_wstrdup(STR_SEPARATOR); - sep->pos = it->pos - 1; - - tvis.item.lParam = (LPARAM)sep; - tvis.item.pszText = sep->name; - tvis.item.iImage = tvis.item.iSelectedImage = 1; - tvis.item.cChildren = 0; - m_menuItems.InsertItem(&tvis); - } - - tvis.item.lParam = (LPARAM)it; - tvis.item.pszText = it->name; - tvis.item.iImage = tvis.item.iSelectedImage = it->bShow; - tvis.item.cChildren = it->pimi->submenu.first != nullptr; - - HTREEITEM hti = m_menuItems.InsertItem(&tvis); - if (bIsFirst) { - if (hRoot == nullptr) - m_menuItems.SelectItem(hti); - bIsFirst = false; - } - - if (it->pimi->submenu.first != nullptr) { - BuildTreeInternal(pszModule, bReread, it->pimi->submenu.first, hti); - m_menuItems.Expand(hti, TVE_EXPAND); - } - - lastpos = it->pos; - } - } - - bool BuildTree(int MenuObjectId, bool bReread) - { - FreeTreeData(); - - TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId); - if (pmo == nullptr || pmo->m_items.first == nullptr) - return false; - - char szModule[256]; - mir_snprintf(szModule, "%s_Items", pmo->pszName); - - if (bReread) // no need to reread database on reset - MO_RecursiveWalkMenu(pmo->m_items.first, Menu_LoadFromDatabase, szModule); - - m_menuItems.SetDraw(false); - m_menuItems.DeleteAllItems(); - - BuildTreeInternal(szModule, bReread, pmo->m_items.first, nullptr); - - m_menuItems.SetDraw(true); - - m_warning.Show(!pmo->m_bUseUserDefinedItems); - m_menuItems.Enable(pmo->m_bUseUserDefinedItems); - m_btnInsSeparator.Enable(pmo->m_bUseUserDefinedItems); - m_btnInsMenu.Enable(pmo->m_bUseUserDefinedItems); - return 1; - } - - bool GetCurrentMenuObjectID(int &result) - { - int iItem = m_menuObjects.GetCurSel(); - if (iItem == -1) - return false; - - result = (int)m_menuObjects.GetItemData(iItem); - return true; - } - - CCtrlListBox m_menuObjects; - CCtrlTreeView m_menuItems; - CCtrlCheck m_radio1, m_radio2, m_enableIcons; - CCtrlEdit m_customName, m_service, m_module; - CCtrlButton m_btnInsSeparator, m_btnInsMenu, m_btnReset, m_btnSet, m_btnDefault, m_btnDelete; - CCtrlBase m_warning; - -public: - CGenMenuOptionsPage() : - CDlgBase(g_plugin, IDD_OPT_GENMENU), - m_arDeleted(1), - m_menuItems(this, IDC_MENUITEMS), - m_menuObjects(this, IDC_MENUOBJECTS), - m_radio1(this, IDC_RADIO1), - m_radio2(this, IDC_RADIO2), - m_enableIcons(this, IDC_DISABLEMENUICONS), - m_btnInsSeparator(this, IDC_INSERTSEPARATOR), - m_btnInsMenu(this, IDC_INSERTSUBMENU), - m_btnReset(this, IDC_RESETMENU), - m_btnSet(this, IDC_GENMENU_SET), - m_btnDelete(this, IDC_GENMENU_DELETE), - m_btnDefault(this, IDC_GENMENU_DEFAULT), - m_customName(this, IDC_GENMENU_CUSTOMNAME), - m_service(this, IDC_GENMENU_SERVICE), - m_module(this, IDC_GENMENU_MODULE), - m_warning(this, IDC_NOTSUPPORTWARNING) - { - m_btnSet.OnClick = Callback(this, &CGenMenuOptionsPage::btnSet_Clicked); - m_btnReset.OnClick = Callback(this, &CGenMenuOptionsPage::btnReset_Clicked); - m_btnInsSeparator.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsSep_Clicked); - m_btnInsMenu.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsMenu_Clicked); - m_btnDefault.OnClick = Callback(this, &CGenMenuOptionsPage::btnDefault_Clicked); - m_btnDelete.OnClick = Callback(this, &CGenMenuOptionsPage::btnDelete_Clicked); - - m_menuObjects.OnSelChange = Callback(this, &CGenMenuOptionsPage::onMenuObjectChanged); - - m_menuItems.SetFlags(MTREE_CHECKBOX | MTREE_DND); - m_menuItems.OnSelChanged = Callback(this, &CGenMenuOptionsPage::onMenuItemChanged); - m_menuItems.OnBeginDrag = Callback(this, &CGenMenuOptionsPage::onMenuItemBeginDrag); - - m_customName.SetSilent(); - m_service.SetSilent(); - m_module.SetSilent(); - } - - //---- init dialog ------------------------------------------- - bool OnInitDialog() override - { - iInitMenuValue = db_get_b(0, "CList", "MoveProtoMenus", TRUE); - - if (iInitMenuValue) - m_radio2.SetState(true); - else - m_radio1.SetState(true); - - m_enableIcons.SetState(!bIconsDisabled); - - //---- init menu object list -------------------------------------- - for (auto &p : g_menus) - if (p->id != (int)hStatusMenuObject && p->m_bUseUserDefinedItems) - m_menuObjects.AddString(TranslateW(p->ptszDisplayName), p->id); - - m_menuObjects.SetCurSel(0); - RebuildCurrent(); - return true; - } - - bool OnApply() override - { - bIconsDisabled = m_enableIcons.GetState() == 0; - db_set_b(0, "CList", "DisableMenuIcons", bIconsDisabled); - SaveTree(); - - for (auto &pimi : m_arDeleted) - Menu_RemoveItem(pimi); - - int iNewMenuValue = !m_radio1.GetState(); - if (iNewMenuValue != iInitMenuValue) { - db_set_b(0, "CList", "MoveProtoMenus", iNewMenuValue); - - RebuildProtoMenus(); - iInitMenuValue = iNewMenuValue; - } - RebuildCurrent(); - return true; - } - - void OnDestroy() override - { - FreeTreeData(); - } - - void btnInsSep_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT; - tvi.hItem = hti; - if (!m_menuItems.GetItem(&tvi)) - return; - - MenuItemOptData *PD = new MenuItemOptData(); - PD->id = -1; - PD->name = mir_wstrdup(STR_SEPARATOR); - PD->pos = ((MenuItemOptData *)tvi.lParam)->pos - 1; - - TVINSERTSTRUCT tvis = {}; - tvis.item.lParam = (LPARAM)PD; - tvis.item.pszText = PD->name; - tvis.item.iImage = tvis.item.iSelectedImage = 1; - tvis.hInsertAfter = hti; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - m_menuItems.InsertItem(&tvis); - - NotifyChange(); - } - - void btnInsMenu_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT; - tvi.hItem = hti; - if (!m_menuItems.GetItem(&tvi)) - return; - - MenuItemOptData *curData = (MenuItemOptData*)tvi.lParam; - - TMO_MenuItem mi = {}; - UuidCreate((UUID*)&mi.uid); - mi.flags = CMIF_CUSTOM; - mi.name.a = LPGEN("New submenu"); - mi.position = curData->pos - 1; - TMO_IntMenuItem *pimi = Menu_AddItem(curData->pimi->parent->id, &mi, nullptr); - - MenuItemOptData *PD = new MenuItemOptData(); - PD->id = -1; - PD->name = mir_wstrdup(pimi->mi.name.w); - PD->pos = pimi->mi.position; - PD->pimi = pimi; - - TVINSERTSTRUCT tvis = {}; - tvis.item.lParam = (LPARAM)PD; - tvis.item.pszText = PD->name; - tvis.item.iImage = tvis.item.iSelectedImage = 1; - tvis.hInsertAfter = hti; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - m_menuItems.InsertItem(&tvis); - - NotifyChange(); - } - - void btnReset_Clicked(CCtrlButton*) - { - int MenuObjectID; - if (GetCurrentMenuObjectID(MenuObjectID)) { - BuildTree(MenuObjectID, false); - NotifyChange(); - } - } - - void btnDefault_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) - return; - - iod->name = mir_wstrdup(iod->defname); - m_customName.SetText(iod->defname); - - tvi.mask = TVIF_TEXT; - tvi.pszText = iod->name; - m_menuItems.SetItem(&tvi); - NotifyChange(); - } - - void btnSet_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) - return; - - iod->name = m_customName.GetText(); - - tvi.mask = TVIF_TEXT; - tvi.pszText = iod->name; - m_menuItems.SetItem(&tvi); - NotifyChange(); - } - - void btnDelete_Clicked(CCtrlButton *) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (!(iod->pimi->mi.flags & CMIF_CUSTOM)) - return; - - if (IDYES == MessageBoxW(m_hwnd, TranslateT("Do you really want to delete this menu item?"), TranslateT("Miranda"), MB_YESNO | MB_ICONQUESTION)) { - m_arDeleted.insert(iod->pimi); - m_menuItems.DeleteItem(hti); - delete iod; - NotifyChange(); - } - } - - void onMenuObjectChanged(void*) - { - m_bInitialized = false; - RebuildCurrent(); - m_bInitialized = true; - } - - void onMenuItemChanged(void*) - { - m_customName.SetTextA(""); - m_service.SetTextA(""); - m_module.SetTextA(""); - - m_btnInsMenu.Disable(); - m_btnDefault.Disable(); - m_btnSet.Disable(); - m_btnDelete.Disable(); - m_customName.Disable(); - - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - if (tvi.lParam == 0) - return; - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) - return; - - m_customName.SetText(iod->name); - - if (iod->pimi->mi.uid != miid_last) { - char szText[100]; - bin2hex(&iod->pimi->mi.uid, sizeof(iod->pimi->mi.uid), szText); - m_service.SetTextA(szText); - } - - const CMPluginBase *pPlugin = iod->pimi->mi.pPlugin; - m_module.SetTextA(pPlugin == nullptr ? "" : pPlugin->getInfo().shortName); - - m_btnInsMenu.Enable(iod->pimi->mi.root == nullptr); - m_btnDefault.Enable(mir_wstrcmp(iod->name, iod->defname) != 0); - m_btnDelete.Enable(iod->pimi->mi.flags & CMIF_CUSTOM); - m_btnSet.Enable(true); - m_customName.Enable(true); - } - - void onMenuItemBeginDrag(CCtrlTreeView::TEventInfo *evt) - { - MenuItemOptData *p = (MenuItemOptData*)evt->nmtv->itemNew.lParam; - if (p->pimi != nullptr) - if (p->pimi->mi.flags & CMIF_UNMOVABLE) - evt->nmhdr->code = 0; // reject an attempt to change item's position - } -}; - -int GenMenuOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = -1000000000; - odp.szTitle.a = LPGEN("Menus"); - odp.szGroup.a = LPGEN("Customize"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CGenMenuOptionsPage(); - g_plugin.addOptions(wParam, &odp); - - return ProtocolOrderOptInit(wParam, 0); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "genmenu.h"
+#include "plugins.h"
+
+#define STR_SEPARATOR L"-----------------------------------"
+
+extern bool bIconsDisabled;
+extern int DefaultImageListColorDepth;
+void RebuildProtoMenus();
+
+MIR_APP_DLL(void) Menu_SetVisible(TMO_IntMenuItem *pimi, bool bVisible)
+{
+ if ((pimi = MO_GetIntMenuItem(pimi)) == nullptr)
+ return;
+
+ char szModule[256], menuItemName[256];
+ mir_snprintf(szModule, "%s_Items", pimi->parent->pszName);
+ bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName);
+
+ ptrW wszValue(db_get_wsa(0, szModule, menuItemName, L"1;;;"));
+ wszValue[0] = bVisible ? '1' : '0';
+ db_set_ws(0, szModule, menuItemName, wszValue);
+
+ Menu_ShowItem(pimi, bVisible);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct MenuItemOptData : public MZeroedObject
+{
+ ~MenuItemOptData() {}
+
+ int pos;
+
+ ptrW name;
+ ptrW defname;
+
+ bool bShow;
+ int id;
+
+ TMO_IntMenuItem *pimi;
+};
+
+static int SortMenuItems(const MenuItemOptData *p1, const MenuItemOptData *p2)
+{
+ if (p1->pos < p2->pos) return -1;
+ if (p1->pos > p2->pos) return 1;
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CGenMenuOptionsPage : public CDlgBase
+{
+ int iInitMenuValue;
+ LIST<TMO_IntMenuItem> m_arDeleted;
+
+ wchar_t idstr[100];
+
+ void SaveTreeInternal(MenuItemOptData *pParent, HTREEITEM hRootItem, const char *szModule)
+ {
+ TVITEMEX tvi;
+ tvi.hItem = hRootItem;
+ tvi.cchTextMax = _countof(idstr);
+ tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE | TVIF_IMAGE;
+ tvi.pszText = idstr;
+
+ int count = 0, customOrder = 0;
+ int runtimepos = 100;
+
+ char pszParent[33];
+ if (pParent == nullptr)
+ pszParent[0] = 0;
+ else
+ bin2hex(&pParent->pimi->mi.uid, sizeof(MUUID), pszParent);
+
+ while (tvi.hItem != nullptr) {
+ m_menuItems.GetItem(&tvi);
+ auto *iod = (MenuItemOptData*)tvi.lParam;
+ if (TMO_IntMenuItem *pimi = iod->pimi) {
+ if (pimi->mi.uid != miid_last) {
+ char menuItemName[256];
+ bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName);
+
+ int visible = tvi.iImage != 0;
+ wchar_t *ptszCustomName;
+ if (iod->name != nullptr && iod->defname != nullptr && mir_wstrcmp(iod->name, iod->defname) != 0)
+ ptszCustomName = iod->name;
+ else
+ ptszCustomName = L"";
+
+ CMStringW tszValue(FORMAT, L"%d;%d;%S;%s", visible, runtimepos, pszParent, ptszCustomName);
+ db_set_ws(0, szModule, menuItemName, tszValue);
+
+ if (pimi->mi.flags & CMIF_CUSTOM)
+ db_set_s(0, szModule, CMStringA(FORMAT, "Custom%d", customOrder++), menuItemName);
+ }
+
+ HTREEITEM hChild = m_menuItems.GetChild(tvi.hItem);
+ if (hChild != nullptr)
+ SaveTreeInternal(iod, hChild, szModule);
+
+ runtimepos += 100;
+ }
+
+ if (iod->name && !mir_wstrcmp(iod->name, STR_SEPARATOR) && tvi.iImage)
+ runtimepos += SEPARATORPOSITIONINTERVAL;
+
+ tvi.hItem = m_menuItems.GetNextSibling(tvi.hItem);
+ count++;
+ }
+ }
+
+ void SaveTree()
+ {
+ int MenuObjectId;
+ if (!GetCurrentMenuObjectID(MenuObjectId))
+ return;
+
+ TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId);
+ if (pmo == nullptr)
+ return;
+
+ char szModule[256];
+ mir_snprintf(szModule, "%s_Items", pmo->pszName);
+ db_delete_module(0, szModule);
+ SaveTreeInternal(nullptr, m_menuItems.GetRoot(), szModule);
+ db_set_b(0, szModule, "MenuFormat", 1);
+ }
+
+ void FreeTreeData()
+ {
+ HTREEITEM hItem = m_menuItems.GetRoot();
+ while (hItem != nullptr) {
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hItem;
+ m_menuItems.GetItem(&tvi);
+ delete (MenuItemOptData *)tvi.lParam;
+
+ tvi.lParam = 0;
+ m_menuItems.SetItem(&tvi);
+
+ hItem = m_menuItems.GetNextSibling(hItem);
+ }
+ }
+
+ void RebuildCurrent()
+ {
+ int MenuObjectID;
+ if (GetCurrentMenuObjectID(MenuObjectID))
+ BuildTree(MenuObjectID, true);
+ }
+
+ void BuildTreeInternal(const char *pszModule, bool bReread, TMO_IntMenuItem *pFirst, HTREEITEM hRoot)
+ {
+ LIST<MenuItemOptData> arItems(10, SortMenuItems);
+
+ for (TMO_IntMenuItem *p = pFirst; p != nullptr; p = p->next) {
+ // filter out items whose presence & position might not be changed
+ if (p->mi.flags & CMIF_SYSTEM)
+ continue;
+
+ MenuItemOptData *PD = new MenuItemOptData();
+ PD->pimi = p;
+ PD->defname = mir_wstrdup(GetMenuItemText(p));
+ PD->name = mir_wstrdup((bReread && p->ptszCustomName != nullptr) ? p->ptszCustomName : PD->defname);
+ PD->bShow = (p->mi.flags & CMIF_HIDDEN) == 0;
+ PD->pos = (bReread) ? p->mi.position : p->originalPosition;
+ PD->id = p->iCommand;
+ arItems.insert(PD);
+ }
+
+ int lastpos = 0;
+ bool bIsFirst = TRUE;
+
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = hRoot;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+
+ for (auto &it : arItems) {
+ if (it != arItems[0] && it->pos - lastpos >= SEPARATORPOSITIONINTERVAL) {
+ MenuItemOptData *sep = new MenuItemOptData();
+ sep->id = -1;
+ sep->name = mir_wstrdup(STR_SEPARATOR);
+ sep->pos = it->pos - 1;
+
+ tvis.item.lParam = (LPARAM)sep;
+ tvis.item.pszText = sep->name;
+ tvis.item.iImage = tvis.item.iSelectedImage = 1;
+ tvis.item.cChildren = 0;
+ m_menuItems.InsertItem(&tvis);
+ }
+
+ tvis.item.lParam = (LPARAM)it;
+ tvis.item.pszText = it->name;
+ tvis.item.iImage = tvis.item.iSelectedImage = it->bShow;
+ tvis.item.cChildren = it->pimi->submenu.first != nullptr;
+
+ HTREEITEM hti = m_menuItems.InsertItem(&tvis);
+ if (bIsFirst) {
+ if (hRoot == nullptr)
+ m_menuItems.SelectItem(hti);
+ bIsFirst = false;
+ }
+
+ if (it->pimi->submenu.first != nullptr) {
+ BuildTreeInternal(pszModule, bReread, it->pimi->submenu.first, hti);
+ m_menuItems.Expand(hti, TVE_EXPAND);
+ }
+
+ lastpos = it->pos;
+ }
+ }
+
+ bool BuildTree(int MenuObjectId, bool bReread)
+ {
+ FreeTreeData();
+
+ TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId);
+ if (pmo == nullptr || pmo->m_items.first == nullptr)
+ return false;
+
+ char szModule[256];
+ mir_snprintf(szModule, "%s_Items", pmo->pszName);
+
+ if (bReread) // no need to reread database on reset
+ MO_RecursiveWalkMenu(pmo->m_items.first, Menu_LoadFromDatabase, szModule);
+
+ m_menuItems.SetDraw(false);
+ m_menuItems.DeleteAllItems();
+
+ BuildTreeInternal(szModule, bReread, pmo->m_items.first, nullptr);
+
+ m_menuItems.SetDraw(true);
+
+ m_warning.Show(!pmo->m_bUseUserDefinedItems);
+ m_menuItems.Enable(pmo->m_bUseUserDefinedItems);
+ m_btnInsSeparator.Enable(pmo->m_bUseUserDefinedItems);
+ m_btnInsMenu.Enable(pmo->m_bUseUserDefinedItems);
+ return 1;
+ }
+
+ bool GetCurrentMenuObjectID(int &result)
+ {
+ int iItem = m_menuObjects.GetCurSel();
+ if (iItem == -1)
+ return false;
+
+ result = (int)m_menuObjects.GetItemData(iItem);
+ return true;
+ }
+
+ CCtrlListBox m_menuObjects;
+ CCtrlTreeView m_menuItems;
+ CCtrlCheck m_radio1, m_radio2, m_enableIcons;
+ CCtrlEdit m_customName, m_service, m_module;
+ CCtrlButton m_btnInsSeparator, m_btnInsMenu, m_btnReset, m_btnSet, m_btnDefault, m_btnDelete;
+ CCtrlBase m_warning;
+
+public:
+ CGenMenuOptionsPage() :
+ CDlgBase(g_plugin, IDD_OPT_GENMENU),
+ m_arDeleted(1),
+ m_menuItems(this, IDC_MENUITEMS),
+ m_menuObjects(this, IDC_MENUOBJECTS),
+ m_radio1(this, IDC_RADIO1),
+ m_radio2(this, IDC_RADIO2),
+ m_enableIcons(this, IDC_DISABLEMENUICONS),
+ m_btnInsSeparator(this, IDC_INSERTSEPARATOR),
+ m_btnInsMenu(this, IDC_INSERTSUBMENU),
+ m_btnReset(this, IDC_RESETMENU),
+ m_btnSet(this, IDC_GENMENU_SET),
+ m_btnDelete(this, IDC_GENMENU_DELETE),
+ m_btnDefault(this, IDC_GENMENU_DEFAULT),
+ m_customName(this, IDC_GENMENU_CUSTOMNAME),
+ m_service(this, IDC_GENMENU_SERVICE),
+ m_module(this, IDC_GENMENU_MODULE),
+ m_warning(this, IDC_NOTSUPPORTWARNING)
+ {
+ m_btnSet.OnClick = Callback(this, &CGenMenuOptionsPage::btnSet_Clicked);
+ m_btnReset.OnClick = Callback(this, &CGenMenuOptionsPage::btnReset_Clicked);
+ m_btnInsSeparator.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsSep_Clicked);
+ m_btnInsMenu.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsMenu_Clicked);
+ m_btnDefault.OnClick = Callback(this, &CGenMenuOptionsPage::btnDefault_Clicked);
+ m_btnDelete.OnClick = Callback(this, &CGenMenuOptionsPage::btnDelete_Clicked);
+
+ m_menuObjects.OnSelChange = Callback(this, &CGenMenuOptionsPage::onMenuObjectChanged);
+
+ m_menuItems.SetFlags(MTREE_CHECKBOX | MTREE_DND);
+ m_menuItems.OnSelChanged = Callback(this, &CGenMenuOptionsPage::onMenuItemChanged);
+ m_menuItems.OnBeginDrag = Callback(this, &CGenMenuOptionsPage::onMenuItemBeginDrag);
+
+ m_customName.SetSilent();
+ m_service.SetSilent();
+ m_module.SetSilent();
+ }
+
+ //---- init dialog -------------------------------------------
+ bool OnInitDialog() override
+ {
+ iInitMenuValue = db_get_b(0, "CList", "MoveProtoMenus", TRUE);
+
+ if (iInitMenuValue)
+ m_radio2.SetState(true);
+ else
+ m_radio1.SetState(true);
+
+ m_enableIcons.SetState(!bIconsDisabled);
+
+ //---- init menu object list --------------------------------------
+ for (auto &p : g_menus)
+ if (p->id != (int)hStatusMenuObject && p->m_bUseUserDefinedItems)
+ m_menuObjects.AddString(TranslateW(p->ptszDisplayName), p->id);
+
+ m_menuObjects.SetCurSel(0);
+ RebuildCurrent();
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ bIconsDisabled = m_enableIcons.GetState() == 0;
+ db_set_b(0, "CList", "DisableMenuIcons", bIconsDisabled);
+ SaveTree();
+
+ for (auto &pimi : m_arDeleted)
+ Menu_RemoveItem(pimi);
+
+ int iNewMenuValue = !m_radio1.GetState();
+ if (iNewMenuValue != iInitMenuValue) {
+ db_set_b(0, "CList", "MoveProtoMenus", iNewMenuValue);
+
+ RebuildProtoMenus();
+ iInitMenuValue = iNewMenuValue;
+ }
+ RebuildCurrent();
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ FreeTreeData();
+ }
+
+ void btnInsSep_Clicked(CCtrlButton*)
+ {
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT;
+ tvi.hItem = hti;
+ if (!m_menuItems.GetItem(&tvi))
+ return;
+
+ MenuItemOptData *PD = new MenuItemOptData();
+ PD->id = -1;
+ PD->name = mir_wstrdup(STR_SEPARATOR);
+ PD->pos = ((MenuItemOptData *)tvi.lParam)->pos - 1;
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.item.lParam = (LPARAM)PD;
+ tvis.item.pszText = PD->name;
+ tvis.item.iImage = tvis.item.iSelectedImage = 1;
+ tvis.hInsertAfter = hti;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ m_menuItems.InsertItem(&tvis);
+
+ NotifyChange();
+ }
+
+ void btnInsMenu_Clicked(CCtrlButton*)
+ {
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT;
+ tvi.hItem = hti;
+ if (!m_menuItems.GetItem(&tvi))
+ return;
+
+ MenuItemOptData *curData = (MenuItemOptData*)tvi.lParam;
+
+ TMO_MenuItem mi = {};
+ UuidCreate((UUID*)&mi.uid);
+ mi.flags = CMIF_CUSTOM;
+ mi.name.a = LPGEN("New submenu");
+ mi.position = curData->pos - 1;
+ TMO_IntMenuItem *pimi = Menu_AddItem(curData->pimi->parent->id, &mi, nullptr);
+
+ MenuItemOptData *PD = new MenuItemOptData();
+ PD->id = -1;
+ PD->name = mir_wstrdup(pimi->mi.name.w);
+ PD->pos = pimi->mi.position;
+ PD->pimi = pimi;
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.item.lParam = (LPARAM)PD;
+ tvis.item.pszText = PD->name;
+ tvis.item.iImage = tvis.item.iSelectedImage = 1;
+ tvis.hInsertAfter = hti;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ m_menuItems.InsertItem(&tvis);
+
+ NotifyChange();
+ }
+
+ void btnReset_Clicked(CCtrlButton*)
+ {
+ int MenuObjectID;
+ if (GetCurrentMenuObjectID(MenuObjectID)) {
+ BuildTree(MenuObjectID, false);
+ NotifyChange();
+ }
+ }
+
+ void btnDefault_Clicked(CCtrlButton*)
+ {
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
+ tvi.hItem = hti;
+ m_menuItems.GetItem(&tvi);
+
+ MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam;
+ if (iod->name && wcsstr(iod->name, STR_SEPARATOR))
+ return;
+
+ iod->name = mir_wstrdup(iod->defname);
+ m_customName.SetText(iod->defname);
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = iod->name;
+ m_menuItems.SetItem(&tvi);
+ NotifyChange();
+ }
+
+ void btnSet_Clicked(CCtrlButton*)
+ {
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
+ tvi.hItem = hti;
+ m_menuItems.GetItem(&tvi);
+
+ MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam;
+ if (iod->name && wcsstr(iod->name, STR_SEPARATOR))
+ return;
+
+ iod->name = m_customName.GetText();
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = iod->name;
+ m_menuItems.SetItem(&tvi);
+ NotifyChange();
+ }
+
+ void btnDelete_Clicked(CCtrlButton *)
+ {
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_PARAM;
+ tvi.hItem = hti;
+ m_menuItems.GetItem(&tvi);
+
+ MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam;
+ if (!(iod->pimi->mi.flags & CMIF_CUSTOM))
+ return;
+
+ if (IDYES == MessageBoxW(m_hwnd, TranslateT("Do you really want to delete this menu item?"), TranslateT("Miranda"), MB_YESNO | MB_ICONQUESTION)) {
+ m_arDeleted.insert(iod->pimi);
+ m_menuItems.DeleteItem(hti);
+ delete iod;
+ NotifyChange();
+ }
+ }
+
+ void onMenuObjectChanged(void*)
+ {
+ m_bInitialized = false;
+ RebuildCurrent();
+ m_bInitialized = true;
+ }
+
+ void onMenuItemChanged(void*)
+ {
+ m_customName.SetTextA("");
+ m_service.SetTextA("");
+ m_module.SetTextA("");
+
+ m_btnInsMenu.Disable();
+ m_btnDefault.Disable();
+ m_btnSet.Disable();
+ m_btnDelete.Disable();
+ m_customName.Disable();
+
+ HTREEITEM hti = m_menuItems.GetSelection();
+ if (hti == nullptr)
+ return;
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
+ tvi.hItem = hti;
+ m_menuItems.GetItem(&tvi);
+ if (tvi.lParam == 0)
+ return;
+
+ MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam;
+ if (iod->name && wcsstr(iod->name, STR_SEPARATOR))
+ return;
+
+ m_customName.SetText(iod->name);
+
+ if (iod->pimi->mi.uid != miid_last) {
+ char szText[100];
+ bin2hex(&iod->pimi->mi.uid, sizeof(iod->pimi->mi.uid), szText);
+ m_service.SetTextA(szText);
+ }
+
+ const CMPluginBase *pPlugin = iod->pimi->mi.pPlugin;
+ m_module.SetTextA(pPlugin == nullptr ? "" : pPlugin->getInfo().shortName);
+
+ m_btnInsMenu.Enable(iod->pimi->mi.root == nullptr);
+ m_btnDefault.Enable(mir_wstrcmp(iod->name, iod->defname) != 0);
+ m_btnDelete.Enable(iod->pimi->mi.flags & CMIF_CUSTOM);
+ m_btnSet.Enable(true);
+ m_customName.Enable(true);
+ }
+
+ void onMenuItemBeginDrag(CCtrlTreeView::TEventInfo *evt)
+ {
+ MenuItemOptData *p = (MenuItemOptData*)evt->nmtv->itemNew.lParam;
+ if (p->pimi != nullptr)
+ if (p->pimi->mi.flags & CMIF_UNMOVABLE)
+ evt->nmhdr->code = 0; // reject an attempt to change item's position
+ }
+};
+
+int GenMenuOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = -1000000000;
+ odp.szTitle.a = LPGEN("Menus");
+ odp.szGroup.a = LPGEN("Customize");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new CGenMenuOptionsPage();
+ g_plugin.addOptions(wParam, &odp);
+
+ return ProtocolOrderOptInit(wParam, 0);
+}
diff --git a/src/mir_app/src/menu_tray.cpp b/src/mir_app/src/menu_tray.cpp index 69446e1590..77b4a482b9 100644 --- a/src/mir_app/src/menu_tray.cpp +++ b/src/mir_app/src/menu_tray.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/menu_utils.cpp b/src/mir_app/src/menu_utils.cpp index 12df821386..ff048552de 100644 --- a/src/mir_app/src/menu_utils.cpp +++ b/src/mir_app/src/menu_utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/meta_addto.cpp b/src/mir_app/src/meta_addto.cpp index 7cf6b8d7ce..345598d429 100644 --- a/src/mir_app/src/meta_addto.cpp +++ b/src/mir_app/src/meta_addto.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_api.cpp b/src/mir_app/src/meta_api.cpp index 97da5e0acd..886e0be1c7 100644 --- a/src/mir_app/src/meta_api.cpp +++ b/src/mir_app/src/meta_api.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_edit.cpp b/src/mir_app/src/meta_edit.cpp index 9f57a4131f..ac7917ac77 100644 --- a/src/mir_app/src/meta_edit.cpp +++ b/src/mir_app/src/meta_edit.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_main.cpp b/src/mir_app/src/meta_main.cpp index 7ac06606b7..f59794693b 100644 --- a/src/mir_app/src/meta_main.cpp +++ b/src/mir_app/src/meta_main.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_menu.cpp b/src/mir_app/src/meta_menu.cpp index dc4ad1252f..1ae7ec2247 100644 --- a/src/mir_app/src/meta_menu.cpp +++ b/src/mir_app/src/meta_menu.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_options.cpp b/src/mir_app/src/meta_options.cpp index b6e3f81b7f..71f7c6a997 100644 --- a/src/mir_app/src/meta_options.cpp +++ b/src/mir_app/src/meta_options.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_services.cpp b/src/mir_app/src/meta_services.cpp index e8d5b042d6..74f218e3d1 100644 --- a/src/mir_app/src/meta_services.cpp +++ b/src/mir_app/src/meta_services.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/meta_utils.cpp b/src/mir_app/src/meta_utils.cpp index ad75ed4910..3db142ba98 100644 --- a/src/mir_app/src/meta_utils.cpp +++ b/src/mir_app/src/meta_utils.cpp @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/metacontacts.h b/src/mir_app/src/metacontacts.h index 49e86ef32c..47f8b5bab2 100644 --- a/src/mir_app/src/metacontacts.h +++ b/src/mir_app/src/metacontacts.h @@ -1,7 +1,7 @@ /*
former MetaContacts Plugin for Miranda IM.
-Copyright © 2014-22 Miranda NG team
+Copyright © 2014-23 Miranda NG team
Copyright © 2004-07 Scott Ellis
Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
diff --git a/src/mir_app/src/miranda.cpp b/src/mir_app/src/miranda.cpp index 7628da2e66..850e769535 100644 --- a/src/mir_app/src/miranda.cpp +++ b/src/mir_app/src/miranda.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/miranda.h b/src/mir_app/src/miranda.h index b3dd931124..b0508c18f3 100644 --- a/src/mir_app/src/miranda.h +++ b/src/mir_app/src/miranda.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/modules.cpp b/src/mir_app/src/modules.cpp index 2274247cc2..ff0b6682f2 100644 --- a/src/mir_app/src/modules.cpp +++ b/src/mir_app/src/modules.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/movetogroup.cpp b/src/mir_app/src/movetogroup.cpp index 4f810c4002..3eb84c9e87 100644 --- a/src/mir_app/src/movetogroup.cpp +++ b/src/mir_app/src/movetogroup.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/netlib.cpp b/src/mir_app/src/netlib.cpp index 68c2d6e3d0..2914620911 100644 --- a/src/mir_app/src/netlib.cpp +++ b/src/mir_app/src/netlib.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/netlib.h b/src/mir_app/src/netlib.h index 4b85f6c365..09480637bf 100644 --- a/src/mir_app/src/netlib.h +++ b/src/mir_app/src/netlib.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/netlib_autoproxy.cpp b/src/mir_app/src/netlib_autoproxy.cpp index cc2ef2b9e1..f325f3a721 100644 --- a/src/mir_app/src/netlib_autoproxy.cpp +++ b/src/mir_app/src/netlib_autoproxy.cpp @@ -1,365 +1,365 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#include <wininet.h> - -///////////////////////////////////////////////////////////////////////////////////////// -// local module data - -static char *szProxyHost[3]; -static LIST<char> proxyBypass(5); - -static HMODULE hModJS; - -static pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll; -static pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll; -static pfnInternetGetProxyInfo pInternetGetProxyInfo; - -static bool bEnabled, bOneProxy; - -///////////////////////////////////////////////////////////////////////////////////////// - -static void GetFile(char *szUrl, AUTO_PROXY_SCRIPT_BUFFER &buf) -{ - NetlibUser nlu = {}; - nlu.handleType = NLH_USER; - nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS; - nlu.user.szSettingsModule = "(NULL)"; - nlu.toLog = 1; - - // initialize the netlib request - NETLIBHTTPREQUEST nlhr = {}; - nlhr.cbSize = sizeof(nlhr); - nlhr.requestType = REQUEST_GET; - nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT; - nlhr.szUrl = szUrl; - - // download the page - NLHR_PTR nlhrReply(Netlib_HttpTransaction(&nlu, &nlhr)); - if (nlhrReply) { - if (nlhrReply->resultCode == 200) { - buf.lpszScriptBuffer = nlhrReply->pData; - buf.dwScriptBufferSize = nlhrReply->dataLength + 1; - - nlhrReply->dataLength = 0; - nlhrReply->pData = nullptr; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps) -{ - bool noHttp = false; - bool usingSsl = false; - char szUrl[1024]; - - if ((nlc->url.flags & NLOCF_HTTP) && (nlc->url.flags & NLOCF_SSL) || nlc->url.port == 443 || forceHttps) { - mir_snprintf(szUrl, "https://%s", nlc->url.szHost.c_str()); - usingSsl = true; - } - else if ((nlc->url.flags & NLOCF_HTTP)) - mir_snprintf(szUrl, "http://%s", nlc->url.szHost.c_str()); - else { - strncpy_s(szUrl, nlc->url.szHost, _TRUNCATE); - noHttp = true; - } - - mir_free(nlc->szProxyServer); nlc->szProxyServer = nullptr; - nlc->wProxyPort = 0; - nlc->proxyType = 0; - - char *mt = NetlibGetIeProxy(szUrl); - char *m = NEWSTR_ALLOCA(mt); - mir_free(mt); - - if (m == nullptr) - return false; - - // if multiple servers, use the first one - char *c = strchr(m, ';'); if (c) *c = 0; - - // if 'direct' no proxy - if (_stricmp(lrtrim(m), "direct") == 0) - return false; - - // find proxy address - char *h = strchr(m, ' '); - if (h == nullptr) - return false; - - // find proxy port - *h = 0; ++h; - char *p = strchr(h, ':'); - if (p) { *p = 0; ++p; } - - lrtrim(h); ltrim(p); - if (_stricmp(m, "proxy") == 0 && h[0]) { - nlc->proxyType = (usingSsl || noHttp) ? PROXYTYPE_HTTPS : PROXYTYPE_HTTP; - nlc->wProxyPort = p ? atol(p) : 8080; - nlc->szProxyServer = mir_strdup(h); - } - else if (_stricmp(m, "socks") == 0 && h[0]) { - nlc->proxyType = PROXYTYPE_SOCKS4; - nlc->wProxyPort = p ? atol(p) : 1080; - nlc->szProxyServer = mir_strdup(h); - } - else if (_stricmp(m, "socks5") == 0 && h[0]) { - nlc->proxyType = PROXYTYPE_SOCKS5; - nlc->wProxyPort = p ? atol(p) : 1080; - nlc->szProxyServer = mir_strdup(h); - } - else return false; - - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static char szAutoUrlStr[MAX_PATH] = ""; -static AUTO_PROXY_SCRIPT_BUFFER abuf = { 0 }; -static HANDLE hIeProxyMutex; -static bool bAutoProxyInit; - -static void NetlibInitAutoProxy(void) -{ - if (bAutoProxyInit) return; - - if (!hModJS) { - if (!(hModJS = LoadLibraryA("jsproxy.dll"))) - return; - - pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetInitializeAutoProxyDll"); - pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll"); - pInternetGetProxyInfo = (pfnInternetGetProxyInfo)GetProcAddress(hModJS, "InternetGetProxyInfo"); - } - - if (strstr(szAutoUrlStr, "file://") == nullptr && strstr(szAutoUrlStr, "://") != nullptr) { - abuf.dwStructSize = sizeof(abuf); - GetFile(szAutoUrlStr, abuf); - } - bAutoProxyInit = true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct IeProxyParam -{ - char *szUrl; - char *szHost; - char *szProxy; -}; - -static void __cdecl NetlibIeProxyThread(IeProxyParam *param) -{ - param->szProxy = nullptr; - - if (!bAutoProxyInit) { - WaitForSingleObject(hIeProxyMutex, INFINITE); - NetlibInitAutoProxy(); - ReleaseMutex(hIeProxyMutex); - } - - BOOL res; - char *loc = strstr(szAutoUrlStr, "file://"); - if (loc || strstr(szAutoUrlStr, "://") == nullptr) { - Netlib_Logf(nullptr, "Autoproxy Init file: %s", loc); - loc = loc ? loc + 7 : szAutoUrlStr; - res = pInternetInitializeAutoProxyDll(0, loc, nullptr, nullptr /*&HelperFunctions*/, nullptr); - } - else { - Netlib_Logf(nullptr, "Autoproxy Init %d", abuf.dwScriptBufferSize); - if (abuf.dwScriptBufferSize) - res = pInternetInitializeAutoProxyDll(0, nullptr, nullptr, nullptr /*&HelperFunctions*/, &abuf); - else - res = false; - } - - if (res) { - char proxyBuffer[1024]; - char *proxy = proxyBuffer; - DWORD dwProxyLen = sizeof(proxyBuffer); - - if (pInternetGetProxyInfo(param->szUrl, (uint32_t)mir_strlen(param->szUrl), - param->szHost, (uint32_t)mir_strlen(param->szHost), &proxy, &dwProxyLen)) - param->szProxy = mir_strdup(lrtrim(proxy)); - - Netlib_Logf(nullptr, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost); - pInternetDeInitializeAutoProxyDll(nullptr, 0); - } - else Netlib_Logf(nullptr, "Autoproxy init failed"); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* NetlibGetIeProxy(char *szUrl) -{ - char *res = nullptr, *szHost; - { - char* p = strstr(szUrl, "://"); - if (p) p += 3; else p = szUrl; - - szHost = NEWSTR_ALLOCA(p); - p = strchr(szHost, '/'); if (p) *p = 0; - p = strchr(szHost, ':'); if (p) *p = 0; - _strlwr(szHost); - } - - if (bEnabled) { - for (auto &p : proxyBypass) { - if (mir_strcmp(p, "<local>") == 0) { - if (strchr(szHost, '.') == nullptr) - return nullptr; - } - else if (wildcmp(szHost, p)) - return nullptr; - } - - int ind = -1; - if (strstr(szUrl, "http://")) - ind = szProxyHost[0] ? 0 : 2; - else if (strstr(szUrl, "https://")) - ind = bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2); - else - ind = szProxyHost[2] ? 2 : (bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2)); - - if (ind < 0 || !szProxyHost[ind]) - return nullptr; - - size_t len = mir_strlen(szHost) + 20; - res = (char*)mir_alloc(len); - mir_snprintf(res, len, "%s %s", ind == 2 ? "SOCKS" : "PROXY", szProxyHost[ind]); - return res; - } - - if (szAutoUrlStr[0]) { - IeProxyParam param = { szUrl, szHost, nullptr }; - HANDLE hThread = mir_forkThread<IeProxyParam>(NetlibIeProxyThread, ¶m); - WaitForSingleObject(hThread, INFINITE); - res = param.szProxy; - } - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void NetlibLoadIeProxy(void) -{ - HKEY hSettings; - if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_QUERY_VALUE, &hSettings)) - return; - - DWORD tValueLen, enabled = 0; - char szHostStr[256] = "", szProxyBypassStr[4096] = ""; - - tValueLen = sizeof(enabled); - int tResult = RegQueryValueExA(hSettings, "ProxyEnable", nullptr, nullptr, (uint8_t*)&enabled, &tValueLen); - bEnabled = enabled && tResult == ERROR_SUCCESS; - - tValueLen = _countof(szHostStr); - tResult = RegQueryValueExA(hSettings, "ProxyServer", nullptr, nullptr, (uint8_t*)szHostStr, &tValueLen); - bEnabled = bEnabled && tResult == ERROR_SUCCESS; - - tValueLen = _countof(szAutoUrlStr); - RegQueryValueExA(hSettings, "AutoConfigUrl", nullptr, nullptr, (uint8_t*)szAutoUrlStr, &tValueLen); - - tValueLen = _countof(szProxyBypassStr); - RegQueryValueExA(hSettings, "ProxyOverride", nullptr, nullptr, (uint8_t*)szProxyBypassStr, &tValueLen); - - RegCloseKey(hSettings); - - if (bEnabled) { - char *szProxy = ltrim(szHostStr); - if (szProxy[0] == 0) { - enabled = false; - return; - } - - while (true) { - char *szProxyEnd = strchr(szProxy, ';'); - if (szProxyEnd) - *szProxyEnd = 0; - - int ind = -1; - if (strncmp(szProxy, "http=", 5) == 0) { ind = 0; szProxy += 5; } - else if (strncmp(szProxy, "https=", 6) == 0) { ind = 1; szProxy += 6; } - else if (strncmp(szProxy, "socks=", 6) == 0) { ind = 2; szProxy += 6; } - else if (strchr(szProxy, '=')) ind = -2; - - if (ind != -2) { - bOneProxy = ind < 0; if (ind < 0) ind = 0; - - lrtrim(szProxy); - - if (strchr(szProxy, ':')) - szProxyHost[ind] = mir_strdup(szProxy); - else { - size_t len = mir_strlen(szProxy) + 10; - szProxyHost[ind] = (char*)mir_alloc(len); - mir_snprintf(szProxyHost[ind], len, "%s:%u", szProxy, ind == 2 ? 1080 : 8080); - } - if (bOneProxy) - break; - } - if (szProxyEnd == nullptr) - break; - szProxy = szProxyEnd + 1; - } - - char *szProxyBypass = szProxyBypassStr; - while (true) { - char *szProxyBypassEnd = strchr(szProxyBypass, ';'); - if (szProxyBypassEnd) - *szProxyBypassEnd = 0; - - lrtrim(szProxyBypass); - - proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass))); - if (szProxyBypassEnd == nullptr) - break; - - szProxyBypass = szProxyBypassEnd + 1; - } - } - - if (bEnabled || szAutoUrlStr[0]) - hIeProxyMutex = CreateMutex(nullptr, FALSE, nullptr); -} - -void NetlibUnloadIeProxy(void) -{ - for (int i = 0; i < 3; i++) - mir_free(szProxyHost[i]); - - for (auto &p : proxyBypass) - mir_free(p); - - mir_free(abuf.lpszScriptBuffer); - - CloseHandle(hIeProxyMutex); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#include <wininet.h>
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// local module data
+
+static char *szProxyHost[3];
+static LIST<char> proxyBypass(5);
+
+static HMODULE hModJS;
+
+static pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll;
+static pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll;
+static pfnInternetGetProxyInfo pInternetGetProxyInfo;
+
+static bool bEnabled, bOneProxy;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void GetFile(char *szUrl, AUTO_PROXY_SCRIPT_BUFFER &buf)
+{
+ NetlibUser nlu = {};
+ nlu.handleType = NLH_USER;
+ nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS;
+ nlu.user.szSettingsModule = "(NULL)";
+ nlu.toLog = 1;
+
+ // initialize the netlib request
+ NETLIBHTTPREQUEST nlhr = {};
+ nlhr.cbSize = sizeof(nlhr);
+ nlhr.requestType = REQUEST_GET;
+ nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT;
+ nlhr.szUrl = szUrl;
+
+ // download the page
+ NLHR_PTR nlhrReply(Netlib_HttpTransaction(&nlu, &nlhr));
+ if (nlhrReply) {
+ if (nlhrReply->resultCode == 200) {
+ buf.lpszScriptBuffer = nlhrReply->pData;
+ buf.dwScriptBufferSize = nlhrReply->dataLength + 1;
+
+ nlhrReply->dataLength = 0;
+ nlhrReply->pData = nullptr;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps)
+{
+ bool noHttp = false;
+ bool usingSsl = false;
+ char szUrl[1024];
+
+ if ((nlc->url.flags & NLOCF_HTTP) && (nlc->url.flags & NLOCF_SSL) || nlc->url.port == 443 || forceHttps) {
+ mir_snprintf(szUrl, "https://%s", nlc->url.szHost.c_str());
+ usingSsl = true;
+ }
+ else if ((nlc->url.flags & NLOCF_HTTP))
+ mir_snprintf(szUrl, "http://%s", nlc->url.szHost.c_str());
+ else {
+ strncpy_s(szUrl, nlc->url.szHost, _TRUNCATE);
+ noHttp = true;
+ }
+
+ mir_free(nlc->szProxyServer); nlc->szProxyServer = nullptr;
+ nlc->wProxyPort = 0;
+ nlc->proxyType = 0;
+
+ char *mt = NetlibGetIeProxy(szUrl);
+ char *m = NEWSTR_ALLOCA(mt);
+ mir_free(mt);
+
+ if (m == nullptr)
+ return false;
+
+ // if multiple servers, use the first one
+ char *c = strchr(m, ';'); if (c) *c = 0;
+
+ // if 'direct' no proxy
+ if (_stricmp(lrtrim(m), "direct") == 0)
+ return false;
+
+ // find proxy address
+ char *h = strchr(m, ' ');
+ if (h == nullptr)
+ return false;
+
+ // find proxy port
+ *h = 0; ++h;
+ char *p = strchr(h, ':');
+ if (p) { *p = 0; ++p; }
+
+ lrtrim(h); ltrim(p);
+ if (_stricmp(m, "proxy") == 0 && h[0]) {
+ nlc->proxyType = (usingSsl || noHttp) ? PROXYTYPE_HTTPS : PROXYTYPE_HTTP;
+ nlc->wProxyPort = p ? atol(p) : 8080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else if (_stricmp(m, "socks") == 0 && h[0]) {
+ nlc->proxyType = PROXYTYPE_SOCKS4;
+ nlc->wProxyPort = p ? atol(p) : 1080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else if (_stricmp(m, "socks5") == 0 && h[0]) {
+ nlc->proxyType = PROXYTYPE_SOCKS5;
+ nlc->wProxyPort = p ? atol(p) : 1080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else return false;
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static char szAutoUrlStr[MAX_PATH] = "";
+static AUTO_PROXY_SCRIPT_BUFFER abuf = { 0 };
+static HANDLE hIeProxyMutex;
+static bool bAutoProxyInit;
+
+static void NetlibInitAutoProxy(void)
+{
+ if (bAutoProxyInit) return;
+
+ if (!hModJS) {
+ if (!(hModJS = LoadLibraryA("jsproxy.dll")))
+ return;
+
+ pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetInitializeAutoProxyDll");
+ pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll");
+ pInternetGetProxyInfo = (pfnInternetGetProxyInfo)GetProcAddress(hModJS, "InternetGetProxyInfo");
+ }
+
+ if (strstr(szAutoUrlStr, "file://") == nullptr && strstr(szAutoUrlStr, "://") != nullptr) {
+ abuf.dwStructSize = sizeof(abuf);
+ GetFile(szAutoUrlStr, abuf);
+ }
+ bAutoProxyInit = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct IeProxyParam
+{
+ char *szUrl;
+ char *szHost;
+ char *szProxy;
+};
+
+static void __cdecl NetlibIeProxyThread(IeProxyParam *param)
+{
+ param->szProxy = nullptr;
+
+ if (!bAutoProxyInit) {
+ WaitForSingleObject(hIeProxyMutex, INFINITE);
+ NetlibInitAutoProxy();
+ ReleaseMutex(hIeProxyMutex);
+ }
+
+ BOOL res;
+ char *loc = strstr(szAutoUrlStr, "file://");
+ if (loc || strstr(szAutoUrlStr, "://") == nullptr) {
+ Netlib_Logf(nullptr, "Autoproxy Init file: %s", loc);
+ loc = loc ? loc + 7 : szAutoUrlStr;
+ res = pInternetInitializeAutoProxyDll(0, loc, nullptr, nullptr /*&HelperFunctions*/, nullptr);
+ }
+ else {
+ Netlib_Logf(nullptr, "Autoproxy Init %d", abuf.dwScriptBufferSize);
+ if (abuf.dwScriptBufferSize)
+ res = pInternetInitializeAutoProxyDll(0, nullptr, nullptr, nullptr /*&HelperFunctions*/, &abuf);
+ else
+ res = false;
+ }
+
+ if (res) {
+ char proxyBuffer[1024];
+ char *proxy = proxyBuffer;
+ DWORD dwProxyLen = sizeof(proxyBuffer);
+
+ if (pInternetGetProxyInfo(param->szUrl, (uint32_t)mir_strlen(param->szUrl),
+ param->szHost, (uint32_t)mir_strlen(param->szHost), &proxy, &dwProxyLen))
+ param->szProxy = mir_strdup(lrtrim(proxy));
+
+ Netlib_Logf(nullptr, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost);
+ pInternetDeInitializeAutoProxyDll(nullptr, 0);
+ }
+ else Netlib_Logf(nullptr, "Autoproxy init failed");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* NetlibGetIeProxy(char *szUrl)
+{
+ char *res = nullptr, *szHost;
+ {
+ char* p = strstr(szUrl, "://");
+ if (p) p += 3; else p = szUrl;
+
+ szHost = NEWSTR_ALLOCA(p);
+ p = strchr(szHost, '/'); if (p) *p = 0;
+ p = strchr(szHost, ':'); if (p) *p = 0;
+ _strlwr(szHost);
+ }
+
+ if (bEnabled) {
+ for (auto &p : proxyBypass) {
+ if (mir_strcmp(p, "<local>") == 0) {
+ if (strchr(szHost, '.') == nullptr)
+ return nullptr;
+ }
+ else if (wildcmp(szHost, p))
+ return nullptr;
+ }
+
+ int ind = -1;
+ if (strstr(szUrl, "http://"))
+ ind = szProxyHost[0] ? 0 : 2;
+ else if (strstr(szUrl, "https://"))
+ ind = bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2);
+ else
+ ind = szProxyHost[2] ? 2 : (bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2));
+
+ if (ind < 0 || !szProxyHost[ind])
+ return nullptr;
+
+ size_t len = mir_strlen(szHost) + 20;
+ res = (char*)mir_alloc(len);
+ mir_snprintf(res, len, "%s %s", ind == 2 ? "SOCKS" : "PROXY", szProxyHost[ind]);
+ return res;
+ }
+
+ if (szAutoUrlStr[0]) {
+ IeProxyParam param = { szUrl, szHost, nullptr };
+ HANDLE hThread = mir_forkThread<IeProxyParam>(NetlibIeProxyThread, ¶m);
+ WaitForSingleObject(hThread, INFINITE);
+ res = param.szProxy;
+ }
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NetlibLoadIeProxy(void)
+{
+ HKEY hSettings;
+ if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_QUERY_VALUE, &hSettings))
+ return;
+
+ DWORD tValueLen, enabled = 0;
+ char szHostStr[256] = "", szProxyBypassStr[4096] = "";
+
+ tValueLen = sizeof(enabled);
+ int tResult = RegQueryValueExA(hSettings, "ProxyEnable", nullptr, nullptr, (uint8_t*)&enabled, &tValueLen);
+ bEnabled = enabled && tResult == ERROR_SUCCESS;
+
+ tValueLen = _countof(szHostStr);
+ tResult = RegQueryValueExA(hSettings, "ProxyServer", nullptr, nullptr, (uint8_t*)szHostStr, &tValueLen);
+ bEnabled = bEnabled && tResult == ERROR_SUCCESS;
+
+ tValueLen = _countof(szAutoUrlStr);
+ RegQueryValueExA(hSettings, "AutoConfigUrl", nullptr, nullptr, (uint8_t*)szAutoUrlStr, &tValueLen);
+
+ tValueLen = _countof(szProxyBypassStr);
+ RegQueryValueExA(hSettings, "ProxyOverride", nullptr, nullptr, (uint8_t*)szProxyBypassStr, &tValueLen);
+
+ RegCloseKey(hSettings);
+
+ if (bEnabled) {
+ char *szProxy = ltrim(szHostStr);
+ if (szProxy[0] == 0) {
+ enabled = false;
+ return;
+ }
+
+ while (true) {
+ char *szProxyEnd = strchr(szProxy, ';');
+ if (szProxyEnd)
+ *szProxyEnd = 0;
+
+ int ind = -1;
+ if (strncmp(szProxy, "http=", 5) == 0) { ind = 0; szProxy += 5; }
+ else if (strncmp(szProxy, "https=", 6) == 0) { ind = 1; szProxy += 6; }
+ else if (strncmp(szProxy, "socks=", 6) == 0) { ind = 2; szProxy += 6; }
+ else if (strchr(szProxy, '=')) ind = -2;
+
+ if (ind != -2) {
+ bOneProxy = ind < 0; if (ind < 0) ind = 0;
+
+ lrtrim(szProxy);
+
+ if (strchr(szProxy, ':'))
+ szProxyHost[ind] = mir_strdup(szProxy);
+ else {
+ size_t len = mir_strlen(szProxy) + 10;
+ szProxyHost[ind] = (char*)mir_alloc(len);
+ mir_snprintf(szProxyHost[ind], len, "%s:%u", szProxy, ind == 2 ? 1080 : 8080);
+ }
+ if (bOneProxy)
+ break;
+ }
+ if (szProxyEnd == nullptr)
+ break;
+ szProxy = szProxyEnd + 1;
+ }
+
+ char *szProxyBypass = szProxyBypassStr;
+ while (true) {
+ char *szProxyBypassEnd = strchr(szProxyBypass, ';');
+ if (szProxyBypassEnd)
+ *szProxyBypassEnd = 0;
+
+ lrtrim(szProxyBypass);
+
+ proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass)));
+ if (szProxyBypassEnd == nullptr)
+ break;
+
+ szProxyBypass = szProxyBypassEnd + 1;
+ }
+ }
+
+ if (bEnabled || szAutoUrlStr[0])
+ hIeProxyMutex = CreateMutex(nullptr, FALSE, nullptr);
+}
+
+void NetlibUnloadIeProxy(void)
+{
+ for (int i = 0; i < 3; i++)
+ mir_free(szProxyHost[i]);
+
+ for (auto &p : proxyBypass)
+ mir_free(p);
+
+ mir_free(abuf.lpszScriptBuffer);
+
+ CloseHandle(hIeProxyMutex);
+}
diff --git a/src/mir_app/src/netlib_bind.cpp b/src/mir_app/src/netlib_bind.cpp index 53aad20fa3..cd9c955a75 100644 --- a/src/mir_app/src/netlib_bind.cpp +++ b/src/mir_app/src/netlib_bind.cpp @@ -1,330 +1,330 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn) -{ - SOCKADDR_IN sin = {0}; - sin.sin_family = AF_INET; - - SOCKADDR_IN6 sin6 = {0}; - sin6.sin6_family = AF_INET6; - - mir_cslock lck(csNetlibUser); - - if (--*portn < 0 && (s != INVALID_SOCKET || s6 != INVALID_SOCKET)) { - BindSocketToPort(szPorts, INVALID_SOCKET, INVALID_SOCKET, portn); - if (*portn == 0) - return false; - - uint16_t num; - Utils_GetRandom(&num, sizeof(uint16_t)); - *portn = num % *portn; - } - - bool before = false; - while (true) { - const char *psz; - char *pszEnd; - int portMin, portMax, port, portnum = 0; - - for (psz = szPorts;*psz;) { - while (*psz == ' ' || *psz == ',') psz++; - portMin = strtol(psz, &pszEnd, 0); - if (pszEnd == psz) - break; - while (*pszEnd == ' ') - pszEnd++; - if (*pszEnd == '-') { - psz = pszEnd + 1; - portMax = strtol(psz, &pszEnd, 0); - if (pszEnd == psz) portMax = 65535; - if (portMin > portMax) { - port = portMin; - portMin = portMax; - portMax = port; - } - } - else portMax = portMin; - if (portMax >= 1) { - if (portMin <= 0) portMin = 1; - for (port = portMin; port <= portMax; port++) { - if (port > 65535) - break; - - ++portnum; - - if (s == INVALID_SOCKET) continue; - if (!before && portnum <= *portn) continue; - if (before && portnum >= *portn) - return false; - - sin.sin_port = htons((uint16_t)port); - bool bV4Mapped = s == INVALID_SOCKET || bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0; - - sin6.sin6_port = htons((uint16_t)port); - bool bV6Mapped = s6 == INVALID_SOCKET || bind(s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0; - - if (bV4Mapped && bV6Mapped) { - *portn = portnum + 1; - return true; - } - } - } - psz = pszEnd; - } - - if (*portn < 0) { - *portn = portnum; - return true; - } - - if (*portn >= portnum) - *portn = 0; - else - before = true; - } -} - -int NetlibFreeBoundPort(NetlibBoundPort *nlbp) -{ - NETLIBCONNECTIONEVENTINFO ncei; - - ZeroMemory(&ncei, sizeof(ncei)); - ncei.connected = 0; - ncei.listening = 1; - ncei.szSettingsModule = nlbp->nlu->user.szSettingsModule; - int size = sizeof(SOCKADDR_IN); - getsockname(nlbp->s, (SOCKADDR *)&ncei.local, &size); - NotifyFastHook(hEventDisconnected, (WPARAM)&ncei, 0); - - nlbp->close(); - if (nlbp->hThread) - WaitForSingleObject(nlbp->hThread, INFINITE); - Netlib_Logf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort); - delete nlbp; - return 1; -} - -static void __cdecl NetlibBindAcceptThread(NetlibBoundPort *nlbp) -{ - Netlib_Logf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort); - - while (true) { - fd_set r; - FD_ZERO(&r); - if (nlbp->s != INVALID_SOCKET) - FD_SET(nlbp->s, &r); - if (nlbp->s6 != INVALID_SOCKET) - FD_SET(nlbp->s6, &r); - if (select(0, &r, nullptr, nullptr, nullptr) == SOCKET_ERROR) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): select failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - - sockaddr_in sin; - int sinLen = sizeof(sin); - memset(&sin, 0, sizeof(sin)); - - SOCKET s; - if (FD_ISSET(nlbp->s, &r)) { - s = accept(nlbp->s, (sockaddr*)&sin, &sinLen); - if (s == INVALID_SOCKET) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V4 failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - } - else if (FD_ISSET(nlbp->s6, &r)) { - s = accept(nlbp->s6, (sockaddr*)&sin, &sinLen); - if (s == INVALID_SOCKET) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V6 failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - } - else s = 0; - - Netlib_Logf(nlbp->nlu, "New incoming connection on port %u from %s (%p)", nlbp->wPort, ptrA(Netlib_AddressToString(&sin)).get(), (void*)s); - - NetlibConnection *nlc = new NetlibConnection(); - nlc->nlu = nlbp->nlu; - nlc->s = s; - - if (nlbp->pfnNewConnection) - nlbp->pfnNewConnection(nlc, ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra); - } - - NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP"); - nlbp->hThread = nullptr; - - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread: (%p) thread for port %u closed", (void*)nlbp->s, nlbp->wPort); -} - -MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb) -{ - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) || nlb == nullptr || nlb->pfnNewConnection == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - NetlibBoundPort *nlbp = new NetlibBoundPort(nlu, nlb); - if (nlbp->s == INVALID_SOCKET && nlbp->s6 == INVALID_SOCKET) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "socket", WSAGetLastError()); -LBL_Error: - delete nlbp; - return nullptr; - } - - SOCKADDR_IN sin = { 0 }; - sin.sin_family = AF_INET; - - SOCKADDR_IN6 sin6 = { 0 }; - sin6.sin6_family = AF_INET6; - - /* if the netlib user wanted a free port given in the range, then - they better have given wPort == 0, let's hope so */ - int foundPort = 0; - if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0) { - if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, nlbp->s6, &nlu->outportnum)) { - Netlib_Logf(nlu, "Netlib bind: Not enough ports for incoming connections specified"); - SetLastError(WSAEADDRINUSE); - } - else foundPort = 1; - } - else { - /* if ->wPort == 0 then they'll get any free port, otherwise they'll - be asking for whatever was in nlb->wPort*/ - if (nlb->wPort != 0) { - Netlib_Logf(nlu, "%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.", __FILE__, __LINE__, nlb->wPort); - sin.sin_port = htons(nlb->wPort); - sin6.sin6_port = htons(nlb->wPort); - } - - if (nlbp->s != INVALID_SOCKET) - if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0) { - SOCKADDR_IN sin2 = { 0 }; - int len = sizeof(sin2); - if (!getsockname(nlbp->s, (PSOCKADDR)&sin2, &len)) - sin6.sin6_port = sin2.sin_port; - foundPort = 1; - } - - if (nlbp->s6 != INVALID_SOCKET) - if (bind(nlbp->s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0) - foundPort = 1; - } - if (!foundPort) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "bind", WSAGetLastError()); - goto LBL_Error; - } - - if (nlbp->s != INVALID_SOCKET && listen(nlbp->s, 5)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); - goto LBL_Error; - } - - if (nlbp->s6 != INVALID_SOCKET && listen(nlbp->s6, 5)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); - goto LBL_Error; - } - - SOCKADDR_INET_M sinm = { 0 }; - int len = sizeof(sinm); - if (!getsockname(nlbp->s, (PSOCKADDR)&sinm, &len)) { - nlb->wPort = ntohs(sinm.Ipv4.sin_port); - nlb->dwInternalIP = ntohl(sinm.Ipv4.sin_addr.S_un.S_addr); - } - else if (!getsockname(nlbp->s6, (PSOCKADDR)&sinm, &len)) - nlb->wPort = ntohs(sinm.Ipv6.sin6_port); - else { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "getsockname", WSAGetLastError()); - goto LBL_Error; - } - nlbp->wPort = nlb->wPort; - - if (nlb->dwInternalIP == 0) { - char hostname[64] = ""; - gethostname(hostname, _countof(hostname)); - - PHOSTENT he = gethostbyname(hostname); - if (he && he->h_addr) - nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr); - } - - uint32_t extIP; - if (nlu->settings.enableUPnP && NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, true)) { - Netlib_Logf(nullptr, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n", nlb->wPort, nlbp->wExPort); - nlb->wExPort = nlbp->wExPort; - nlb->dwExternalIP = extIP; - } - else { - if (nlu->settings.enableUPnP) - Netlib_Logf(nullptr, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort); - else - Netlib_Logf(nullptr, "UPnP disabled. Internal Port: %u\n", nlb->wPort); - - nlbp->wExPort = 0; - nlb->wExPort = nlb->wPort; - nlb->dwExternalIP = nlb->dwInternalIP; - } - - nlbp->hThread = mir_forkThread<NetlibBoundPort>(NetlibBindAcceptThread, nlbp); - - if (GetSubscribersCount((THook*)hEventConnected)) { - NETLIBCONNECTIONEVENTINFO ncei = {}; - ncei.connected = 1; - ncei.listening = 1; - ncei.szSettingsModule = nlu->user.szSettingsModule; - memcpy(&ncei.local, &sin, sizeof(sin)); - NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); - } - - return nlbp; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -NetlibBoundPort::NetlibBoundPort(HNETLIBUSER _nlu, NETLIBBIND *nlb) - : handleType(NLH_BOUNDPORT), - nlu(_nlu) -{ - pfnNewConnection = nlb->pfnNewConnection; - pExtra = nlb->pExtra; - - s = socket(PF_INET, SOCK_STREAM, 0); - s6 = socket(PF_INET6, SOCK_STREAM, 0); -} - -void NetlibBoundPort::close() -{ - if (s != INVALID_SOCKET) { - closesocket(s); - s = INVALID_SOCKET; - } - if (s6 != INVALID_SOCKET) { - closesocket(s6); - s6 = INVALID_SOCKET; - } -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn)
+{
+ SOCKADDR_IN sin = {0};
+ sin.sin_family = AF_INET;
+
+ SOCKADDR_IN6 sin6 = {0};
+ sin6.sin6_family = AF_INET6;
+
+ mir_cslock lck(csNetlibUser);
+
+ if (--*portn < 0 && (s != INVALID_SOCKET || s6 != INVALID_SOCKET)) {
+ BindSocketToPort(szPorts, INVALID_SOCKET, INVALID_SOCKET, portn);
+ if (*portn == 0)
+ return false;
+
+ uint16_t num;
+ Utils_GetRandom(&num, sizeof(uint16_t));
+ *portn = num % *portn;
+ }
+
+ bool before = false;
+ while (true) {
+ const char *psz;
+ char *pszEnd;
+ int portMin, portMax, port, portnum = 0;
+
+ for (psz = szPorts;*psz;) {
+ while (*psz == ' ' || *psz == ',') psz++;
+ portMin = strtol(psz, &pszEnd, 0);
+ if (pszEnd == psz)
+ break;
+ while (*pszEnd == ' ')
+ pszEnd++;
+ if (*pszEnd == '-') {
+ psz = pszEnd + 1;
+ portMax = strtol(psz, &pszEnd, 0);
+ if (pszEnd == psz) portMax = 65535;
+ if (portMin > portMax) {
+ port = portMin;
+ portMin = portMax;
+ portMax = port;
+ }
+ }
+ else portMax = portMin;
+ if (portMax >= 1) {
+ if (portMin <= 0) portMin = 1;
+ for (port = portMin; port <= portMax; port++) {
+ if (port > 65535)
+ break;
+
+ ++portnum;
+
+ if (s == INVALID_SOCKET) continue;
+ if (!before && portnum <= *portn) continue;
+ if (before && portnum >= *portn)
+ return false;
+
+ sin.sin_port = htons((uint16_t)port);
+ bool bV4Mapped = s == INVALID_SOCKET || bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0;
+
+ sin6.sin6_port = htons((uint16_t)port);
+ bool bV6Mapped = s6 == INVALID_SOCKET || bind(s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0;
+
+ if (bV4Mapped && bV6Mapped) {
+ *portn = portnum + 1;
+ return true;
+ }
+ }
+ }
+ psz = pszEnd;
+ }
+
+ if (*portn < 0) {
+ *portn = portnum;
+ return true;
+ }
+
+ if (*portn >= portnum)
+ *portn = 0;
+ else
+ before = true;
+ }
+}
+
+int NetlibFreeBoundPort(NetlibBoundPort *nlbp)
+{
+ NETLIBCONNECTIONEVENTINFO ncei;
+
+ ZeroMemory(&ncei, sizeof(ncei));
+ ncei.connected = 0;
+ ncei.listening = 1;
+ ncei.szSettingsModule = nlbp->nlu->user.szSettingsModule;
+ int size = sizeof(SOCKADDR_IN);
+ getsockname(nlbp->s, (SOCKADDR *)&ncei.local, &size);
+ NotifyFastHook(hEventDisconnected, (WPARAM)&ncei, 0);
+
+ nlbp->close();
+ if (nlbp->hThread)
+ WaitForSingleObject(nlbp->hThread, INFINITE);
+ Netlib_Logf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort);
+ delete nlbp;
+ return 1;
+}
+
+static void __cdecl NetlibBindAcceptThread(NetlibBoundPort *nlbp)
+{
+ Netlib_Logf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort);
+
+ while (true) {
+ fd_set r;
+ FD_ZERO(&r);
+ if (nlbp->s != INVALID_SOCKET)
+ FD_SET(nlbp->s, &r);
+ if (nlbp->s6 != INVALID_SOCKET)
+ FD_SET(nlbp->s6, &r);
+ if (select(0, &r, nullptr, nullptr, nullptr) == SOCKET_ERROR) {
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): select failed (%d)", (void*)nlbp->s, GetLastError());
+ break;
+ }
+
+ sockaddr_in sin;
+ int sinLen = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+
+ SOCKET s;
+ if (FD_ISSET(nlbp->s, &r)) {
+ s = accept(nlbp->s, (sockaddr*)&sin, &sinLen);
+ if (s == INVALID_SOCKET) {
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V4 failed (%d)", (void*)nlbp->s, GetLastError());
+ break;
+ }
+ }
+ else if (FD_ISSET(nlbp->s6, &r)) {
+ s = accept(nlbp->s6, (sockaddr*)&sin, &sinLen);
+ if (s == INVALID_SOCKET) {
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V6 failed (%d)", (void*)nlbp->s, GetLastError());
+ break;
+ }
+ }
+ else s = 0;
+
+ Netlib_Logf(nlbp->nlu, "New incoming connection on port %u from %s (%p)", nlbp->wPort, ptrA(Netlib_AddressToString(&sin)).get(), (void*)s);
+
+ NetlibConnection *nlc = new NetlibConnection();
+ nlc->nlu = nlbp->nlu;
+ nlc->s = s;
+
+ if (nlbp->pfnNewConnection)
+ nlbp->pfnNewConnection(nlc, ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra);
+ }
+
+ NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP");
+ nlbp->hThread = nullptr;
+
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread: (%p) thread for port %u closed", (void*)nlbp->s, nlbp->wPort);
+}
+
+MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb)
+{
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) || nlb == nullptr || nlb->pfnNewConnection == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return nullptr;
+ }
+
+ NetlibBoundPort *nlbp = new NetlibBoundPort(nlu, nlb);
+ if (nlbp->s == INVALID_SOCKET && nlbp->s6 == INVALID_SOCKET) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "socket", WSAGetLastError());
+LBL_Error:
+ delete nlbp;
+ return nullptr;
+ }
+
+ SOCKADDR_IN sin = { 0 };
+ sin.sin_family = AF_INET;
+
+ SOCKADDR_IN6 sin6 = { 0 };
+ sin6.sin6_family = AF_INET6;
+
+ /* if the netlib user wanted a free port given in the range, then
+ they better have given wPort == 0, let's hope so */
+ int foundPort = 0;
+ if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0) {
+ if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, nlbp->s6, &nlu->outportnum)) {
+ Netlib_Logf(nlu, "Netlib bind: Not enough ports for incoming connections specified");
+ SetLastError(WSAEADDRINUSE);
+ }
+ else foundPort = 1;
+ }
+ else {
+ /* if ->wPort == 0 then they'll get any free port, otherwise they'll
+ be asking for whatever was in nlb->wPort*/
+ if (nlb->wPort != 0) {
+ Netlib_Logf(nlu, "%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.", __FILE__, __LINE__, nlb->wPort);
+ sin.sin_port = htons(nlb->wPort);
+ sin6.sin6_port = htons(nlb->wPort);
+ }
+
+ if (nlbp->s != INVALID_SOCKET)
+ if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0) {
+ SOCKADDR_IN sin2 = { 0 };
+ int len = sizeof(sin2);
+ if (!getsockname(nlbp->s, (PSOCKADDR)&sin2, &len))
+ sin6.sin6_port = sin2.sin_port;
+ foundPort = 1;
+ }
+
+ if (nlbp->s6 != INVALID_SOCKET)
+ if (bind(nlbp->s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0)
+ foundPort = 1;
+ }
+ if (!foundPort) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "bind", WSAGetLastError());
+ goto LBL_Error;
+ }
+
+ if (nlbp->s != INVALID_SOCKET && listen(nlbp->s, 5)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError());
+ goto LBL_Error;
+ }
+
+ if (nlbp->s6 != INVALID_SOCKET && listen(nlbp->s6, 5)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError());
+ goto LBL_Error;
+ }
+
+ SOCKADDR_INET_M sinm = { 0 };
+ int len = sizeof(sinm);
+ if (!getsockname(nlbp->s, (PSOCKADDR)&sinm, &len)) {
+ nlb->wPort = ntohs(sinm.Ipv4.sin_port);
+ nlb->dwInternalIP = ntohl(sinm.Ipv4.sin_addr.S_un.S_addr);
+ }
+ else if (!getsockname(nlbp->s6, (PSOCKADDR)&sinm, &len))
+ nlb->wPort = ntohs(sinm.Ipv6.sin6_port);
+ else {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "getsockname", WSAGetLastError());
+ goto LBL_Error;
+ }
+ nlbp->wPort = nlb->wPort;
+
+ if (nlb->dwInternalIP == 0) {
+ char hostname[64] = "";
+ gethostname(hostname, _countof(hostname));
+
+ PHOSTENT he = gethostbyname(hostname);
+ if (he && he->h_addr)
+ nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr);
+ }
+
+ uint32_t extIP;
+ if (nlu->settings.enableUPnP && NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, true)) {
+ Netlib_Logf(nullptr, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n", nlb->wPort, nlbp->wExPort);
+ nlb->wExPort = nlbp->wExPort;
+ nlb->dwExternalIP = extIP;
+ }
+ else {
+ if (nlu->settings.enableUPnP)
+ Netlib_Logf(nullptr, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort);
+ else
+ Netlib_Logf(nullptr, "UPnP disabled. Internal Port: %u\n", nlb->wPort);
+
+ nlbp->wExPort = 0;
+ nlb->wExPort = nlb->wPort;
+ nlb->dwExternalIP = nlb->dwInternalIP;
+ }
+
+ nlbp->hThread = mir_forkThread<NetlibBoundPort>(NetlibBindAcceptThread, nlbp);
+
+ if (GetSubscribersCount((THook*)hEventConnected)) {
+ NETLIBCONNECTIONEVENTINFO ncei = {};
+ ncei.connected = 1;
+ ncei.listening = 1;
+ ncei.szSettingsModule = nlu->user.szSettingsModule;
+ memcpy(&ncei.local, &sin, sizeof(sin));
+ NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0);
+ }
+
+ return nlbp;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+NetlibBoundPort::NetlibBoundPort(HNETLIBUSER _nlu, NETLIBBIND *nlb)
+ : handleType(NLH_BOUNDPORT),
+ nlu(_nlu)
+{
+ pfnNewConnection = nlb->pfnNewConnection;
+ pExtra = nlb->pExtra;
+
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ s6 = socket(PF_INET6, SOCK_STREAM, 0);
+}
+
+void NetlibBoundPort::close()
+{
+ if (s != INVALID_SOCKET) {
+ closesocket(s);
+ s = INVALID_SOCKET;
+ }
+ if (s6 != INVALID_SOCKET) {
+ closesocket(s6);
+ s6 = INVALID_SOCKET;
+ }
+}
diff --git a/src/mir_app/src/netlib_http.cpp b/src/mir_app/src/netlib_http.cpp index 7c94db57ca..260d3a6199 100644 --- a/src/mir_app/src/netlib_http.cpp +++ b/src/mir_app/src/netlib_http.cpp @@ -1,1150 +1,1150 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "../libs/zlib/src/zlib.h" -#include "netlib.h" - -#define HTTPRECVHEADERSTIMEOUT 30000 //in ms -#define HTTPRECVDATATIMEOUT 20000 - -struct ProxyAuth -{ - char *szServer; - char *szMethod; - - ProxyAuth(const char *pszServer, const char *pszMethod) - { - szServer = mir_strdup(pszServer); - szMethod = mir_strdup(pszMethod); - } - ~ProxyAuth() - { - mir_free(szServer); - mir_free(szMethod); - } - static int Compare(const ProxyAuth *p1, const ProxyAuth *p2) - { - return mir_strcmpi(p1->szServer, p2->szServer); - } -}; - -struct ProxyAuthList : OBJLIST<ProxyAuth> -{ - ProxyAuthList() : OBJLIST<ProxyAuth>(2, ProxyAuth::Compare) {} - - void add(const char *szServer, const char *szMethod) - { - if (szServer == nullptr) return; - int i = getIndex((ProxyAuth*)&szServer); - if (i >= 0) { - ProxyAuth &rec = (*this)[i]; - if (szMethod == nullptr) - remove(i); - else if (_stricmp(rec.szMethod, szMethod)) { - mir_free(rec.szMethod); - rec.szMethod = mir_strdup(szMethod); - } - } - else insert(new ProxyAuth(szServer, szMethod)); - } - - const char* find(const char *szServer) - { - ProxyAuth *rec = szServer ? OBJLIST<ProxyAuth>::find((ProxyAuth*)&szServer) : nullptr; - return rec ? rec->szMethod : nullptr; - } -}; - -ProxyAuthList proxyAuthList; - -static int RecvWithTimeoutTime(NetlibConnection *nlc, int dwTimeoutTime, char *buf, int len, int flags) -{ - int dwTimeNow; - - if (nlc->foreBuf.isEmpty() && !Netlib_SslPending(nlc->hSsl)) { - while ((dwTimeNow = GetTickCount()) < dwTimeoutTime) { - int dwDeltaTime = min(dwTimeoutTime - dwTimeNow, 1000); - int res = WaitUntilReadable(nlc->s, dwDeltaTime); - - switch (res) { - case SOCKET_ERROR: - return SOCKET_ERROR; - - case 1: - return Netlib_Recv(nlc, buf, len, flags); - } - - if (nlc->termRequested || Miranda_IsTerminated()) - return 0; - } - SetLastError(ERROR_TIMEOUT); - return SOCKET_ERROR; - } - return Netlib_Recv(nlc, buf, len, flags); -} - -MIR_APP_DLL(char *) Netlib_GetHeader(const NETLIBHTTPREQUEST *nlhr, const char *hdr) -{ - if (nlhr == nullptr || hdr == nullptr) - return nullptr; - - for (int i=0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->headers[i]; - if (_stricmp(p.szName, hdr) == 0) - return p.szValue; - } - - return nullptr; -} - -static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider) -{ - char *szBasicHdr = nullptr; - char *szNegoHdr = nullptr; - char *szNtlmHdr = nullptr; - - for (int i=0; i < nlhrReply->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhrReply->headers[i]; - if (_stricmp(p.szName, hdr) == 0) { - if (_strnicmp(p.szValue, "Negotiate", 9) == 0) - szNegoHdr = p.szValue; - else if (_strnicmp(p.szValue, "NTLM", 4) == 0) - szNtlmHdr = p.szValue; - else if (_strnicmp(p.szValue, "Basic", 5) == 0) - szBasicHdr = p.szValue; - } - } - - if (szNegoHdr && (!szProvider || !_stricmp(szProvider, "Negotiate"))) return szNegoHdr; - if (szNtlmHdr && (!szProvider || !_stricmp(szProvider, "NTLM"))) return szNtlmHdr; - if (!szProvider || !_stricmp(szProvider, "Basic")) return szBasicHdr; - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void NetlibConnFromUrl(const char *szUrl, bool secur, NetlibUrl &url) -{ - secur = secur || _strnicmp(szUrl, "https", 5) == 0; - - const char* phost = strstr(szUrl, "://"); - url.szHost = phost ? phost + 3 : szUrl; - - int idx = url.szHost.Find('/'); - if (idx != -1) - url.szHost.Truncate(idx); - - if ((idx = url.szHost.Find(':')) != -1) { - url.port = strtol(url.szHost.c_str() + idx + 1, nullptr, 10); - url.szHost.Truncate(idx); - } - else url.port = secur ? 443 : 80; - url.flags = (secur ? NLOCF_SSL : 0); -} - -static NetlibConnection* NetlibHttpProcessUrl(NETLIBHTTPREQUEST *nlhr, NetlibUser *nlu, NetlibConnection *nlc, const char *szUrl = nullptr) -{ - NetlibUrl url; - - if (szUrl == nullptr) - NetlibConnFromUrl(nlhr->szUrl, (nlhr->flags & NLHRF_SSL) != 0, url); - else - NetlibConnFromUrl(szUrl, false, url); - - url.flags |= NLOCF_HTTP; - if (url.flags & NLOCF_SSL) - nlhr->flags |= NLHRF_SSL; - else - nlhr->flags &= ~NLHRF_SSL; - - if (nlc != nullptr) { - bool httpProxy = !(url.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP; - bool sameHost = mir_strcmp(nlc->url.szHost, url.szHost) == 0 && nlc->url.port == url.port; - - if (!httpProxy && !sameHost) { - NetlibDoCloseSocket(nlc); - - nlc->url = url; - return NetlibDoConnect(nlc) ? nlc : nullptr; - } - } - else nlc = (NetlibConnection*)Netlib_OpenConnection(nlu, url.szHost, url.port, 0, url.flags); - - return nlc; -} - -struct HttpSecurityContext -{ - HANDLE m_hNtlmSecurity; - char *m_szHost; - char *m_szProvider; - - HttpSecurityContext() - { - m_hNtlmSecurity = nullptr; m_szHost = nullptr; m_szProvider = nullptr; - } - - ~HttpSecurityContext() { Destroy(); } - - void Destroy(void) - { - if (!m_hNtlmSecurity) return; - - Netlib_DestroySecurityProvider(m_hNtlmSecurity); - m_hNtlmSecurity = nullptr; - mir_free(m_szHost); m_szHost = nullptr; - mir_free(m_szProvider); m_szProvider = nullptr; - } - - bool TryBasic(void) - { - return m_hNtlmSecurity && m_szProvider && _stricmp(m_szProvider, "Basic"); - } - - char* Execute(NetlibConnection *nlc, char *szHost, const char *szProvider, const char *szChallenge, unsigned &complete) - { - char *szAuthHdr = nullptr; - bool justCreated = false; - NetlibUser *nlu = nlc->nlu; - - if (m_hNtlmSecurity) { - bool newAuth = !m_szProvider || !szProvider || _stricmp(m_szProvider, szProvider); - newAuth = newAuth || (m_szHost != szHost && (!m_szHost || !szHost || _stricmp(m_szHost, szHost))); - if (newAuth) - Destroy(); - } - - if (m_hNtlmSecurity == nullptr) { - CMStringA szSpnStr; - if (szHost && _stricmp(szProvider, "Basic")) { - unsigned long ip = inet_addr(szHost); - PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET); - szSpnStr.Format("HTTP/%s", host && host->h_name ? host->h_name : szHost); - _strlwr(szSpnStr.GetBuffer() + 5); - Netlib_Logf(nlu, "Host SPN: %s", szSpnStr.c_str()); - } - m_hNtlmSecurity = Netlib_InitSecurityProvider(_A2T(szProvider), szSpnStr.IsEmpty() ? nullptr : _A2T(szSpnStr.c_str())); - if (m_hNtlmSecurity) { - m_szProvider = mir_strdup(szProvider); - m_szHost = mir_strdup(szHost); - justCreated = true; - } - } - - if (m_hNtlmSecurity) { - ptrW szLogin, szPassw; - - if (nlu->settings.useProxyAuth) { - mir_cslock lck(csNetlibUser); - szLogin = mir_a2u(nlu->settings.szProxyAuthUser); - szPassw = mir_a2u(nlu->settings.szProxyAuthPassword); - } - - szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity, szChallenge, szLogin, szPassw, true, complete); - if (!szAuthHdr) - Netlib_Logf(nullptr, "Security login %s failed, user: %S pssw: %S", szProvider, szLogin ? szLogin.get() : L"(no user)", szPassw ? L"(exist)" : L"(no psw)"); - else if (justCreated) - proxyAuthList.add(m_szHost, m_szProvider); - } - else complete = 1; - - return szAuthHdr; - } -}; - -static int HttpPeekFirstResponseLine(NetlibConnection *nlc, uint32_t dwTimeoutTime, uint32_t recvFlags, int *resultCode, char **ppszResultDescr, int *length) -{ - int bytesPeeked; - char buffer[2048], *peol; - - while (true) { - bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, _countof(buffer) - 1, MSG_PEEK | recvFlags); - if (bytesPeeked == 0) { - SetLastError(ERROR_HANDLE_EOF); - return 0; - } - if (bytesPeeked == SOCKET_ERROR) - return 0; - - buffer[bytesPeeked] = '\0'; - if ((peol = strchr(buffer, '\n')) != nullptr) - break; - - if ((int)mir_strlen(buffer) < bytesPeeked) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - if (bytesPeeked == _countof(buffer) - 1) { - SetLastError(ERROR_BUFFER_OVERFLOW); - return 0; - } - if (Miranda_IsTerminated()) - return 0; - Sleep(10); - } - - if (peol == buffer) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - - *peol = '\0'; - - if (_strnicmp(buffer, "HTTP/", 5)) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - - size_t off = strcspn(buffer, " \t"); - if (off >= (unsigned)bytesPeeked) - return 0; - - char *pResultCode = buffer + off; - *(pResultCode++) = 0; - - char *pResultDescr; - *resultCode = strtol(pResultCode, &pResultDescr, 10); - - if (ppszResultDescr) - *ppszResultDescr = mir_strdup(lrtrimp(pResultDescr)); - - if (length) - *length = peol - buffer + 1; - return 1; -} - -static int SendHttpRequestAndData(NetlibConnection *nlc, CMStringA &httpRequest, NETLIBHTTPREQUEST *nlhr, int sendContentLengthHeader) -{ - bool sendData = (nlhr->requestType == REQUEST_POST || nlhr->requestType == REQUEST_PUT || nlhr->requestType == REQUEST_PATCH); - - if (sendContentLengthHeader && sendData) - httpRequest.AppendFormat("Content-Length: %d\r\n\r\n", nlhr->dataLength); - else - httpRequest.AppendFormat("\r\n"); - - uint32_t hflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND | NLHRF_NODUMPHEADERS) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - int bytesSent = Netlib_Send(nlc, httpRequest, httpRequest.GetLength(), hflags); - if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength) { - uint32_t sflags = MSG_NOTITLE | (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - int sendResult = Netlib_Send(nlc, nlhr->pData, nlhr->dataLength, sflags); - - bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR; - } - - return bytesSent; -} - -MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN nlc, NETLIBHTTPREQUEST *nlhr) -{ - NETLIBHTTPREQUEST *nlhrReply = nullptr; - HttpSecurityContext httpSecurity; - - char *szHost = nullptr, *szNewUrl = nullptr; - char *pszProxyAuthHdr = nullptr, *pszAuthHdr = nullptr; - int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader; - int bytesSent = 0; - bool lastFirstLineFail = false; - - if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl == nullptr || nlhr->szUrl[0] == '\0') { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - NetlibUser *nlu = nlc->nlu; - if (GetNetlibHandleType(nlu) != NLH_USER) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - int hdrTimeout = (nlhr->timeout) ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT; - - const char *pszRequest; - switch (nlhr->requestType) { - case REQUEST_GET: pszRequest = "GET"; break; - case REQUEST_POST: pszRequest = "POST"; break; - case REQUEST_CONNECT: pszRequest = "CONNECT"; break; - case REQUEST_HEAD: pszRequest = "HEAD"; break; - case REQUEST_PUT: pszRequest = "PUT"; break; - case REQUEST_DELETE: pszRequest = "DELETE"; break; - case REQUEST_PATCH: pszRequest = "PATCH"; break; - default: - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) - return SOCKET_ERROR; - - const char *pszFullUrl = nlhr->szUrl; - const char *pszUrl = nullptr; - - unsigned complete = false; - int count = 11; - while (--count) { - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { - nlc = nullptr; - bytesSent = SOCKET_ERROR; - break; - } - - if (!NetlibReconnect(nlc)) { - bytesSent = SOCKET_ERROR; - break; - } - - if (!pszUrl) { - pszUrl = pszFullUrl; - if (!(nlhr->flags & NLHRF_MANUALHOST)) { - bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL); - - const char *ppath, *phost; - phost = strstr(pszUrl, "://"); - if (phost == nullptr) phost = pszUrl; - else phost += 3; - ppath = strchr(phost, '/'); - if (ppath == phost) - phost = nullptr; - - replaceStr(szHost, phost); - if (ppath && phost) - szHost[ppath - phost] = 0; - - if ((nlhr->flags & NLHRF_SMARTREMOVEHOST) && !usingProxy) - pszUrl = ppath ? ppath : "/"; - - if (usingProxy && phost && !nlc->dnsThroughProxy) { - char *tszHost = mir_strdup(phost); - if (ppath) - tszHost[ppath - phost] = 0; - char *cln = strchr(tszHost, ':'); if (cln) *cln = 0; - - if (inet_addr(tszHost) == INADDR_NONE) { - in_addr ip; - if (ip.S_un.S_addr = DnsLookup(nlu, tszHost)) { - mir_free(szHost); - if (cln) *cln = ':'; - szHost = CMStringA(FORMAT, "%s%s", inet_ntoa(ip), cln ? cln : "").Detach(); - } - } - mir_free(tszHost); - } - } - } - - if (nlc->proxyAuthNeeded && proxyAuthList.getCount()) { - if (httpSecurity.m_szProvider == nullptr && nlc->szProxyServer) { - const char *szAuthMethodNlu = proxyAuthList.find(nlc->szProxyServer); - if (szAuthMethodNlu) { - mir_free(pszProxyAuthHdr); - pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthMethodNlu, "", complete); - } - } - } - nlc->proxyAuthNeeded = false; - - CMStringA httpRequest(FORMAT, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0); - - // HTTP headers - doneHostHeader = doneContentLengthHeader = doneProxyAuthHeader = doneAuthHeader = 0; - for (i = 0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->headers[i]; - if (!mir_strcmpi(p.szName, "Host")) doneHostHeader = 1; - else if (!mir_strcmpi(p.szName, "Content-Length")) doneContentLengthHeader = 1; - else if (!mir_strcmpi(p.szName, "Proxy-Authorization")) doneProxyAuthHeader = 1; - else if (!mir_strcmpi(p.szName, "Authorization")) doneAuthHeader = 1; - else if (!mir_strcmpi(p.szName, "Connection")) continue; - if (p.szValue == nullptr) continue; - httpRequest.AppendFormat("%s: %s\r\n", p.szName, p.szValue); - } - if (szHost && !doneHostHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Host", szHost); - if (pszProxyAuthHdr && !doneProxyAuthHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr); - if (pszAuthHdr && !doneAuthHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Authorization", pszAuthHdr); - httpRequest.AppendFormat("%s: %s\r\n", "Connection", "Keep-Alive"); - httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Connection", "Keep-Alive"); - - // Add Sticky Headers - if (nlu->szStickyHeaders != nullptr) - httpRequest.AppendFormat("%s\r\n", nlu->szStickyHeaders); - - // send it - bytesSent = SendHttpRequestAndData(nlc, httpRequest, nlhr, !doneContentLengthHeader); - if (bytesSent == SOCKET_ERROR) - break; - - // ntlm reply - if (doneContentLengthHeader && nlhr->requestType != REQUEST_HEAD) - break; - - uint32_t fflags = MSG_PEEK | MSG_NODUMP | ((nlhr->flags & NLHRF_NOPROXY) ? MSG_RAW : 0); - uint32_t dwTimeOutTime = hdrTimeout < 0 ? -1 : GetTickCount() + hdrTimeout; - if (!HttpPeekFirstResponseLine(nlc, dwTimeOutTime, fflags, &nlhr->resultCode, nullptr, nullptr)) { - uint32_t err = GetLastError(); - Netlib_Logf(nlu, "%s %d: %s Failed (%u %u)", __FILE__, __LINE__, "HttpPeekFirstResponseLine", err, count); - - // connection died while we were waiting - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { - nlc = nullptr; - break; - } - - if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW || lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT) { - bytesSent = SOCKET_ERROR; - break; - } - - lastFirstLineFail = true; - continue; - } - - int resultCode = nlhr->resultCode; - lastFirstLineFail = false; - - uint32_t hflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPHEADERS | NLHRF_NODUMPSEND) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - uint32_t dflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? MSG_NODUMP : MSG_DUMPASTEXT | MSG_DUMPPROXY) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0) | MSG_NODUMP; - - if (resultCode == 100) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - - else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) && (nlhr->flags & NLHRF_REDIRECT))) { // redirect - pszUrl = nullptr; - - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - if (nlhrReply) { - auto *tmpUrl = Netlib_GetHeader(nlhrReply, "Location"); - if (tmpUrl) { - size_t rlen = 0; - if (tmpUrl[0] == '/') { - const char *ppath, *phost; - phost = strstr(pszFullUrl, "://"); - phost = phost ? phost + 3 : pszFullUrl; - ppath = strchr(phost, '/'); - rlen = ppath ? ppath - pszFullUrl : mir_strlen(pszFullUrl); - } - - nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + mir_strlen(tmpUrl) * 3 + 1); - - strncpy(nlc->szNewUrl, pszFullUrl, rlen); - mir_strcpy(nlc->szNewUrl + rlen, tmpUrl); - pszFullUrl = nlc->szNewUrl; - pszUrl = nullptr; - - if (NetlibHttpProcessUrl(nlhr, nlu, nlc, pszFullUrl) == nullptr) { - bytesSent = SOCKET_ERROR; - break; - } - } - else { - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else { - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else if (resultCode == 401 && !doneAuthHeader) { //auth required - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - replaceStr(pszAuthHdr, nullptr); - if (nlhrReply) { - char *szAuthStr = nullptr; - if (!complete) { - szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", httpSecurity.m_szProvider); - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (!szChallenge || !*lrtrimp(szChallenge)) - complete = true; - } - } - if (complete && httpSecurity.m_hNtlmSecurity) - szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", "Basic") : nullptr; - - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } - - pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete); - } - } - if (pszAuthHdr == nullptr) { - proxyAuthList.add(szHost, nullptr); - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else if (resultCode == 407 && !doneProxyAuthHeader) { //proxy auth required - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - mir_free(pszProxyAuthHdr); pszProxyAuthHdr = nullptr; - if (nlhrReply) { - char *szAuthStr = nullptr; - if (!complete) { - szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", httpSecurity.m_szProvider); - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (!szChallenge || !*lrtrimp(szChallenge + 1)) - complete = true; - } - } - if (complete && httpSecurity.m_hNtlmSecurity) - szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", "Basic") : nullptr; - - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } - - pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthStr, szChallenge, complete); - } - } - if (pszProxyAuthHdr == nullptr) { - proxyAuthList.add(nlc->szProxyServer, nullptr); - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else break; - - if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader) - replaceStr(pszProxyAuthHdr, nullptr); - - if (pszAuthHdr && resultCode != 401 && !doneAuthHeader) - replaceStr(pszAuthHdr, nullptr); - - if (nlhrReply) { - Netlib_FreeHttpRequest(nlhrReply); - nlhrReply = nullptr; - } - } - - if (count == 0) bytesSent = SOCKET_ERROR; - if (nlhrReply) - Netlib_FreeHttpRequest(nlhrReply); - - //clean up - mir_free(pszProxyAuthHdr); - mir_free(pszAuthHdr); - mir_free(szHost); - mir_free(szNewUrl); - - if (nlc) - NetlibLeaveNestedCS(&nlc->ncsSend); - - return bytesSent; -} - -MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr) -{ - if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE) { - SetLastError(ERROR_INVALID_PARAMETER); - return false; - } - - if (nlhr->headers) { - for (int i = 0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->headers[i]; - mir_free(p.szName); - mir_free(p.szValue); - } - mir_free(nlhr->headers); - } - mir_free(nlhr->pData); - mir_free(nlhr->szResultDescr); - mir_free(nlhr->szUrl); - mir_free(nlhr); - return true; -} - -#define NHRV_BUF_SIZE 8192 - -MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags) -{ - NetlibConnection *nlc = (NetlibConnection*)hConnection; - if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) - return nullptr; - - uint32_t dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT; - NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST)); - nlhr->cbSize = sizeof(NETLIBHTTPREQUEST); - nlhr->nlc = nlc; // Needed to id connection in the protocol HTTP gateway wrapper functions - nlhr->requestType = REQUEST_RESPONSE; - - int firstLineLength = 0; - if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, flags | MSG_PEEK, &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength)) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - return nullptr; - } - - char *buffer = (char*)_alloca(NHRV_BUF_SIZE + 1); - int bytesPeeked = Netlib_Recv(nlc, buffer, min(firstLineLength, NHRV_BUF_SIZE), flags | MSG_DUMPASTEXT); - if (bytesPeeked != firstLineLength) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - if (bytesPeeked != SOCKET_ERROR) - SetLastError(ERROR_HANDLE_EOF); - return nullptr; - } - - // Make sure all headers arrived - MBinBuffer buf; - int headersCount = 0; - bytesPeeked = 0; - for (bool headersCompleted = false; !headersCompleted;) { - bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, NHRV_BUF_SIZE, flags | MSG_DUMPASTEXT | MSG_NOTITLE); - if (bytesPeeked == 0) - break; - - if (bytesPeeked == SOCKET_ERROR) { - bytesPeeked = 0; - break; - } - - buf.append(buffer, bytesPeeked); - - headersCount = 0; - for (char *pbuffer = (char*)buf.data();; headersCount++) { - char *peol = strchr(pbuffer, '\n'); - if (peol == nullptr) break; - if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) { - bytesPeeked = peol - (char*)buf.data() + 1; - headersCompleted = true; - break; - } - pbuffer = peol + 1; - } - } - - if (bytesPeeked <= 0) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - return nullptr; - } - - // Receive headers - nlhr->headersCount = headersCount; - nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount); - - headersCount = 0; - for (char *pbuffer = (char*)buf.data();; headersCount++) { - char *peol = strchr(pbuffer, '\n'); - if (peol == nullptr || peol == pbuffer || (peol == (pbuffer+1) && *pbuffer == '\r')) - break; - *peol = 0; - - char *pColon = strchr(pbuffer, ':'); - if (pColon == nullptr) { - Netlib_FreeHttpRequest(nlhr); nlhr = nullptr; - SetLastError(ERROR_INVALID_DATA); - break; - } - - *pColon = 0; - nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer)); - nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon+1)); - pbuffer = peol + 1; - } - - // remove processed data - buf.remove(bytesPeeked); - nlc->foreBuf.appendBefore(buf.data(), buf.length()); - - NetlibLeaveNestedCS(&nlc->ncsRecv); - return nlhr; -} - -MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER nlu, NETLIBHTTPREQUEST *nlhr) -{ - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) || - nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || - nlhr->szUrl == nullptr || nlhr->szUrl[0] == 0) - { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - if (nlhr->nlc != nullptr && GetNetlibHandleType(nlhr->nlc) != NLH_CONNECTION) - nlhr->nlc = nullptr; - - NetlibConnection *nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc); - if (nlc == nullptr) - return nullptr; - - NETLIBHTTPREQUEST nlhrSend = *nlhr; - nlhrSend.flags |= NLHRF_SMARTREMOVEHOST; - - bool doneUserAgentHeader = Netlib_GetHeader(nlhr, "User-Agent") != nullptr; - bool doneAcceptEncoding = Netlib_GetHeader(nlhr, "Accept-Encoding") != nullptr; - if (!doneUserAgentHeader || !doneAcceptEncoding) { - nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2)); - memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount); - } - - char szUserAgent[64]; - if (!doneUserAgentHeader) { - nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent"; - nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent; - ++nlhrSend.headersCount; - - char szMirandaVer[64]; - strncpy_s(szMirandaVer, MIRANDA_VERSION_STRING, _TRUNCATE); - #if defined(_WIN64) - strncat_s(szMirandaVer, " x64", _TRUNCATE); - #endif - - char *pspace = strchr(szMirandaVer, ' '); - if (pspace) { - *pspace++ = '\0'; - mir_snprintf(szUserAgent, "Miranda/%s (%s)", szMirandaVer, pspace); - } - else mir_snprintf(szUserAgent, "Miranda/%s", szMirandaVer); - } - if (!doneAcceptEncoding) { - nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding"; - nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip"; - ++nlhrSend.headersCount; - } - if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) { - if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers); - nlhr->resultCode = nlhrSend.resultCode; - Netlib_CloseHandle(nlc); - return nullptr; - } - if (!doneUserAgentHeader || !doneAcceptEncoding) - mir_free(nlhrSend.headers); - - uint32_t dflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - uint32_t hflags = - (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - NETLIBHTTPREQUEST *nlhrReply; - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = Netlib_RecvHttpHeaders(nlc); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - if (nlhrReply) { - nlhrReply->szUrl = nlc->szNewUrl; - nlc->szNewUrl = nullptr; - } - - if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == nullptr) { - Netlib_CloseHandle(nlc); - if (nlhrReply) - nlhrReply->nlc = nullptr; - } - else nlhrReply->nlc = nlc; - - return nlhrReply; -} - -void NetlibHttpSetLastErrorUsingHttpResult(int result) -{ - if (result >= 200 && result < 300) { - SetLastError(ERROR_SUCCESS); - return; - } - switch (result) { - case 400: SetLastError(ERROR_BAD_FORMAT); break; - case 401: - case 402: - case 403: - case 407: SetLastError(ERROR_ACCESS_DENIED); break; - case 404: SetLastError(ERROR_FILE_NOT_FOUND); break; - case 405: - case 406: SetLastError(ERROR_INVALID_FUNCTION); break; - case 408: SetLastError(ERROR_TIMEOUT); break; - default: SetLastError(ERROR_GEN_FAILURE); break; - } -} - -char* gzip_decode(char *gzip_data, int *len_ptr, int window) -{ - if (*len_ptr == 0) return nullptr; - - int gzip_len = *len_ptr * 5; - char* output_data = nullptr; - - int gzip_err; - z_stream zstr; - - do { - output_data = (char*)mir_realloc(output_data, gzip_len+1); - if (output_data == nullptr) - break; - - zstr.next_in = (Bytef*)gzip_data; - zstr.avail_in = *len_ptr; - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; - inflateInit2_(&zstr, window, ZLIB_VERSION, sizeof(z_stream)); - - zstr.next_out = (Bytef*)output_data; - zstr.avail_out = gzip_len; - - gzip_err = inflate(&zstr, Z_FINISH); - - inflateEnd(&zstr); - gzip_len *= 2; - if (gzip_len > 10000000) - break; - } while (gzip_err == Z_BUF_ERROR); - - gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1; - - if (gzip_len <= 0) { - mir_free(output_data); - output_data = nullptr; - } - else output_data[gzip_len] = 0; - - *len_ptr = gzip_len; - return output_data; -} - -static int NetlibHttpRecvChunkHeader(NetlibConnection *nlc, bool first, uint32_t flags) -{ - MBinBuffer buf; - - while (true) { - char data[1000]; - int recvResult = Netlib_Recv(nlc, data, _countof(data) - 1, MSG_RAW | flags); - if (recvResult <= 0 || recvResult >= _countof(data)) - return SOCKET_ERROR; - - buf.append(data, recvResult); // add chunk - - auto *peol1 = (const char*)memchr(buf.data(), '\n', buf.length()); - if (peol1 == nullptr) - continue; - - auto *pStart = (const char *)buf.data(); - int cbRest = int(peol1 - pStart) + 1; - const char *peol2 = first ? peol1 : (const char*)memchr(peol1 + 1, '\n', buf.length() - cbRest); - if (peol2 == nullptr) - continue; - - int sz = peol2 - pStart + 1; - int r = strtol(first ? pStart : peol1 + 1, nullptr, 16); - if (r == 0) { - const char *peol3 = strchr(peol2 + 1, '\n'); - if (peol3 == nullptr) - continue; - sz = peol3 - pStart + 1; - } - buf.remove(sz); // remove all our data from buffer - nlc->foreBuf.appendBefore(buf.data(), buf.length()); - return r; - } -} - -NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection *nlc, uint32_t hflags, uint32_t dflags, bool isConnect) -{ - int dataLen = -1, i, chunkhdr = 0; - bool chunked = false; - int cenc = 0, cenctype = 0, close = 0; - -next: - NETLIBHTTPREQUEST *nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); - if (nlhrReply == nullptr) - return nullptr; - - if (nlhrReply->resultCode == 100) { - Netlib_FreeHttpRequest(nlhrReply); - goto next; - } - - if (nlhrReply->resultCode == 204) - dataLen = 0; - - for (i = 0; i < nlhrReply->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhrReply->headers[i]; - if (!mir_strcmpi(p.szName, "Content-Length")) - dataLen = atoi(p.szValue); - - if (!mir_strcmpi(p.szName, "Content-Encoding")) { - cenc = i; - if (strstr(p.szValue, "gzip")) - cenctype = 1; - else if (strstr(p.szValue, "deflate")) - cenctype = 2; - } - - if (!mir_strcmpi(p.szName, "Connection")) - close = !mir_strcmpi(p.szValue, "close"); - - if (!mir_strcmpi(p.szName, "Transfer-Encoding") && !mir_strcmpi(p.szValue, "chunked")) { - chunked = true; - chunkhdr = i; - dataLen = -1; - } - } - - if (nlhrReply->resultCode >= 200 && (dataLen > 0 || (!isConnect && dataLen < 0))) { - int recvResult, chunksz = -1; - int dataBufferAlloced; - - if (chunked) { - chunksz = NetlibHttpRecvChunkHeader(nlc, true, dflags | (cenctype ? MSG_NODUMP : 0)); - if (chunksz == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - dataLen = chunksz; - } - dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1; - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - - while (chunksz != 0) { - while (true) { - recvResult = RecvWithTimeoutTime(nlc, GetTickCount() + HTTPRECVDATATIMEOUT, - nlhrReply->pData + nlhrReply->dataLength, - dataBufferAlloced - nlhrReply->dataLength - 1, - dflags | (cenctype ? MSG_NODUMP : 0)); - - if (recvResult == 0) break; - if (recvResult == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - nlhrReply->dataLength += recvResult; - - if (dataLen >= 0) { - if (nlhrReply->dataLength >= dataLen) - break; - } - else if ((dataBufferAlloced - nlhrReply->dataLength) < 256) { - dataBufferAlloced += 2048; - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - if (nlhrReply->pData == nullptr) { - SetLastError(ERROR_OUTOFMEMORY); - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - } - Sleep(10); - } - - if (!chunked) - break; - - chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags | MSG_NODUMP); - if (chunksz == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - dataLen += chunksz; - dataBufferAlloced += chunksz; - - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - } - - nlhrReply->pData[nlhrReply->dataLength] = '\0'; - } - - if (chunked) { - nlhrReply->headers[chunkhdr].szName = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szName, 16); - mir_strcpy(nlhrReply->headers[chunkhdr].szName, "Content-Length"); - - nlhrReply->headers[chunkhdr].szValue = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szValue, 16); - mir_snprintf(nlhrReply->headers[chunkhdr].szValue, 16, "%u", nlhrReply->dataLength); - } - - if (cenctype) { - int bufsz = nlhrReply->dataLength; - char* szData = nullptr; - - switch (cenctype) { - case 1: - szData = gzip_decode(nlhrReply->pData, &bufsz, 0x10 | MAX_WBITS); - break; - - case 2: - szData = gzip_decode(nlhrReply->pData, &bufsz, -MAX_WBITS); - if (bufsz < 0) { - bufsz = nlhrReply->dataLength; - szData = gzip_decode(nlhrReply->pData, &bufsz, MAX_WBITS); - } - break; - } - - if (bufsz > 0) { - Netlib_Dump(nlc, (uint8_t*)szData, bufsz, false, dflags | MSG_NOTITLE); - mir_free(nlhrReply->pData); - nlhrReply->pData = szData; - nlhrReply->dataLength = bufsz; - - mir_free(nlhrReply->headers[cenc].szName); - mir_free(nlhrReply->headers[cenc].szValue); - memmove(&nlhrReply->headers[cenc], &nlhrReply->headers[cenc+1], (--nlhrReply->headersCount-cenc)*sizeof(nlhrReply->headers[0])); - } - else if (bufsz == 0) { - mir_free(nlhrReply->pData); - nlhrReply->pData = nullptr; - nlhrReply->dataLength = 0; - } - } - - if (close && - (nlc->proxyType != PROXYTYPE_HTTP || nlc->url.flags & NLOCF_SSL) && - (!isConnect || nlhrReply->resultCode != 200)) - NetlibDoCloseSocket(nlc); - - return nlhrReply; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "../libs/zlib/src/zlib.h"
+#include "netlib.h"
+
+#define HTTPRECVHEADERSTIMEOUT 30000 //in ms
+#define HTTPRECVDATATIMEOUT 20000
+
+struct ProxyAuth
+{
+ char *szServer;
+ char *szMethod;
+
+ ProxyAuth(const char *pszServer, const char *pszMethod)
+ {
+ szServer = mir_strdup(pszServer);
+ szMethod = mir_strdup(pszMethod);
+ }
+ ~ProxyAuth()
+ {
+ mir_free(szServer);
+ mir_free(szMethod);
+ }
+ static int Compare(const ProxyAuth *p1, const ProxyAuth *p2)
+ {
+ return mir_strcmpi(p1->szServer, p2->szServer);
+ }
+};
+
+struct ProxyAuthList : OBJLIST<ProxyAuth>
+{
+ ProxyAuthList() : OBJLIST<ProxyAuth>(2, ProxyAuth::Compare) {}
+
+ void add(const char *szServer, const char *szMethod)
+ {
+ if (szServer == nullptr) return;
+ int i = getIndex((ProxyAuth*)&szServer);
+ if (i >= 0) {
+ ProxyAuth &rec = (*this)[i];
+ if (szMethod == nullptr)
+ remove(i);
+ else if (_stricmp(rec.szMethod, szMethod)) {
+ mir_free(rec.szMethod);
+ rec.szMethod = mir_strdup(szMethod);
+ }
+ }
+ else insert(new ProxyAuth(szServer, szMethod));
+ }
+
+ const char* find(const char *szServer)
+ {
+ ProxyAuth *rec = szServer ? OBJLIST<ProxyAuth>::find((ProxyAuth*)&szServer) : nullptr;
+ return rec ? rec->szMethod : nullptr;
+ }
+};
+
+ProxyAuthList proxyAuthList;
+
+static int RecvWithTimeoutTime(NetlibConnection *nlc, int dwTimeoutTime, char *buf, int len, int flags)
+{
+ int dwTimeNow;
+
+ if (nlc->foreBuf.isEmpty() && !Netlib_SslPending(nlc->hSsl)) {
+ while ((dwTimeNow = GetTickCount()) < dwTimeoutTime) {
+ int dwDeltaTime = min(dwTimeoutTime - dwTimeNow, 1000);
+ int res = WaitUntilReadable(nlc->s, dwDeltaTime);
+
+ switch (res) {
+ case SOCKET_ERROR:
+ return SOCKET_ERROR;
+
+ case 1:
+ return Netlib_Recv(nlc, buf, len, flags);
+ }
+
+ if (nlc->termRequested || Miranda_IsTerminated())
+ return 0;
+ }
+ SetLastError(ERROR_TIMEOUT);
+ return SOCKET_ERROR;
+ }
+ return Netlib_Recv(nlc, buf, len, flags);
+}
+
+MIR_APP_DLL(char *) Netlib_GetHeader(const NETLIBHTTPREQUEST *nlhr, const char *hdr)
+{
+ if (nlhr == nullptr || hdr == nullptr)
+ return nullptr;
+
+ for (int i=0; i < nlhr->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhr->headers[i];
+ if (_stricmp(p.szName, hdr) == 0)
+ return p.szValue;
+ }
+
+ return nullptr;
+}
+
+static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider)
+{
+ char *szBasicHdr = nullptr;
+ char *szNegoHdr = nullptr;
+ char *szNtlmHdr = nullptr;
+
+ for (int i=0; i < nlhrReply->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhrReply->headers[i];
+ if (_stricmp(p.szName, hdr) == 0) {
+ if (_strnicmp(p.szValue, "Negotiate", 9) == 0)
+ szNegoHdr = p.szValue;
+ else if (_strnicmp(p.szValue, "NTLM", 4) == 0)
+ szNtlmHdr = p.szValue;
+ else if (_strnicmp(p.szValue, "Basic", 5) == 0)
+ szBasicHdr = p.szValue;
+ }
+ }
+
+ if (szNegoHdr && (!szProvider || !_stricmp(szProvider, "Negotiate"))) return szNegoHdr;
+ if (szNtlmHdr && (!szProvider || !_stricmp(szProvider, "NTLM"))) return szNtlmHdr;
+ if (!szProvider || !_stricmp(szProvider, "Basic")) return szBasicHdr;
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NetlibConnFromUrl(const char *szUrl, bool secur, NetlibUrl &url)
+{
+ secur = secur || _strnicmp(szUrl, "https", 5) == 0;
+
+ const char* phost = strstr(szUrl, "://");
+ url.szHost = phost ? phost + 3 : szUrl;
+
+ int idx = url.szHost.Find('/');
+ if (idx != -1)
+ url.szHost.Truncate(idx);
+
+ if ((idx = url.szHost.Find(':')) != -1) {
+ url.port = strtol(url.szHost.c_str() + idx + 1, nullptr, 10);
+ url.szHost.Truncate(idx);
+ }
+ else url.port = secur ? 443 : 80;
+ url.flags = (secur ? NLOCF_SSL : 0);
+}
+
+static NetlibConnection* NetlibHttpProcessUrl(NETLIBHTTPREQUEST *nlhr, NetlibUser *nlu, NetlibConnection *nlc, const char *szUrl = nullptr)
+{
+ NetlibUrl url;
+
+ if (szUrl == nullptr)
+ NetlibConnFromUrl(nlhr->szUrl, (nlhr->flags & NLHRF_SSL) != 0, url);
+ else
+ NetlibConnFromUrl(szUrl, false, url);
+
+ url.flags |= NLOCF_HTTP;
+ if (url.flags & NLOCF_SSL)
+ nlhr->flags |= NLHRF_SSL;
+ else
+ nlhr->flags &= ~NLHRF_SSL;
+
+ if (nlc != nullptr) {
+ bool httpProxy = !(url.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP;
+ bool sameHost = mir_strcmp(nlc->url.szHost, url.szHost) == 0 && nlc->url.port == url.port;
+
+ if (!httpProxy && !sameHost) {
+ NetlibDoCloseSocket(nlc);
+
+ nlc->url = url;
+ return NetlibDoConnect(nlc) ? nlc : nullptr;
+ }
+ }
+ else nlc = (NetlibConnection*)Netlib_OpenConnection(nlu, url.szHost, url.port, 0, url.flags);
+
+ return nlc;
+}
+
+struct HttpSecurityContext
+{
+ HANDLE m_hNtlmSecurity;
+ char *m_szHost;
+ char *m_szProvider;
+
+ HttpSecurityContext()
+ {
+ m_hNtlmSecurity = nullptr; m_szHost = nullptr; m_szProvider = nullptr;
+ }
+
+ ~HttpSecurityContext() { Destroy(); }
+
+ void Destroy(void)
+ {
+ if (!m_hNtlmSecurity) return;
+
+ Netlib_DestroySecurityProvider(m_hNtlmSecurity);
+ m_hNtlmSecurity = nullptr;
+ mir_free(m_szHost); m_szHost = nullptr;
+ mir_free(m_szProvider); m_szProvider = nullptr;
+ }
+
+ bool TryBasic(void)
+ {
+ return m_hNtlmSecurity && m_szProvider && _stricmp(m_szProvider, "Basic");
+ }
+
+ char* Execute(NetlibConnection *nlc, char *szHost, const char *szProvider, const char *szChallenge, unsigned &complete)
+ {
+ char *szAuthHdr = nullptr;
+ bool justCreated = false;
+ NetlibUser *nlu = nlc->nlu;
+
+ if (m_hNtlmSecurity) {
+ bool newAuth = !m_szProvider || !szProvider || _stricmp(m_szProvider, szProvider);
+ newAuth = newAuth || (m_szHost != szHost && (!m_szHost || !szHost || _stricmp(m_szHost, szHost)));
+ if (newAuth)
+ Destroy();
+ }
+
+ if (m_hNtlmSecurity == nullptr) {
+ CMStringA szSpnStr;
+ if (szHost && _stricmp(szProvider, "Basic")) {
+ unsigned long ip = inet_addr(szHost);
+ PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET);
+ szSpnStr.Format("HTTP/%s", host && host->h_name ? host->h_name : szHost);
+ _strlwr(szSpnStr.GetBuffer() + 5);
+ Netlib_Logf(nlu, "Host SPN: %s", szSpnStr.c_str());
+ }
+ m_hNtlmSecurity = Netlib_InitSecurityProvider(_A2T(szProvider), szSpnStr.IsEmpty() ? nullptr : _A2T(szSpnStr.c_str()));
+ if (m_hNtlmSecurity) {
+ m_szProvider = mir_strdup(szProvider);
+ m_szHost = mir_strdup(szHost);
+ justCreated = true;
+ }
+ }
+
+ if (m_hNtlmSecurity) {
+ ptrW szLogin, szPassw;
+
+ if (nlu->settings.useProxyAuth) {
+ mir_cslock lck(csNetlibUser);
+ szLogin = mir_a2u(nlu->settings.szProxyAuthUser);
+ szPassw = mir_a2u(nlu->settings.szProxyAuthPassword);
+ }
+
+ szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity, szChallenge, szLogin, szPassw, true, complete);
+ if (!szAuthHdr)
+ Netlib_Logf(nullptr, "Security login %s failed, user: %S pssw: %S", szProvider, szLogin ? szLogin.get() : L"(no user)", szPassw ? L"(exist)" : L"(no psw)");
+ else if (justCreated)
+ proxyAuthList.add(m_szHost, m_szProvider);
+ }
+ else complete = 1;
+
+ return szAuthHdr;
+ }
+};
+
+static int HttpPeekFirstResponseLine(NetlibConnection *nlc, uint32_t dwTimeoutTime, uint32_t recvFlags, int *resultCode, char **ppszResultDescr, int *length)
+{
+ int bytesPeeked;
+ char buffer[2048], *peol;
+
+ while (true) {
+ bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, _countof(buffer) - 1, MSG_PEEK | recvFlags);
+ if (bytesPeeked == 0) {
+ SetLastError(ERROR_HANDLE_EOF);
+ return 0;
+ }
+ if (bytesPeeked == SOCKET_ERROR)
+ return 0;
+
+ buffer[bytesPeeked] = '\0';
+ if ((peol = strchr(buffer, '\n')) != nullptr)
+ break;
+
+ if ((int)mir_strlen(buffer) < bytesPeeked) {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+ if (bytesPeeked == _countof(buffer) - 1) {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return 0;
+ }
+ if (Miranda_IsTerminated())
+ return 0;
+ Sleep(10);
+ }
+
+ if (peol == buffer) {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+
+ *peol = '\0';
+
+ if (_strnicmp(buffer, "HTTP/", 5)) {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+
+ size_t off = strcspn(buffer, " \t");
+ if (off >= (unsigned)bytesPeeked)
+ return 0;
+
+ char *pResultCode = buffer + off;
+ *(pResultCode++) = 0;
+
+ char *pResultDescr;
+ *resultCode = strtol(pResultCode, &pResultDescr, 10);
+
+ if (ppszResultDescr)
+ *ppszResultDescr = mir_strdup(lrtrimp(pResultDescr));
+
+ if (length)
+ *length = peol - buffer + 1;
+ return 1;
+}
+
+static int SendHttpRequestAndData(NetlibConnection *nlc, CMStringA &httpRequest, NETLIBHTTPREQUEST *nlhr, int sendContentLengthHeader)
+{
+ bool sendData = (nlhr->requestType == REQUEST_POST || nlhr->requestType == REQUEST_PUT || nlhr->requestType == REQUEST_PATCH);
+
+ if (sendContentLengthHeader && sendData)
+ httpRequest.AppendFormat("Content-Length: %d\r\n\r\n", nlhr->dataLength);
+ else
+ httpRequest.AppendFormat("\r\n");
+
+ uint32_t hflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
+ (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND | NLHRF_NODUMPHEADERS) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ int bytesSent = Netlib_Send(nlc, httpRequest, httpRequest.GetLength(), hflags);
+ if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength) {
+ uint32_t sflags = MSG_NOTITLE | (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
+ (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ int sendResult = Netlib_Send(nlc, nlhr->pData, nlhr->dataLength, sflags);
+
+ bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR;
+ }
+
+ return bytesSent;
+}
+
+MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN nlc, NETLIBHTTPREQUEST *nlhr)
+{
+ NETLIBHTTPREQUEST *nlhrReply = nullptr;
+ HttpSecurityContext httpSecurity;
+
+ char *szHost = nullptr, *szNewUrl = nullptr;
+ char *pszProxyAuthHdr = nullptr, *pszAuthHdr = nullptr;
+ int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader;
+ int bytesSent = 0;
+ bool lastFirstLineFail = false;
+
+ if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl == nullptr || nlhr->szUrl[0] == '\0') {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ NetlibUser *nlu = nlc->nlu;
+ if (GetNetlibHandleType(nlu) != NLH_USER) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ int hdrTimeout = (nlhr->timeout) ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT;
+
+ const char *pszRequest;
+ switch (nlhr->requestType) {
+ case REQUEST_GET: pszRequest = "GET"; break;
+ case REQUEST_POST: pszRequest = "POST"; break;
+ case REQUEST_CONNECT: pszRequest = "CONNECT"; break;
+ case REQUEST_HEAD: pszRequest = "HEAD"; break;
+ case REQUEST_PUT: pszRequest = "PUT"; break;
+ case REQUEST_DELETE: pszRequest = "DELETE"; break;
+ case REQUEST_PATCH: pszRequest = "PATCH"; break;
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ if (!NetlibEnterNestedCS(nlc, NLNCS_SEND))
+ return SOCKET_ERROR;
+
+ const char *pszFullUrl = nlhr->szUrl;
+ const char *pszUrl = nullptr;
+
+ unsigned complete = false;
+ int count = 11;
+ while (--count) {
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION) {
+ nlc = nullptr;
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+
+ if (!NetlibReconnect(nlc)) {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+
+ if (!pszUrl) {
+ pszUrl = pszFullUrl;
+ if (!(nlhr->flags & NLHRF_MANUALHOST)) {
+ bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL);
+
+ const char *ppath, *phost;
+ phost = strstr(pszUrl, "://");
+ if (phost == nullptr) phost = pszUrl;
+ else phost += 3;
+ ppath = strchr(phost, '/');
+ if (ppath == phost)
+ phost = nullptr;
+
+ replaceStr(szHost, phost);
+ if (ppath && phost)
+ szHost[ppath - phost] = 0;
+
+ if ((nlhr->flags & NLHRF_SMARTREMOVEHOST) && !usingProxy)
+ pszUrl = ppath ? ppath : "/";
+
+ if (usingProxy && phost && !nlc->dnsThroughProxy) {
+ char *tszHost = mir_strdup(phost);
+ if (ppath)
+ tszHost[ppath - phost] = 0;
+ char *cln = strchr(tszHost, ':'); if (cln) *cln = 0;
+
+ if (inet_addr(tszHost) == INADDR_NONE) {
+ in_addr ip;
+ if (ip.S_un.S_addr = DnsLookup(nlu, tszHost)) {
+ mir_free(szHost);
+ if (cln) *cln = ':';
+ szHost = CMStringA(FORMAT, "%s%s", inet_ntoa(ip), cln ? cln : "").Detach();
+ }
+ }
+ mir_free(tszHost);
+ }
+ }
+ }
+
+ if (nlc->proxyAuthNeeded && proxyAuthList.getCount()) {
+ if (httpSecurity.m_szProvider == nullptr && nlc->szProxyServer) {
+ const char *szAuthMethodNlu = proxyAuthList.find(nlc->szProxyServer);
+ if (szAuthMethodNlu) {
+ mir_free(pszProxyAuthHdr);
+ pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthMethodNlu, "", complete);
+ }
+ }
+ }
+ nlc->proxyAuthNeeded = false;
+
+ CMStringA httpRequest(FORMAT, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0);
+
+ // HTTP headers
+ doneHostHeader = doneContentLengthHeader = doneProxyAuthHeader = doneAuthHeader = 0;
+ for (i = 0; i < nlhr->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhr->headers[i];
+ if (!mir_strcmpi(p.szName, "Host")) doneHostHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Content-Length")) doneContentLengthHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Proxy-Authorization")) doneProxyAuthHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Authorization")) doneAuthHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Connection")) continue;
+ if (p.szValue == nullptr) continue;
+ httpRequest.AppendFormat("%s: %s\r\n", p.szName, p.szValue);
+ }
+ if (szHost && !doneHostHeader)
+ httpRequest.AppendFormat("%s: %s\r\n", "Host", szHost);
+ if (pszProxyAuthHdr && !doneProxyAuthHeader)
+ httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr);
+ if (pszAuthHdr && !doneAuthHeader)
+ httpRequest.AppendFormat("%s: %s\r\n", "Authorization", pszAuthHdr);
+ httpRequest.AppendFormat("%s: %s\r\n", "Connection", "Keep-Alive");
+ httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Connection", "Keep-Alive");
+
+ // Add Sticky Headers
+ if (nlu->szStickyHeaders != nullptr)
+ httpRequest.AppendFormat("%s\r\n", nlu->szStickyHeaders);
+
+ // send it
+ bytesSent = SendHttpRequestAndData(nlc, httpRequest, nlhr, !doneContentLengthHeader);
+ if (bytesSent == SOCKET_ERROR)
+ break;
+
+ // ntlm reply
+ if (doneContentLengthHeader && nlhr->requestType != REQUEST_HEAD)
+ break;
+
+ uint32_t fflags = MSG_PEEK | MSG_NODUMP | ((nlhr->flags & NLHRF_NOPROXY) ? MSG_RAW : 0);
+ uint32_t dwTimeOutTime = hdrTimeout < 0 ? -1 : GetTickCount() + hdrTimeout;
+ if (!HttpPeekFirstResponseLine(nlc, dwTimeOutTime, fflags, &nlhr->resultCode, nullptr, nullptr)) {
+ uint32_t err = GetLastError();
+ Netlib_Logf(nlu, "%s %d: %s Failed (%u %u)", __FILE__, __LINE__, "HttpPeekFirstResponseLine", err, count);
+
+ // connection died while we were waiting
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION) {
+ nlc = nullptr;
+ break;
+ }
+
+ if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW || lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT) {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+
+ lastFirstLineFail = true;
+ continue;
+ }
+
+ int resultCode = nlhr->resultCode;
+ lastFirstLineFail = false;
+
+ uint32_t hflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPHEADERS | NLHRF_NODUMPSEND) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ uint32_t dflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? MSG_NODUMP : MSG_DUMPASTEXT | MSG_DUMPPROXY) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0) | MSG_NODUMP;
+
+ if (resultCode == 100)
+ nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
+
+ else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) && (nlhr->flags & NLHRF_REDIRECT))) { // redirect
+ pszUrl = nullptr;
+
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ if (nlhrReply) {
+ auto *tmpUrl = Netlib_GetHeader(nlhrReply, "Location");
+ if (tmpUrl) {
+ size_t rlen = 0;
+ if (tmpUrl[0] == '/') {
+ const char *ppath, *phost;
+ phost = strstr(pszFullUrl, "://");
+ phost = phost ? phost + 3 : pszFullUrl;
+ ppath = strchr(phost, '/');
+ rlen = ppath ? ppath - pszFullUrl : mir_strlen(pszFullUrl);
+ }
+
+ nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + mir_strlen(tmpUrl) * 3 + 1);
+
+ strncpy(nlc->szNewUrl, pszFullUrl, rlen);
+ mir_strcpy(nlc->szNewUrl + rlen, tmpUrl);
+ pszFullUrl = nlc->szNewUrl;
+ pszUrl = nullptr;
+
+ if (NetlibHttpProcessUrl(nlhr, nlu, nlc, pszFullUrl) == nullptr) {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else {
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else {
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else if (resultCode == 401 && !doneAuthHeader) { //auth required
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ replaceStr(pszAuthHdr, nullptr);
+ if (nlhrReply) {
+ char *szAuthStr = nullptr;
+ if (!complete) {
+ szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", httpSecurity.m_szProvider);
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (!szChallenge || !*lrtrimp(szChallenge))
+ complete = true;
+ }
+ }
+ if (complete && httpSecurity.m_hNtlmSecurity)
+ szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", "Basic") : nullptr;
+
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); }
+
+ pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete);
+ }
+ }
+ if (pszAuthHdr == nullptr) {
+ proxyAuthList.add(szHost, nullptr);
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else if (resultCode == 407 && !doneProxyAuthHeader) { //proxy auth required
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ mir_free(pszProxyAuthHdr); pszProxyAuthHdr = nullptr;
+ if (nlhrReply) {
+ char *szAuthStr = nullptr;
+ if (!complete) {
+ szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", httpSecurity.m_szProvider);
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (!szChallenge || !*lrtrimp(szChallenge + 1))
+ complete = true;
+ }
+ }
+ if (complete && httpSecurity.m_hNtlmSecurity)
+ szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", "Basic") : nullptr;
+
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); }
+
+ pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthStr, szChallenge, complete);
+ }
+ }
+ if (pszProxyAuthHdr == nullptr) {
+ proxyAuthList.add(nlc->szProxyServer, nullptr);
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else break;
+
+ if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader)
+ replaceStr(pszProxyAuthHdr, nullptr);
+
+ if (pszAuthHdr && resultCode != 401 && !doneAuthHeader)
+ replaceStr(pszAuthHdr, nullptr);
+
+ if (nlhrReply) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ nlhrReply = nullptr;
+ }
+ }
+
+ if (count == 0) bytesSent = SOCKET_ERROR;
+ if (nlhrReply)
+ Netlib_FreeHttpRequest(nlhrReply);
+
+ //clean up
+ mir_free(pszProxyAuthHdr);
+ mir_free(pszAuthHdr);
+ mir_free(szHost);
+ mir_free(szNewUrl);
+
+ if (nlc)
+ NetlibLeaveNestedCS(&nlc->ncsSend);
+
+ return bytesSent;
+}
+
+MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr)
+{
+ if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ if (nlhr->headers) {
+ for (int i = 0; i < nlhr->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhr->headers[i];
+ mir_free(p.szName);
+ mir_free(p.szValue);
+ }
+ mir_free(nlhr->headers);
+ }
+ mir_free(nlhr->pData);
+ mir_free(nlhr->szResultDescr);
+ mir_free(nlhr->szUrl);
+ mir_free(nlhr);
+ return true;
+}
+
+#define NHRV_BUF_SIZE 8192
+
+MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags)
+{
+ NetlibConnection *nlc = (NetlibConnection*)hConnection;
+ if (!NetlibEnterNestedCS(nlc, NLNCS_RECV))
+ return nullptr;
+
+ uint32_t dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT;
+ NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST));
+ nlhr->cbSize = sizeof(NETLIBHTTPREQUEST);
+ nlhr->nlc = nlc; // Needed to id connection in the protocol HTTP gateway wrapper functions
+ nlhr->requestType = REQUEST_RESPONSE;
+
+ int firstLineLength = 0;
+ if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, flags | MSG_PEEK, &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength)) {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ Netlib_FreeHttpRequest(nlhr);
+ return nullptr;
+ }
+
+ char *buffer = (char*)_alloca(NHRV_BUF_SIZE + 1);
+ int bytesPeeked = Netlib_Recv(nlc, buffer, min(firstLineLength, NHRV_BUF_SIZE), flags | MSG_DUMPASTEXT);
+ if (bytesPeeked != firstLineLength) {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ Netlib_FreeHttpRequest(nlhr);
+ if (bytesPeeked != SOCKET_ERROR)
+ SetLastError(ERROR_HANDLE_EOF);
+ return nullptr;
+ }
+
+ // Make sure all headers arrived
+ MBinBuffer buf;
+ int headersCount = 0;
+ bytesPeeked = 0;
+ for (bool headersCompleted = false; !headersCompleted;) {
+ bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, NHRV_BUF_SIZE, flags | MSG_DUMPASTEXT | MSG_NOTITLE);
+ if (bytesPeeked == 0)
+ break;
+
+ if (bytesPeeked == SOCKET_ERROR) {
+ bytesPeeked = 0;
+ break;
+ }
+
+ buf.append(buffer, bytesPeeked);
+
+ headersCount = 0;
+ for (char *pbuffer = (char*)buf.data();; headersCount++) {
+ char *peol = strchr(pbuffer, '\n');
+ if (peol == nullptr) break;
+ if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) {
+ bytesPeeked = peol - (char*)buf.data() + 1;
+ headersCompleted = true;
+ break;
+ }
+ pbuffer = peol + 1;
+ }
+ }
+
+ if (bytesPeeked <= 0) {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ Netlib_FreeHttpRequest(nlhr);
+ return nullptr;
+ }
+
+ // Receive headers
+ nlhr->headersCount = headersCount;
+ nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount);
+
+ headersCount = 0;
+ for (char *pbuffer = (char*)buf.data();; headersCount++) {
+ char *peol = strchr(pbuffer, '\n');
+ if (peol == nullptr || peol == pbuffer || (peol == (pbuffer+1) && *pbuffer == '\r'))
+ break;
+ *peol = 0;
+
+ char *pColon = strchr(pbuffer, ':');
+ if (pColon == nullptr) {
+ Netlib_FreeHttpRequest(nlhr); nlhr = nullptr;
+ SetLastError(ERROR_INVALID_DATA);
+ break;
+ }
+
+ *pColon = 0;
+ nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer));
+ nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon+1));
+ pbuffer = peol + 1;
+ }
+
+ // remove processed data
+ buf.remove(bytesPeeked);
+ nlc->foreBuf.appendBefore(buf.data(), buf.length());
+
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ return nlhr;
+}
+
+MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER nlu, NETLIBHTTPREQUEST *nlhr)
+{
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) ||
+ nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) ||
+ nlhr->szUrl == nullptr || nlhr->szUrl[0] == 0)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return nullptr;
+ }
+
+ if (nlhr->nlc != nullptr && GetNetlibHandleType(nlhr->nlc) != NLH_CONNECTION)
+ nlhr->nlc = nullptr;
+
+ NetlibConnection *nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc);
+ if (nlc == nullptr)
+ return nullptr;
+
+ NETLIBHTTPREQUEST nlhrSend = *nlhr;
+ nlhrSend.flags |= NLHRF_SMARTREMOVEHOST;
+
+ bool doneUserAgentHeader = Netlib_GetHeader(nlhr, "User-Agent") != nullptr;
+ bool doneAcceptEncoding = Netlib_GetHeader(nlhr, "Accept-Encoding") != nullptr;
+ if (!doneUserAgentHeader || !doneAcceptEncoding) {
+ nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2));
+ memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount);
+ }
+
+ char szUserAgent[64];
+ if (!doneUserAgentHeader) {
+ nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent";
+ nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent;
+ ++nlhrSend.headersCount;
+
+ char szMirandaVer[64];
+ strncpy_s(szMirandaVer, MIRANDA_VERSION_STRING, _TRUNCATE);
+ #if defined(_WIN64)
+ strncat_s(szMirandaVer, " x64", _TRUNCATE);
+ #endif
+
+ char *pspace = strchr(szMirandaVer, ' ');
+ if (pspace) {
+ *pspace++ = '\0';
+ mir_snprintf(szUserAgent, "Miranda/%s (%s)", szMirandaVer, pspace);
+ }
+ else mir_snprintf(szUserAgent, "Miranda/%s", szMirandaVer);
+ }
+ if (!doneAcceptEncoding) {
+ nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding";
+ nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip";
+ ++nlhrSend.headersCount;
+ }
+ if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) {
+ if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers);
+ nlhr->resultCode = nlhrSend.resultCode;
+ Netlib_CloseHandle(nlc);
+ return nullptr;
+ }
+ if (!doneUserAgentHeader || !doneAcceptEncoding)
+ mir_free(nlhrSend.headers);
+
+ uint32_t dflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
+ (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ uint32_t hflags =
+ (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ NETLIBHTTPREQUEST *nlhrReply;
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = Netlib_RecvHttpHeaders(nlc);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ if (nlhrReply) {
+ nlhrReply->szUrl = nlc->szNewUrl;
+ nlc->szNewUrl = nullptr;
+ }
+
+ if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == nullptr) {
+ Netlib_CloseHandle(nlc);
+ if (nlhrReply)
+ nlhrReply->nlc = nullptr;
+ }
+ else nlhrReply->nlc = nlc;
+
+ return nlhrReply;
+}
+
+void NetlibHttpSetLastErrorUsingHttpResult(int result)
+{
+ if (result >= 200 && result < 300) {
+ SetLastError(ERROR_SUCCESS);
+ return;
+ }
+ switch (result) {
+ case 400: SetLastError(ERROR_BAD_FORMAT); break;
+ case 401:
+ case 402:
+ case 403:
+ case 407: SetLastError(ERROR_ACCESS_DENIED); break;
+ case 404: SetLastError(ERROR_FILE_NOT_FOUND); break;
+ case 405:
+ case 406: SetLastError(ERROR_INVALID_FUNCTION); break;
+ case 408: SetLastError(ERROR_TIMEOUT); break;
+ default: SetLastError(ERROR_GEN_FAILURE); break;
+ }
+}
+
+char* gzip_decode(char *gzip_data, int *len_ptr, int window)
+{
+ if (*len_ptr == 0) return nullptr;
+
+ int gzip_len = *len_ptr * 5;
+ char* output_data = nullptr;
+
+ int gzip_err;
+ z_stream zstr;
+
+ do {
+ output_data = (char*)mir_realloc(output_data, gzip_len+1);
+ if (output_data == nullptr)
+ break;
+
+ zstr.next_in = (Bytef*)gzip_data;
+ zstr.avail_in = *len_ptr;
+ zstr.zalloc = Z_NULL;
+ zstr.zfree = Z_NULL;
+ zstr.opaque = Z_NULL;
+ inflateInit2_(&zstr, window, ZLIB_VERSION, sizeof(z_stream));
+
+ zstr.next_out = (Bytef*)output_data;
+ zstr.avail_out = gzip_len;
+
+ gzip_err = inflate(&zstr, Z_FINISH);
+
+ inflateEnd(&zstr);
+ gzip_len *= 2;
+ if (gzip_len > 10000000)
+ break;
+ } while (gzip_err == Z_BUF_ERROR);
+
+ gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1;
+
+ if (gzip_len <= 0) {
+ mir_free(output_data);
+ output_data = nullptr;
+ }
+ else output_data[gzip_len] = 0;
+
+ *len_ptr = gzip_len;
+ return output_data;
+}
+
+static int NetlibHttpRecvChunkHeader(NetlibConnection *nlc, bool first, uint32_t flags)
+{
+ MBinBuffer buf;
+
+ while (true) {
+ char data[1000];
+ int recvResult = Netlib_Recv(nlc, data, _countof(data) - 1, MSG_RAW | flags);
+ if (recvResult <= 0 || recvResult >= _countof(data))
+ return SOCKET_ERROR;
+
+ buf.append(data, recvResult); // add chunk
+
+ auto *peol1 = (const char*)memchr(buf.data(), '\n', buf.length());
+ if (peol1 == nullptr)
+ continue;
+
+ auto *pStart = (const char *)buf.data();
+ int cbRest = int(peol1 - pStart) + 1;
+ const char *peol2 = first ? peol1 : (const char*)memchr(peol1 + 1, '\n', buf.length() - cbRest);
+ if (peol2 == nullptr)
+ continue;
+
+ int sz = peol2 - pStart + 1;
+ int r = strtol(first ? pStart : peol1 + 1, nullptr, 16);
+ if (r == 0) {
+ const char *peol3 = strchr(peol2 + 1, '\n');
+ if (peol3 == nullptr)
+ continue;
+ sz = peol3 - pStart + 1;
+ }
+ buf.remove(sz); // remove all our data from buffer
+ nlc->foreBuf.appendBefore(buf.data(), buf.length());
+ return r;
+ }
+}
+
+NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection *nlc, uint32_t hflags, uint32_t dflags, bool isConnect)
+{
+ int dataLen = -1, i, chunkhdr = 0;
+ bool chunked = false;
+ int cenc = 0, cenctype = 0, close = 0;
+
+next:
+ NETLIBHTTPREQUEST *nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags);
+ if (nlhrReply == nullptr)
+ return nullptr;
+
+ if (nlhrReply->resultCode == 100) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ goto next;
+ }
+
+ if (nlhrReply->resultCode == 204)
+ dataLen = 0;
+
+ for (i = 0; i < nlhrReply->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhrReply->headers[i];
+ if (!mir_strcmpi(p.szName, "Content-Length"))
+ dataLen = atoi(p.szValue);
+
+ if (!mir_strcmpi(p.szName, "Content-Encoding")) {
+ cenc = i;
+ if (strstr(p.szValue, "gzip"))
+ cenctype = 1;
+ else if (strstr(p.szValue, "deflate"))
+ cenctype = 2;
+ }
+
+ if (!mir_strcmpi(p.szName, "Connection"))
+ close = !mir_strcmpi(p.szValue, "close");
+
+ if (!mir_strcmpi(p.szName, "Transfer-Encoding") && !mir_strcmpi(p.szValue, "chunked")) {
+ chunked = true;
+ chunkhdr = i;
+ dataLen = -1;
+ }
+ }
+
+ if (nlhrReply->resultCode >= 200 && (dataLen > 0 || (!isConnect && dataLen < 0))) {
+ int recvResult, chunksz = -1;
+ int dataBufferAlloced;
+
+ if (chunked) {
+ chunksz = NetlibHttpRecvChunkHeader(nlc, true, dflags | (cenctype ? MSG_NODUMP : 0));
+ if (chunksz == SOCKET_ERROR) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ dataLen = chunksz;
+ }
+ dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1;
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+
+ while (chunksz != 0) {
+ while (true) {
+ recvResult = RecvWithTimeoutTime(nlc, GetTickCount() + HTTPRECVDATATIMEOUT,
+ nlhrReply->pData + nlhrReply->dataLength,
+ dataBufferAlloced - nlhrReply->dataLength - 1,
+ dflags | (cenctype ? MSG_NODUMP : 0));
+
+ if (recvResult == 0) break;
+ if (recvResult == SOCKET_ERROR) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ nlhrReply->dataLength += recvResult;
+
+ if (dataLen >= 0) {
+ if (nlhrReply->dataLength >= dataLen)
+ break;
+ }
+ else if ((dataBufferAlloced - nlhrReply->dataLength) < 256) {
+ dataBufferAlloced += 2048;
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+ if (nlhrReply->pData == nullptr) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ }
+ Sleep(10);
+ }
+
+ if (!chunked)
+ break;
+
+ chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags | MSG_NODUMP);
+ if (chunksz == SOCKET_ERROR) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ dataLen += chunksz;
+ dataBufferAlloced += chunksz;
+
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+ }
+
+ nlhrReply->pData[nlhrReply->dataLength] = '\0';
+ }
+
+ if (chunked) {
+ nlhrReply->headers[chunkhdr].szName = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szName, 16);
+ mir_strcpy(nlhrReply->headers[chunkhdr].szName, "Content-Length");
+
+ nlhrReply->headers[chunkhdr].szValue = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szValue, 16);
+ mir_snprintf(nlhrReply->headers[chunkhdr].szValue, 16, "%u", nlhrReply->dataLength);
+ }
+
+ if (cenctype) {
+ int bufsz = nlhrReply->dataLength;
+ char* szData = nullptr;
+
+ switch (cenctype) {
+ case 1:
+ szData = gzip_decode(nlhrReply->pData, &bufsz, 0x10 | MAX_WBITS);
+ break;
+
+ case 2:
+ szData = gzip_decode(nlhrReply->pData, &bufsz, -MAX_WBITS);
+ if (bufsz < 0) {
+ bufsz = nlhrReply->dataLength;
+ szData = gzip_decode(nlhrReply->pData, &bufsz, MAX_WBITS);
+ }
+ break;
+ }
+
+ if (bufsz > 0) {
+ Netlib_Dump(nlc, (uint8_t*)szData, bufsz, false, dflags | MSG_NOTITLE);
+ mir_free(nlhrReply->pData);
+ nlhrReply->pData = szData;
+ nlhrReply->dataLength = bufsz;
+
+ mir_free(nlhrReply->headers[cenc].szName);
+ mir_free(nlhrReply->headers[cenc].szValue);
+ memmove(&nlhrReply->headers[cenc], &nlhrReply->headers[cenc+1], (--nlhrReply->headersCount-cenc)*sizeof(nlhrReply->headers[0]));
+ }
+ else if (bufsz == 0) {
+ mir_free(nlhrReply->pData);
+ nlhrReply->pData = nullptr;
+ nlhrReply->dataLength = 0;
+ }
+ }
+
+ if (close &&
+ (nlc->proxyType != PROXYTYPE_HTTP || nlc->url.flags & NLOCF_SSL) &&
+ (!isConnect || nlhrReply->resultCode != 200))
+ NetlibDoCloseSocket(nlc);
+
+ return nlhrReply;
+}
diff --git a/src/mir_app/src/netlib_httpproxy.cpp b/src/mir_app/src/netlib_httpproxy.cpp index 4ea58fcd57..66adc7fe9a 100644 --- a/src/mir_app/src/netlib_httpproxy.cpp +++ b/src/mir_app/src/netlib_httpproxy.cpp @@ -1,85 +1,85 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -typedef enum -{ - reqHelloGet, - reqOldGet, - reqOldPost, - reqNewPost, -} -RequestType; - -///////////////////////////////////////////////////////////////////////////////////////// - -#define NETLIBHTTP_RETRYCOUNT 3 -#define NETLIBHTTP_RETRYTIMEOUT 2000 - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN nlc, const NETLIBHTTPPROXYINFO *nlhpi) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == nullptr || nlhpi->szHttpPostUrl == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - mir_free(nlc->nlhpi.szHttpGetUrl); - mir_free(nlc->nlhpi.szHttpPostUrl); - - nlc->nlhpi.combinePackets = 1; - memcpy(&nlc->nlhpi, nlhpi, sizeof(*nlhpi)); - if (nlc->nlhpi.combinePackets == 0) - nlc->nlhpi.combinePackets = 1; - - nlc->nlhpi.szHttpGetUrl = mir_strdup(nlc->nlhpi.szHttpGetUrl); - nlc->nlhpi.szHttpPostUrl = mir_strdup(nlc->nlhpi.szHttpPostUrl); - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders) -{ - if (GetNetlibHandleType(nlu) != NLH_USER) - return ERROR_INVALID_PARAMETER; - - replaceStr(nlu->szStickyHeaders, szHeaders); // pointer is ours - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN nlc, int iTimeout) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) - return -1; - - int oldTimeout = nlc->pollingTimeout; - nlc->pollingTimeout = iTimeout; - return oldTimeout; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+typedef enum
+{
+ reqHelloGet,
+ reqOldGet,
+ reqOldPost,
+ reqNewPost,
+}
+RequestType;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define NETLIBHTTP_RETRYCOUNT 3
+#define NETLIBHTTP_RETRYTIMEOUT 2000
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN nlc, const NETLIBHTTPPROXYINFO *nlhpi)
+{
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == nullptr || nlhpi->szHttpPostUrl == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ mir_free(nlc->nlhpi.szHttpGetUrl);
+ mir_free(nlc->nlhpi.szHttpPostUrl);
+
+ nlc->nlhpi.combinePackets = 1;
+ memcpy(&nlc->nlhpi, nlhpi, sizeof(*nlhpi));
+ if (nlc->nlhpi.combinePackets == 0)
+ nlc->nlhpi.combinePackets = 1;
+
+ nlc->nlhpi.szHttpGetUrl = mir_strdup(nlc->nlhpi.szHttpGetUrl);
+ nlc->nlhpi.szHttpPostUrl = mir_strdup(nlc->nlhpi.szHttpPostUrl);
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders)
+{
+ if (GetNetlibHandleType(nlu) != NLH_USER)
+ return ERROR_INVALID_PARAMETER;
+
+ replaceStr(nlu->szStickyHeaders, szHeaders); // pointer is ours
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN nlc, int iTimeout)
+{
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION)
+ return -1;
+
+ int oldTimeout = nlc->pollingTimeout;
+ nlc->pollingTimeout = iTimeout;
+ return oldTimeout;
+}
diff --git a/src/mir_app/src/netlib_log.cpp b/src/mir_app/src/netlib_log.cpp index 4a1cea21df..1ddbb63f6f 100644 --- a/src/mir_app/src/netlib_log.cpp +++ b/src/mir_app/src/netlib_log.cpp @@ -1,578 +1,578 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#define MS_NETLIB_LOGWIN "Netlib/Log/Win" - -#define TIMEFORMAT_NONE 0 -#define TIMEFORMAT_HHMMSS 1 -#define TIMEFORMAT_MILLISECONDS 2 -#define TIMEFORMAT_MICROSECONDS 3 -struct { - HWND hwndOpts; - bool toOutputDebugString, toFile, toLog; - bool showUser, rotateLogs, bPrintDate; - bool dumpSent, dumpRecv, dumpProxy, dumpSsl; - bool textDumps, autoDetectText; - int timeFormat; - CMStringW tszFile, tszUserFile; -} -static logOptions = {}; - -struct LOGMSG -{ - const char* pszHead; - const char* pszMsg; -}; - -static __int64 mirandaStartTime, perfCounterFreq; -static int bIsActive = TRUE; -static HANDLE hLogEvent = nullptr; -static HANDLE hLogger = nullptr; - -static void InitLog() -{ - logOptions.dumpRecv = db_get_b(0, "Netlib", "DumpRecv", true) != 0; - logOptions.dumpSent = db_get_b(0, "Netlib", "DumpSent", true) != 0; - logOptions.dumpProxy = db_get_b(0, "Netlib", "DumpProxy", true) != 0; - logOptions.dumpSsl = db_get_b(0, "Netlib", "DumpSsl", false) != 0; - logOptions.textDumps = db_get_b(0, "Netlib", "TextDumps", true) != 0; - logOptions.autoDetectText = db_get_b(0, "Netlib", "AutoDetectText", true) != 0; - logOptions.bPrintDate = db_get_b(0, "Netlib", "PrintDate", false) != 0; - logOptions.timeFormat = db_get_b(0, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS); - logOptions.rotateLogs = db_get_b(0, "Netlib", "RotateLogs", false); - logOptions.showUser = db_get_b(0, "Netlib", "ShowUser", true) != 0; - logOptions.toOutputDebugString = db_get_b(0, "Netlib", "ToOutputDebugString", false) != 0; - logOptions.toFile = db_get_b(0, "Netlib", "ToFile", false) != 0; - logOptions.toLog = db_get_dw(0, "Netlib", "NLlog", true) != 0; - - if (hLogger) { - mir_closeLog(hLogger); - hLogger = nullptr; - } - - ptrW szBuf(db_get_wsa(0, "Netlib", "File")); - if (mir_wstrlen(szBuf)) { - logOptions.tszUserFile = szBuf.get(); - - wchar_t path[MAX_PATH]; - PathToAbsoluteW(VARSW(szBuf), path); - logOptions.tszFile = path; - } - else { - db_set_ws(0, "Netlib", "File", logOptions.tszUserFile = L"%miranda_logpath%\\netlog.txt"); - logOptions.tszFile = VARSW(logOptions.tszUserFile); - } - - if (logOptions.toFile) { - CMStringW wszFileName = logOptions.tszFile; - if (logOptions.rotateLogs) { - int iLogNumber = db_get_dw(0, "Netlib", "RotateId"); - wszFileName.AppendFormat(L".%d", iLogNumber); - db_set_dw(0, "Netlib", "RotateId", (iLogNumber + 1) % 10); - } - - hLogger = mir_createLog("Netlib", LPGENW("Standard Netlib log"), wszFileName, 0); - } -} - -static const wchar_t *szTimeFormats[] = -{ - LPGENW("No times"), - LPGENW("Standard hh:mm:ss times"), - LPGENW("Times in milliseconds"), - LPGENW("Times in microseconds") -}; - -class CLogOptionsDlg : public CDlgBase -{ - CCtrlEdit edtFileName; - CCtrlCombo cmbTimeFormat; - CCtrlButton btnRunNow, btnFileName, btnRunAtStart; - CCtrlTreeView treeFilter; - -public: - CLogOptionsDlg() : - CDlgBase(g_plugin, IDD_NETLIBLOGOPTS), - treeFilter(this, IDC_FILTER), - edtFileName(this, IDC_FILENAME), - cmbTimeFormat(this, IDC_TIMEFORMAT), - btnRunNow(this, IDC_RUNNOW), - btnFileName(this, IDC_FILENAMEBROWSE), - btnRunAtStart(this, IDC_RUNATSTARTBROWSE) - { - btnFileName.OnClick = btnRunAtStart.OnClick = Callback(this, &CLogOptionsDlg::onClick_Browse); - - edtFileName.OnChange = Callback(this, &CLogOptionsDlg::onChange_FileName); - } - - bool OnInitDialog() override - { - logOptions.hwndOpts = m_hwnd; - - CheckDlgButton(m_hwnd, IDC_TOFILE, logOptions.toFile ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPSSL, logOptions.dumpSsl ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPRECV, logOptions.dumpRecv ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPSENT, logOptions.dumpSent ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_SHOWDATE, logOptions.bPrintDate ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_SHOWNAMES, logOptions.showUser ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPPROXY, logOptions.dumpProxy ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_TEXTDUMPS, logOptions.textDumps ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_LOGROTATE, logOptions.rotateLogs ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_AUTODETECTTEXT, logOptions.autoDetectText ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_SHOWTHISDLGATSTART, db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_TOOUTPUTDEBUGSTRING, logOptions.toOutputDebugString ? BST_CHECKED : BST_UNCHECKED); - - for (auto &it : szTimeFormats) - cmbTimeFormat.AddString(TranslateW(it)); - cmbTimeFormat.SetCurSel(logOptions.timeFormat); - - edtFileName.SetText(logOptions.tszUserFile); - SetDlgItemText(m_hwnd, IDC_PATH, logOptions.tszFile); - - ptrA szRun(db_get_sa(0, "Netlib", "RunAtStart")); - if (szRun) - SetDlgItemTextA(m_hwnd, IDC_RUNATSTART, szRun); - - SetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE, GetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES)); - - TVINSERTSTRUCT tvis = {}; - tvis.hInsertAfter = TVI_SORT; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE; - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - - for (auto &it : netlibUser) { - tvis.item.pszText = it->user.szDescriptiveName.w; - tvis.item.lParam = netlibUser.indexOf(&it); - tvis.item.state = INDEXTOSTATEIMAGEMASK(it->toLog ? 2 : 1); - treeFilter.InsertItem(&tvis); - } - - tvis.item.lParam = -1; - tvis.item.pszText = TranslateT("(Miranda core logging)"); - tvis.item.state = INDEXTOSTATEIMAGEMASK((logOptions.toLog) ? 2 : 1); - treeFilter.InsertItem(&tvis); - return true; - } - - bool OnApply() override - { - wchar_t str[MAX_PATH]; - GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str)); - db_set_ws(0, "Netlib", "RunAtStart", str); - - edtFileName.GetText(str, _countof(str)); - logOptions.tszUserFile = rtrimw(str); - db_set_ws(0, "Netlib", "File", str); - - GetDlgItemText(m_hwnd, IDC_PATH, str, _countof(str)); - logOptions.tszFile = rtrimw(str); - - db_set_b(0, "Netlib", "ToFile", IsDlgButtonChecked(m_hwnd, IDC_TOFILE)); - db_set_b(0, "Netlib", "DumpSsl", IsDlgButtonChecked(m_hwnd, IDC_DUMPSSL)); - db_set_b(0, "Netlib", "DumpRecv", IsDlgButtonChecked(m_hwnd, IDC_DUMPRECV)); - db_set_b(0, "Netlib", "DumpSent", IsDlgButtonChecked(m_hwnd, IDC_DUMPSENT)); - db_set_b(0, "Netlib", "ShowUser", IsDlgButtonChecked(m_hwnd, IDC_SHOWNAMES)); - db_set_b(0, "Netlib", "DumpProxy", IsDlgButtonChecked(m_hwnd, IDC_DUMPPROXY)); - db_set_b(0, "Netlib", "PrintDate", IsDlgButtonChecked(m_hwnd, IDC_SHOWDATE)); - db_set_b(0, "Netlib", "TextDumps", IsDlgButtonChecked(m_hwnd, IDC_TEXTDUMPS)); - db_set_b(0, "Netlib", "RotateLogs", IsDlgButtonChecked(m_hwnd, IDC_LOGROTATE)); - db_set_b(0, "Netlib", "AutoDetectText", IsDlgButtonChecked(m_hwnd, IDC_AUTODETECTTEXT)); - db_set_b(0, "Netlib", "ShowLogOptsAtStart", IsDlgButtonChecked(m_hwnd, IDC_SHOWTHISDLGATSTART)); - db_set_b(0, "Netlib", "ToOutputDebugString", IsDlgButtonChecked(m_hwnd, IDC_TOOUTPUTDEBUGSTRING)); - - db_set_b(0, "Netlib", "TimeFormat", cmbTimeFormat.GetCurSel()); - - TVITEMEX tvi = {}; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT; - tvi.hItem = treeFilter.GetRoot(); - - while (tvi.hItem) { - treeFilter.GetItem(&tvi); - bool checked = ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2); - - if (tvi.lParam == -1) { - logOptions.toLog = checked; - db_set_dw(0, "Netlib", "NLlog", checked); - } - else if (tvi.lParam < netlibUser.getCount()) { - netlibUser[tvi.lParam]->toLog = checked; - db_set_dw(0, netlibUser[tvi.lParam]->user.szSettingsModule, "NLlog", checked); - } - - tvi.hItem = treeFilter.GetNextSibling(tvi.hItem); - } - - InitLog(); - return true; - } - - void OnDestroy() override - { - ImageList_Destroy(TreeView_GetImageList(GetDlgItem(m_hwnd, IDC_FILTER), TVSIL_STATE)); - logOptions.hwndOpts = nullptr; - } - - void onChange_FileName(CCtrlEdit *pEdit) - { - if (pEdit->GetHwnd() == GetFocus()) - CheckDlgButton(m_hwnd, IDC_TOFILE, BST_CHECKED); - - wchar_t path[MAX_PATH]; - pEdit->GetText(path, _countof(path)); - PathToAbsoluteW(VARSW(path), path); - SetDlgItemText(m_hwnd, IDC_PATH, path); - } - - void onClick_Browse(CCtrlButton *pButton) - { - wchar_t str[MAX_PATH]; - GetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str, _countof(str)); - - wchar_t filter[200]; - mir_snwprintf(filter, L"%s (*)%c*%c", TranslateT("All files"), 0, 0); - - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = m_hwnd; - ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT; - if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE) - ofn.lpstrTitle = TranslateT("Select where log file will be created"); - else { - ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; - ofn.lpstrTitle = TranslateT("Select program to be run"); - } - ofn.lpstrFilter = filter; - ofn.lpstrFile = str; - ofn.nMaxFile = _countof(str) - 2; - ofn.nMaxFileTitle = MAX_PATH; - if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE) { - if (!GetSaveFileName(&ofn)) - return; - } - else if (!GetOpenFileName(&ofn)) - return; - - if (pButton->GetCtrlId() == IDC_RUNATSTARTBROWSE && wcschr(str, ' ') != nullptr) { - memmove(str + 1, str, ((_countof(str) - 2) * sizeof(wchar_t))); - str[0] = '"'; - mir_wstrcat(str, L"\""); - } - SetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str); - } - - void onClick_RunNow(CCtrlButton *) - { - wchar_t str[MAX_PATH]; - GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str)); - if (!str[0]) - return; - - STARTUPINFO si = { sizeof(si) }; - PROCESS_INFORMATION pi; - CreateProcessW(nullptr, str, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } -}; - -void NetlibLogShowOptions(void) -{ - if (logOptions.hwndOpts == nullptr) - (new CLogOptionsDlg())->Create(); - SetForegroundWindow(logOptions.hwndOpts); -} - -static INT_PTR ShowOptions(WPARAM, LPARAM) -{ - NetlibLogShowOptions(); - return 0; -} - -int NetlibLog_Worker(NetlibUser *nlu, const char *pszMsg, int flags) -{ - if (!bIsActive) - return 0; - - uint32_t dwOriginalLastError = GetLastError(); - - if ((nlu != nullptr && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - /* if the Netlib user handle is nullptr, just pretend its not */ - if (!(nlu != nullptr ? nlu->toLog : logOptions.toLog)) - return 1; - - LARGE_INTEGER liTimeNow; - char szDate[32], szTime[32], szHead[128]; - switch (logOptions.timeFormat) { - case TIMEFORMAT_HHMMSS: - GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, nullptr, nullptr, szTime, _countof(szTime)); - mir_strcat(szTime, " "); - break; - - case TIMEFORMAT_MILLISECONDS: - QueryPerformanceCounter(&liTimeNow); - liTimeNow.QuadPart -= mirandaStartTime; - mir_snprintf(szTime, "%I64u.%03I64u ", liTimeNow.QuadPart / perfCounterFreq, - 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); - break; - - case TIMEFORMAT_MICROSECONDS: - QueryPerformanceCounter(&liTimeNow); - liTimeNow.QuadPart -= mirandaStartTime; - mir_snprintf(szTime, "%I64u.%06I64u ", liTimeNow.QuadPart / perfCounterFreq, - 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); - break; - - default: - szTime[0] = 0; - break; - } - - if (logOptions.bPrintDate) { - GetDateFormatA(LOCALE_USER_DEFAULT, 0, nullptr, "yyyy-MM-dd", szDate, _countof(szDate)); - mir_strcat(szDate, " "); - } - else szDate[0] = 0; - - if (flags & MSG_NOTITLE) - szHead[0] = 0; - else { - char *szUser = (logOptions.showUser) ? (nlu == nullptr ? nullptr : nlu->user.szSettingsModule) : nullptr; - if (szUser) - mir_snprintf(szHead, "[%s%s%04X] [%s] ", szDate, szTime, GetCurrentThreadId(), szUser); - else - mir_snprintf(szHead, "[%s%s%04X] ", szDate, szTime, GetCurrentThreadId()); - } - - if (logOptions.toOutputDebugString) { - if (szHead[0]) - OutputDebugStringA(szHead); - OutputDebugStringA(pszMsg); - OutputDebugStringA("\n"); - } - - if (logOptions.toFile && !logOptions.tszFile.IsEmpty()) { - size_t len = mir_strlen(pszMsg); - mir_writeLogA(hLogger, "%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n"); - } - - LOGMSG logMsg = { szHead, pszMsg }; - NotifyFastHook(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg); - - SetLastError(dwOriginalLastError); - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void PROTO_INTERFACE::debugLogA(const char *szFormat, ...) -{ - char buf[4096]; - va_list args; - va_start(args, szFormat); - int res = _vsnprintf(buf, _countof(buf), szFormat, args); - NetlibLog_Worker(m_hNetlibUser, (res != -1) ? buf : CMStringA().FormatV(szFormat, args), 0); - va_end(args); -} - -void PROTO_INTERFACE::debugLogW(const wchar_t *wszFormat, ...) -{ - wchar_t buf[4096]; - va_list args; - va_start(args, wszFormat); - int res = _vsnwprintf(buf, _countof(buf), wszFormat, args); - NetlibLog_Worker(m_hNetlibUser, ptrA(mir_utf8encodeW((res != -1) ? buf : CMStringW().FormatV(wszFormat, args))), 0); - va_end(args); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...) -{ - va_list va; - va_start(va, fmt); - char szText[8000]; - mir_vsnprintf(szText, _countof(szText), fmt, va); - va_end(va); - return NetlibLog_Worker(hUser, szText, 0); -} - -MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...) -{ - va_list va; - va_start(va, fmt); - wchar_t szText[8000]; - mir_vsnwprintf(szText, _countof(szText), fmt, va); - va_end(va); - return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(szText)), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr) -{ - return NetlibLog_Worker(hUser, pszStr, 0); -} - -MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr) -{ - return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(pwszStr)), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Netlib_Dump(HNETLIBCONN nlc, const void *pBuf, size_t len, bool bIsSent, int flags) -{ - // This section checks a number of conditions and aborts - // the dump if the data should not be written to the log - - // Check packet flags - if (flags & (MSG_PEEK | MSG_NODUMP)) - return; - - // Check user's log settings - if (!(logOptions.toOutputDebugString || GetSubscribersCount((THook*)hLogEvent) != 0 || (logOptions.toFile && !logOptions.tszFile.IsEmpty()))) - return; - if ((bIsSent && !logOptions.dumpSent) || (!bIsSent && !logOptions.dumpRecv)) - return; - if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy) - return; - if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl) - return; - - NetlibUser *nlu; - CMStringA str; - { - mir_cslock lock(csConnectionHeader); - - nlu = nlc ? nlc->nlu : nullptr; - if (!(flags & MSG_NOTITLE)) - str.Format("(%p:%u) Data %s%s\r\n", nlc, nlc ? (int)nlc->s : 0, bIsSent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : ""); - } - - // check filter settings - if (nlu == nullptr) { - if (!logOptions.toLog) - return; - } - else if (!nlu->toLog) - return; - - const uint8_t *buf = (const uint8_t *)pBuf; - - bool isText = true; - if (!logOptions.textDumps) - isText = false; - else if (!(flags & MSG_DUMPASTEXT)) { - if (logOptions.autoDetectText) { - for (size_t i = 0; i < len; i++) { - if ((buf[i] < ' ' && buf[i] != '\t' && buf[i] != '\r' && buf[i] != '\n') || buf[i] >= 0x80) { - isText = false; - break; - } - } - } - else isText = false; - } - - // Text data - if (isText) { - str.Append((const char*)buf, (int)len); - } - // Binary data - else { - for (int line = 0;; line += 16) { - auto *p = buf + line; - int colsInLine = min(16, (int)len - line); - if (colsInLine == 16) - str.AppendFormat("%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ", - line, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); - else { - str.AppendFormat("%08X: ", line); - - // Dump data as hex - int col; - for (col = 0; col < colsInLine; col++) - str.AppendFormat("%02X%c", p[col], ((col & 3) == 3) ? '-' : ' '); - - // Fill out last line with blanks - for (; col < 16; col++) - str.Append(" "); - - str.AppendChar(' '); - } - - for (int col = 0; col < colsInLine; col++) - str.AppendChar((p[col] < ' ') ? '.' : p[col]); - - if (len - line <= 16) - break; - - str.AppendChar('\r'); // End each line with a break - str.AppendChar('\n'); // End each line with a break - } - } - - NetlibLog_Worker(nlu, str, flags); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void NetlibLogInit(void) -{ - LARGE_INTEGER li; - QueryPerformanceFrequency(&li); - perfCounterFreq = li.QuadPart; - QueryPerformanceCounter(&li); - mirandaStartTime = li.QuadPart; - - CreateServiceFunction(MS_NETLIB_LOGWIN, ShowOptions); - hLogEvent = CreateHookableEvent(ME_NETLIB_FASTDUMP); - - InitLog(); - - if (db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0)) - NetlibLogShowOptions(); - - ptrW szBuf(db_get_wsa(0, "Netlib", "RunAtStart")); - if (szBuf) { - STARTUPINFO si = { sizeof(si) }; - PROCESS_INFORMATION pi; - CreateProcess(nullptr, szBuf, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); - } -} - -void NetlibLogShutdown(void) -{ - bIsActive = FALSE; - DestroyHookableEvent(hLogEvent); hLogEvent = nullptr; - if (IsWindow(logOptions.hwndOpts)) - DestroyWindow(logOptions.hwndOpts); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#define MS_NETLIB_LOGWIN "Netlib/Log/Win"
+
+#define TIMEFORMAT_NONE 0
+#define TIMEFORMAT_HHMMSS 1
+#define TIMEFORMAT_MILLISECONDS 2
+#define TIMEFORMAT_MICROSECONDS 3
+struct {
+ HWND hwndOpts;
+ bool toOutputDebugString, toFile, toLog;
+ bool showUser, rotateLogs, bPrintDate;
+ bool dumpSent, dumpRecv, dumpProxy, dumpSsl;
+ bool textDumps, autoDetectText;
+ int timeFormat;
+ CMStringW tszFile, tszUserFile;
+}
+static logOptions = {};
+
+struct LOGMSG
+{
+ const char* pszHead;
+ const char* pszMsg;
+};
+
+static __int64 mirandaStartTime, perfCounterFreq;
+static int bIsActive = TRUE;
+static HANDLE hLogEvent = nullptr;
+static HANDLE hLogger = nullptr;
+
+static void InitLog()
+{
+ logOptions.dumpRecv = db_get_b(0, "Netlib", "DumpRecv", true) != 0;
+ logOptions.dumpSent = db_get_b(0, "Netlib", "DumpSent", true) != 0;
+ logOptions.dumpProxy = db_get_b(0, "Netlib", "DumpProxy", true) != 0;
+ logOptions.dumpSsl = db_get_b(0, "Netlib", "DumpSsl", false) != 0;
+ logOptions.textDumps = db_get_b(0, "Netlib", "TextDumps", true) != 0;
+ logOptions.autoDetectText = db_get_b(0, "Netlib", "AutoDetectText", true) != 0;
+ logOptions.bPrintDate = db_get_b(0, "Netlib", "PrintDate", false) != 0;
+ logOptions.timeFormat = db_get_b(0, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS);
+ logOptions.rotateLogs = db_get_b(0, "Netlib", "RotateLogs", false);
+ logOptions.showUser = db_get_b(0, "Netlib", "ShowUser", true) != 0;
+ logOptions.toOutputDebugString = db_get_b(0, "Netlib", "ToOutputDebugString", false) != 0;
+ logOptions.toFile = db_get_b(0, "Netlib", "ToFile", false) != 0;
+ logOptions.toLog = db_get_dw(0, "Netlib", "NLlog", true) != 0;
+
+ if (hLogger) {
+ mir_closeLog(hLogger);
+ hLogger = nullptr;
+ }
+
+ ptrW szBuf(db_get_wsa(0, "Netlib", "File"));
+ if (mir_wstrlen(szBuf)) {
+ logOptions.tszUserFile = szBuf.get();
+
+ wchar_t path[MAX_PATH];
+ PathToAbsoluteW(VARSW(szBuf), path);
+ logOptions.tszFile = path;
+ }
+ else {
+ db_set_ws(0, "Netlib", "File", logOptions.tszUserFile = L"%miranda_logpath%\\netlog.txt");
+ logOptions.tszFile = VARSW(logOptions.tszUserFile);
+ }
+
+ if (logOptions.toFile) {
+ CMStringW wszFileName = logOptions.tszFile;
+ if (logOptions.rotateLogs) {
+ int iLogNumber = db_get_dw(0, "Netlib", "RotateId");
+ wszFileName.AppendFormat(L".%d", iLogNumber);
+ db_set_dw(0, "Netlib", "RotateId", (iLogNumber + 1) % 10);
+ }
+
+ hLogger = mir_createLog("Netlib", LPGENW("Standard Netlib log"), wszFileName, 0);
+ }
+}
+
+static const wchar_t *szTimeFormats[] =
+{
+ LPGENW("No times"),
+ LPGENW("Standard hh:mm:ss times"),
+ LPGENW("Times in milliseconds"),
+ LPGENW("Times in microseconds")
+};
+
+class CLogOptionsDlg : public CDlgBase
+{
+ CCtrlEdit edtFileName;
+ CCtrlCombo cmbTimeFormat;
+ CCtrlButton btnRunNow, btnFileName, btnRunAtStart;
+ CCtrlTreeView treeFilter;
+
+public:
+ CLogOptionsDlg() :
+ CDlgBase(g_plugin, IDD_NETLIBLOGOPTS),
+ treeFilter(this, IDC_FILTER),
+ edtFileName(this, IDC_FILENAME),
+ cmbTimeFormat(this, IDC_TIMEFORMAT),
+ btnRunNow(this, IDC_RUNNOW),
+ btnFileName(this, IDC_FILENAMEBROWSE),
+ btnRunAtStart(this, IDC_RUNATSTARTBROWSE)
+ {
+ btnFileName.OnClick = btnRunAtStart.OnClick = Callback(this, &CLogOptionsDlg::onClick_Browse);
+
+ edtFileName.OnChange = Callback(this, &CLogOptionsDlg::onChange_FileName);
+ }
+
+ bool OnInitDialog() override
+ {
+ logOptions.hwndOpts = m_hwnd;
+
+ CheckDlgButton(m_hwnd, IDC_TOFILE, logOptions.toFile ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_DUMPSSL, logOptions.dumpSsl ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_DUMPRECV, logOptions.dumpRecv ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_DUMPSENT, logOptions.dumpSent ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_SHOWDATE, logOptions.bPrintDate ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_SHOWNAMES, logOptions.showUser ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_DUMPPROXY, logOptions.dumpProxy ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_TEXTDUMPS, logOptions.textDumps ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_LOGROTATE, logOptions.rotateLogs ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_AUTODETECTTEXT, logOptions.autoDetectText ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_SHOWTHISDLGATSTART, db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_TOOUTPUTDEBUGSTRING, logOptions.toOutputDebugString ? BST_CHECKED : BST_UNCHECKED);
+
+ for (auto &it : szTimeFormats)
+ cmbTimeFormat.AddString(TranslateW(it));
+ cmbTimeFormat.SetCurSel(logOptions.timeFormat);
+
+ edtFileName.SetText(logOptions.tszUserFile);
+ SetDlgItemText(m_hwnd, IDC_PATH, logOptions.tszFile);
+
+ ptrA szRun(db_get_sa(0, "Netlib", "RunAtStart"));
+ if (szRun)
+ SetDlgItemTextA(m_hwnd, IDC_RUNATSTART, szRun);
+
+ SetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE, GetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES));
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.hInsertAfter = TVI_SORT;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+
+ for (auto &it : netlibUser) {
+ tvis.item.pszText = it->user.szDescriptiveName.w;
+ tvis.item.lParam = netlibUser.indexOf(&it);
+ tvis.item.state = INDEXTOSTATEIMAGEMASK(it->toLog ? 2 : 1);
+ treeFilter.InsertItem(&tvis);
+ }
+
+ tvis.item.lParam = -1;
+ tvis.item.pszText = TranslateT("(Miranda core logging)");
+ tvis.item.state = INDEXTOSTATEIMAGEMASK((logOptions.toLog) ? 2 : 1);
+ treeFilter.InsertItem(&tvis);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ wchar_t str[MAX_PATH];
+ GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str));
+ db_set_ws(0, "Netlib", "RunAtStart", str);
+
+ edtFileName.GetText(str, _countof(str));
+ logOptions.tszUserFile = rtrimw(str);
+ db_set_ws(0, "Netlib", "File", str);
+
+ GetDlgItemText(m_hwnd, IDC_PATH, str, _countof(str));
+ logOptions.tszFile = rtrimw(str);
+
+ db_set_b(0, "Netlib", "ToFile", IsDlgButtonChecked(m_hwnd, IDC_TOFILE));
+ db_set_b(0, "Netlib", "DumpSsl", IsDlgButtonChecked(m_hwnd, IDC_DUMPSSL));
+ db_set_b(0, "Netlib", "DumpRecv", IsDlgButtonChecked(m_hwnd, IDC_DUMPRECV));
+ db_set_b(0, "Netlib", "DumpSent", IsDlgButtonChecked(m_hwnd, IDC_DUMPSENT));
+ db_set_b(0, "Netlib", "ShowUser", IsDlgButtonChecked(m_hwnd, IDC_SHOWNAMES));
+ db_set_b(0, "Netlib", "DumpProxy", IsDlgButtonChecked(m_hwnd, IDC_DUMPPROXY));
+ db_set_b(0, "Netlib", "PrintDate", IsDlgButtonChecked(m_hwnd, IDC_SHOWDATE));
+ db_set_b(0, "Netlib", "TextDumps", IsDlgButtonChecked(m_hwnd, IDC_TEXTDUMPS));
+ db_set_b(0, "Netlib", "RotateLogs", IsDlgButtonChecked(m_hwnd, IDC_LOGROTATE));
+ db_set_b(0, "Netlib", "AutoDetectText", IsDlgButtonChecked(m_hwnd, IDC_AUTODETECTTEXT));
+ db_set_b(0, "Netlib", "ShowLogOptsAtStart", IsDlgButtonChecked(m_hwnd, IDC_SHOWTHISDLGATSTART));
+ db_set_b(0, "Netlib", "ToOutputDebugString", IsDlgButtonChecked(m_hwnd, IDC_TOOUTPUTDEBUGSTRING));
+
+ db_set_b(0, "Netlib", "TimeFormat", cmbTimeFormat.GetCurSel());
+
+ TVITEMEX tvi = {};
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT;
+ tvi.hItem = treeFilter.GetRoot();
+
+ while (tvi.hItem) {
+ treeFilter.GetItem(&tvi);
+ bool checked = ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2);
+
+ if (tvi.lParam == -1) {
+ logOptions.toLog = checked;
+ db_set_dw(0, "Netlib", "NLlog", checked);
+ }
+ else if (tvi.lParam < netlibUser.getCount()) {
+ netlibUser[tvi.lParam]->toLog = checked;
+ db_set_dw(0, netlibUser[tvi.lParam]->user.szSettingsModule, "NLlog", checked);
+ }
+
+ tvi.hItem = treeFilter.GetNextSibling(tvi.hItem);
+ }
+
+ InitLog();
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ ImageList_Destroy(TreeView_GetImageList(GetDlgItem(m_hwnd, IDC_FILTER), TVSIL_STATE));
+ logOptions.hwndOpts = nullptr;
+ }
+
+ void onChange_FileName(CCtrlEdit *pEdit)
+ {
+ if (pEdit->GetHwnd() == GetFocus())
+ CheckDlgButton(m_hwnd, IDC_TOFILE, BST_CHECKED);
+
+ wchar_t path[MAX_PATH];
+ pEdit->GetText(path, _countof(path));
+ PathToAbsoluteW(VARSW(path), path);
+ SetDlgItemText(m_hwnd, IDC_PATH, path);
+ }
+
+ void onClick_Browse(CCtrlButton *pButton)
+ {
+ wchar_t str[MAX_PATH];
+ GetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str, _countof(str));
+
+ wchar_t filter[200];
+ mir_snwprintf(filter, L"%s (*)%c*%c", TranslateT("All files"), 0, 0);
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = m_hwnd;
+ ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT;
+ if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE)
+ ofn.lpstrTitle = TranslateT("Select where log file will be created");
+ else {
+ ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
+ ofn.lpstrTitle = TranslateT("Select program to be run");
+ }
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = str;
+ ofn.nMaxFile = _countof(str) - 2;
+ ofn.nMaxFileTitle = MAX_PATH;
+ if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE) {
+ if (!GetSaveFileName(&ofn))
+ return;
+ }
+ else if (!GetOpenFileName(&ofn))
+ return;
+
+ if (pButton->GetCtrlId() == IDC_RUNATSTARTBROWSE && wcschr(str, ' ') != nullptr) {
+ memmove(str + 1, str, ((_countof(str) - 2) * sizeof(wchar_t)));
+ str[0] = '"';
+ mir_wstrcat(str, L"\"");
+ }
+ SetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str);
+ }
+
+ void onClick_RunNow(CCtrlButton *)
+ {
+ wchar_t str[MAX_PATH];
+ GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str));
+ if (!str[0])
+ return;
+
+ STARTUPINFO si = { sizeof(si) };
+ PROCESS_INFORMATION pi;
+ CreateProcessW(nullptr, str, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+};
+
+void NetlibLogShowOptions(void)
+{
+ if (logOptions.hwndOpts == nullptr)
+ (new CLogOptionsDlg())->Create();
+ SetForegroundWindow(logOptions.hwndOpts);
+}
+
+static INT_PTR ShowOptions(WPARAM, LPARAM)
+{
+ NetlibLogShowOptions();
+ return 0;
+}
+
+int NetlibLog_Worker(NetlibUser *nlu, const char *pszMsg, int flags)
+{
+ if (!bIsActive)
+ return 0;
+
+ uint32_t dwOriginalLastError = GetLastError();
+
+ if ((nlu != nullptr && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ /* if the Netlib user handle is nullptr, just pretend its not */
+ if (!(nlu != nullptr ? nlu->toLog : logOptions.toLog))
+ return 1;
+
+ LARGE_INTEGER liTimeNow;
+ char szDate[32], szTime[32], szHead[128];
+ switch (logOptions.timeFormat) {
+ case TIMEFORMAT_HHMMSS:
+ GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, nullptr, nullptr, szTime, _countof(szTime));
+ mir_strcat(szTime, " ");
+ break;
+
+ case TIMEFORMAT_MILLISECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, "%I64u.%03I64u ", liTimeNow.QuadPart / perfCounterFreq,
+ 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ case TIMEFORMAT_MICROSECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, "%I64u.%06I64u ", liTimeNow.QuadPart / perfCounterFreq,
+ 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ default:
+ szTime[0] = 0;
+ break;
+ }
+
+ if (logOptions.bPrintDate) {
+ GetDateFormatA(LOCALE_USER_DEFAULT, 0, nullptr, "yyyy-MM-dd", szDate, _countof(szDate));
+ mir_strcat(szDate, " ");
+ }
+ else szDate[0] = 0;
+
+ if (flags & MSG_NOTITLE)
+ szHead[0] = 0;
+ else {
+ char *szUser = (logOptions.showUser) ? (nlu == nullptr ? nullptr : nlu->user.szSettingsModule) : nullptr;
+ if (szUser)
+ mir_snprintf(szHead, "[%s%s%04X] [%s] ", szDate, szTime, GetCurrentThreadId(), szUser);
+ else
+ mir_snprintf(szHead, "[%s%s%04X] ", szDate, szTime, GetCurrentThreadId());
+ }
+
+ if (logOptions.toOutputDebugString) {
+ if (szHead[0])
+ OutputDebugStringA(szHead);
+ OutputDebugStringA(pszMsg);
+ OutputDebugStringA("\n");
+ }
+
+ if (logOptions.toFile && !logOptions.tszFile.IsEmpty()) {
+ size_t len = mir_strlen(pszMsg);
+ mir_writeLogA(hLogger, "%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n");
+ }
+
+ LOGMSG logMsg = { szHead, pszMsg };
+ NotifyFastHook(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg);
+
+ SetLastError(dwOriginalLastError);
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void PROTO_INTERFACE::debugLogA(const char *szFormat, ...)
+{
+ char buf[4096];
+ va_list args;
+ va_start(args, szFormat);
+ int res = _vsnprintf(buf, _countof(buf), szFormat, args);
+ NetlibLog_Worker(m_hNetlibUser, (res != -1) ? buf : CMStringA().FormatV(szFormat, args), 0);
+ va_end(args);
+}
+
+void PROTO_INTERFACE::debugLogW(const wchar_t *wszFormat, ...)
+{
+ wchar_t buf[4096];
+ va_list args;
+ va_start(args, wszFormat);
+ int res = _vsnwprintf(buf, _countof(buf), wszFormat, args);
+ NetlibLog_Worker(m_hNetlibUser, ptrA(mir_utf8encodeW((res != -1) ? buf : CMStringW().FormatV(wszFormat, args))), 0);
+ va_end(args);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ char szText[8000];
+ mir_vsnprintf(szText, _countof(szText), fmt, va);
+ va_end(va);
+ return NetlibLog_Worker(hUser, szText, 0);
+}
+
+MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ wchar_t szText[8000];
+ mir_vsnwprintf(szText, _countof(szText), fmt, va);
+ va_end(va);
+ return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(szText)), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr)
+{
+ return NetlibLog_Worker(hUser, pszStr, 0);
+}
+
+MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr)
+{
+ return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(pwszStr)), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(void) Netlib_Dump(HNETLIBCONN nlc, const void *pBuf, size_t len, bool bIsSent, int flags)
+{
+ // This section checks a number of conditions and aborts
+ // the dump if the data should not be written to the log
+
+ // Check packet flags
+ if (flags & (MSG_PEEK | MSG_NODUMP))
+ return;
+
+ // Check user's log settings
+ if (!(logOptions.toOutputDebugString || GetSubscribersCount((THook*)hLogEvent) != 0 || (logOptions.toFile && !logOptions.tszFile.IsEmpty())))
+ return;
+ if ((bIsSent && !logOptions.dumpSent) || (!bIsSent && !logOptions.dumpRecv))
+ return;
+ if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy)
+ return;
+ if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl)
+ return;
+
+ NetlibUser *nlu;
+ CMStringA str;
+ {
+ mir_cslock lock(csConnectionHeader);
+
+ nlu = nlc ? nlc->nlu : nullptr;
+ if (!(flags & MSG_NOTITLE))
+ str.Format("(%p:%u) Data %s%s\r\n", nlc, nlc ? (int)nlc->s : 0, bIsSent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : "");
+ }
+
+ // check filter settings
+ if (nlu == nullptr) {
+ if (!logOptions.toLog)
+ return;
+ }
+ else if (!nlu->toLog)
+ return;
+
+ const uint8_t *buf = (const uint8_t *)pBuf;
+
+ bool isText = true;
+ if (!logOptions.textDumps)
+ isText = false;
+ else if (!(flags & MSG_DUMPASTEXT)) {
+ if (logOptions.autoDetectText) {
+ for (size_t i = 0; i < len; i++) {
+ if ((buf[i] < ' ' && buf[i] != '\t' && buf[i] != '\r' && buf[i] != '\n') || buf[i] >= 0x80) {
+ isText = false;
+ break;
+ }
+ }
+ }
+ else isText = false;
+ }
+
+ // Text data
+ if (isText) {
+ str.Append((const char*)buf, (int)len);
+ }
+ // Binary data
+ else {
+ for (int line = 0;; line += 16) {
+ auto *p = buf + line;
+ int colsInLine = min(16, (int)len - line);
+ if (colsInLine == 16)
+ str.AppendFormat("%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ",
+ line, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ else {
+ str.AppendFormat("%08X: ", line);
+
+ // Dump data as hex
+ int col;
+ for (col = 0; col < colsInLine; col++)
+ str.AppendFormat("%02X%c", p[col], ((col & 3) == 3) ? '-' : ' ');
+
+ // Fill out last line with blanks
+ for (; col < 16; col++)
+ str.Append(" ");
+
+ str.AppendChar(' ');
+ }
+
+ for (int col = 0; col < colsInLine; col++)
+ str.AppendChar((p[col] < ' ') ? '.' : p[col]);
+
+ if (len - line <= 16)
+ break;
+
+ str.AppendChar('\r'); // End each line with a break
+ str.AppendChar('\n'); // End each line with a break
+ }
+ }
+
+ NetlibLog_Worker(nlu, str, flags);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NetlibLogInit(void)
+{
+ LARGE_INTEGER li;
+ QueryPerformanceFrequency(&li);
+ perfCounterFreq = li.QuadPart;
+ QueryPerformanceCounter(&li);
+ mirandaStartTime = li.QuadPart;
+
+ CreateServiceFunction(MS_NETLIB_LOGWIN, ShowOptions);
+ hLogEvent = CreateHookableEvent(ME_NETLIB_FASTDUMP);
+
+ InitLog();
+
+ if (db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0))
+ NetlibLogShowOptions();
+
+ ptrW szBuf(db_get_wsa(0, "Netlib", "RunAtStart"));
+ if (szBuf) {
+ STARTUPINFO si = { sizeof(si) };
+ PROCESS_INFORMATION pi;
+ CreateProcess(nullptr, szBuf, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
+ }
+}
+
+void NetlibLogShutdown(void)
+{
+ bIsActive = FALSE;
+ DestroyHookableEvent(hLogEvent); hLogEvent = nullptr;
+ if (IsWindow(logOptions.hwndOpts))
+ DestroyWindow(logOptions.hwndOpts);
+}
diff --git a/src/mir_app/src/netlib_openconn.cpp b/src/mir_app/src/netlib_openconn.cpp index 49e584e4d3..7d9b0b9114 100644 --- a/src/mir_app/src/netlib_openconn.cpp +++ b/src/mir_app/src/netlib_openconn.cpp @@ -1,726 +1,726 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -extern mir_cs csNetlibUser; -extern uint32_t g_LastConnectionTick; -extern int connectionTimeout; -static int iUPnPCleanup = 0; - -#define RECV_DEFAULT_TIMEOUT 60000 - -//returns in network byte order -uint32_t DnsLookup(NetlibUser *nlu, const char *szHost) -{ - HOSTENT* host; - uint32_t ip = inet_addr(szHost); - if (ip != INADDR_NONE) - return ip; - - __try { - host = gethostbyname(szHost); - if (host) - return *(u_long*)host->h_addr_list[0]; - - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "gethostbyname", szHost, WSAGetLastError()); - } - __except (EXCEPTION_EXECUTE_HANDLER) {} - - return 0; -} - -int WaitUntilReadable(SOCKET s, uint32_t dwTimeout, bool check) -{ - fd_set readfd; - TIMEVAL tv; - - if (s == INVALID_SOCKET) return SOCKET_ERROR; - - tv.tv_sec = dwTimeout / 1000; - tv.tv_usec = (dwTimeout % 1000) * 1000; - - FD_ZERO(&readfd); - FD_SET(s, &readfd); - - int result = select(0, &readfd, nullptr, nullptr, &tv); - if (result == 0 && !check) SetLastError(ERROR_TIMEOUT); - return result; -} - -int WaitUntilWritable(SOCKET s, uint32_t dwTimeout) -{ - fd_set writefd; - TIMEVAL tv; - - tv.tv_sec = dwTimeout / 1000; - tv.tv_usec = (dwTimeout % 1000) * 1000; - - FD_ZERO(&writefd); - FD_SET(s, &writefd); - - switch (select(0, nullptr, &writefd, nullptr, &tv)) { - case 0: - SetLastError(ERROR_TIMEOUT); - case SOCKET_ERROR: - return 0; - } - return 1; -} - -bool RecvUntilTimeout(NetlibConnection *nlc, char *buf, int len, int flags, uint32_t dwTimeout) -{ - int nReceived = 0; - uint32_t dwTimeNow, dwCompleteTime = GetTickCount() + dwTimeout; - - while ((dwTimeNow = GetTickCount()) < dwCompleteTime) { - if (WaitUntilReadable(nlc->s, dwCompleteTime - dwTimeNow) <= 0) return false; - nReceived = Netlib_Recv(nlc, buf, len, flags); - if (nReceived <= 0) return false; - - buf += nReceived; - len -= nReceived; - if (len <= 0) return true; - } - SetLastError(ERROR_TIMEOUT); - return false; -} - -static int NetlibInitSocks4Connection(NetlibConnection *nlc) -{ - // http://www.socks.nec.com/protocol/socks4.protocol and http://www.socks.nec.com/protocol/socks4a.protocol - NetlibUrl &url = nlc->url; - if (url.szHost.IsEmpty()) - return 0; - - NetlibUser *nlu = nlc->nlu; - size_t nHostLen = mir_strlen(url.szHost) + 1; - size_t nUserLen = nlu->settings.szProxyAuthUser ? mir_strlen(nlu->settings.szProxyAuthUser) + 1 : 1; - size_t len = 8 + nUserLen; - - char* pInit = (char*)alloca(len + nHostLen); - pInit[0] = 4; // SOCKS4 - pInit[1] = 1; //connect - *(PWORD)&pInit[2] = htons(url.port); - - if (nUserLen <= 1) pInit[8] = 0; - else memcpy(&pInit[8], nlu->settings.szProxyAuthUser, nUserLen); - - //if cannot resolve host, try resolving through proxy (requires SOCKS4a) - uint32_t ip = DnsLookup(nlu, url.szHost); - *(PDWORD)&pInit[4] = ip ? ip : 0x01000000; - if (!ip) { - memcpy(&pInit[len], url.szHost, nHostLen); - len += nHostLen; - } - - if (Netlib_Send(nlc, pInit, (int)len, MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - return 0; - } - - char reply[8]; - if (!RecvUntilTimeout(nlc, reply, sizeof(reply), MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - switch ((uint8_t)reply[1]) { - case 90: return 1; - case 91: SetLastError(ERROR_ACCESS_DENIED); break; - case 92: SetLastError(ERROR_CONNECTION_UNAVAIL); break; - case 93: SetLastError(ERROR_INVALID_ACCESS); break; - default: SetLastError(ERROR_INVALID_DATA); break; - } - Netlib_Logf(nlu, "%s %d: Proxy connection failed (%x %u)", __FILE__, __LINE__, (uint8_t)reply[1], GetLastError()); - return 0; -} - -static int NetlibInitSocks5Connection(NetlibConnection *nlc) -{ - //rfc1928 - uint8_t buf[258]; - NetlibUser *nlu = nlc->nlu; - - buf[0] = 5; //yep, socks5 - buf[1] = 1; //one auth method - buf[2] = nlu->settings.useProxyAuth ? 2 : 0; - if (Netlib_Send(nlc, (char*)buf, 3, MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - return 0; - } - - //confirmation of auth method - if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - if ((buf[1] != 0 && buf[1] != 2)) { - SetLastError(ERROR_INVALID_ID_AUTHORITY); - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Recv", GetLastError()); - return 0; - } - - if (buf[1] == 2) { //rfc1929 - size_t nUserLen = mir_strlen(nlu->settings.szProxyAuthUser); - size_t nPassLen = mir_strlen(nlu->settings.szProxyAuthPassword); - uint8_t *pAuthBuf = (uint8_t*)mir_alloc(3 + nUserLen + nPassLen); - pAuthBuf[0] = 1; //auth version - pAuthBuf[1] = (uint8_t)nUserLen; - memcpy(pAuthBuf + 2, nlu->settings.szProxyAuthUser, nUserLen); - pAuthBuf[2 + nUserLen] = (uint8_t)nPassLen; - memcpy(pAuthBuf + 3 + nUserLen, nlu->settings.szProxyAuthPassword, nPassLen); - if (Netlib_Send(nlc, (char*)pAuthBuf, int(3 + nUserLen + nPassLen), MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - mir_free(pAuthBuf); - return 0; - } - mir_free(pAuthBuf); - - if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - if (buf[1]) { - SetLastError(ERROR_ACCESS_DENIED); - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - } - - size_t nHostLen; - uint32_t hostIP; - - NetlibUrl &url = nlc->url; - if (nlc->dnsThroughProxy) { - hostIP = inet_addr(url.szHost); - nHostLen = (hostIP == INADDR_NONE) ? mir_strlen(url.szHost) + 1 : 4; - } - else { - hostIP = DnsLookup(nlu, url.szHost); - if (hostIP == 0) - return 0; - nHostLen = 4; - } - uint8_t *pInit = (uint8_t*)mir_alloc(6 + nHostLen); - pInit[0] = 5; //SOCKS5 - pInit[1] = url.flags & NLOCF_UDP ? 3 : 1; //connect or UDP - pInit[2] = 0; //reserved - if (hostIP == INADDR_NONE) { //DNS lookup through proxy - pInit[3] = 3; - pInit[4] = uint8_t(nHostLen - 1); - memcpy(pInit + 5, url.szHost, nHostLen - 1); - } - else { - pInit[3] = 1; - *(PDWORD)(pInit + 4) = hostIP; - } - *(PWORD)(pInit + 4 + nHostLen) = htons(url.port); - if (Netlib_Send(nlc, (char*)pInit, int(6 + nHostLen), MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - mir_free(pInit); - return 0; - } - mir_free(pInit); - - if (!RecvUntilTimeout(nlc, (char*)buf, 5, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - if (buf[0] != 5 || buf[1]) { - const char* err = "Unknown response"; - if (buf[0] != 5) - SetLastError(ERROR_BAD_FORMAT); - else { - switch (buf[1]) { - case 1: SetLastError(ERROR_GEN_FAILURE); err = "General failure"; break; - case 2: SetLastError(ERROR_ACCESS_DENIED); err = "Connection not allowed by ruleset"; break; - case 3: SetLastError(WSAENETUNREACH); err = "Network unreachable"; break; - case 4: SetLastError(WSAEHOSTUNREACH); err = "Host unreachable"; break; - case 5: SetLastError(WSAECONNREFUSED); err = "Connection refused by destination host"; break; - case 6: SetLastError(WSAETIMEDOUT); err = "TTL expired"; break; - case 7: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); err = "Command not supported / protocol error"; break; - case 8: SetLastError(ERROR_INVALID_ADDRESS); err = "Address type not supported"; break; - default: SetLastError(ERROR_INVALID_DATA); break; - } - } - Netlib_Logf(nlu, "%s %d: Proxy conection failed. %s.", __FILE__, __LINE__, err); - return 0; - } - - int nRecvSize = 0; - switch (buf[3]) { - case 1:// ipv4 addr - nRecvSize = 5; - break; - case 3:// dns name addr - nRecvSize = buf[4] + 2; - break; - case 4:// ipv6 addr - nRecvSize = 17; - break; - default: - Netlib_Logf(nlu, "%s %d: %s() unknown address type (%u)", __FILE__, __LINE__, "NetlibInitSocks5Connection", (int)buf[3]); - return 0; - } - if (!RecvUntilTimeout(nlc, (char*)buf, nRecvSize, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - //connected - return 1; -} - -static bool NetlibInitHttpsConnection(NetlibConnection *nlc) -{ - // rfc2817 - NetlibUrl &url = nlc->url; - CMStringA szUrl; - if (nlc->dnsThroughProxy) - szUrl.Format("%s:%u", url.szHost.c_str(), url.port); - else { - uint32_t ip = DnsLookup(nlc->nlu, url.szHost); - if (ip == 0) return false; - szUrl.Format("%s:%u", inet_ntoa(*(PIN_ADDR)&ip), url.port); - } - - NETLIBHTTPREQUEST nlhrSend = { 0 }; - nlhrSend.cbSize = sizeof(nlhrSend); - nlhrSend.requestType = REQUEST_CONNECT; - nlhrSend.flags = NLHRF_DUMPPROXY | NLHRF_HTTP11 | NLHRF_NOPROXY | NLHRF_REDIRECT; - nlhrSend.szUrl = szUrl.GetBuffer(); - - if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) - return false; - - NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW, true); - if (nlhrReply == nullptr) - return false; - - if (nlhrReply->resultCode < 200 || nlhrReply->resultCode >= 300) { - if (nlhrReply->resultCode == 403 && nlc->dnsThroughProxy) { - Netlib_FreeHttpRequest(nlhrReply); - nlc->dnsThroughProxy = 0; - return NetlibInitHttpsConnection(nlc); - } - - NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode); - Netlib_Logf(nlc->nlu, "%s %d: %s request failed (%u %s)", __FILE__, __LINE__, - nlc->nlu->settings.proxyType == PROXYTYPE_HTTP ? "HTTP" : "HTTPS", nlhrReply->resultCode, nlhrReply->szResultDescr); - Netlib_FreeHttpRequest(nlhrReply); - return 0; - } - Netlib_FreeHttpRequest(nlhrReply); - return true; // connected -} - -static void FreePartiallyInitedConnection(NetlibConnection *nlc) -{ - uint32_t dwOriginalLastError = GetLastError(); - - if (GetNetlibHandleType(nlc) == NLH_CONNECTION) - delete nlc; - - SetLastError(dwOriginalLastError); -} - -static bool my_connectIP(NetlibConnection *nlc) -{ - NetlibUser *nlu = nlc->nlu; - int rc = SOCKET_ERROR, retrycnt = 0; - u_long notblocking = 1; - uint32_t lasterr = 0; - static const TIMEVAL tv = { 1, 0 }; - - // if timeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway - int timeout = (nlc->timeout <= 0) ? 30 : nlc->timeout; - - // this is for XP SP2 where there is a default connection attempt limit of 10/second - if (connectionTimeout) { - WaitForSingleObject(hConnectionOpenMutex, 10000); - int waitdiff = GetTickCount() - g_LastConnectionTick; - if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE); - g_LastConnectionTick = GetTickCount(); - ReleaseMutex(hConnectionOpenMutex); - - // might have died in between the wait - if (Miranda_IsTerminated()) - return false; - } - - char szPort[6]; - addrinfo *air = nullptr, *ai, hints = { 0 }; - - hints.ai_family = AF_UNSPEC; - - NetlibUrl &url = nlc->url; - if (url.flags & NLOCF_UDP) { - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - } - else { - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - } - - if (nlc->proxyType) { - if (!nlc->szProxyServer) - return false; - - Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, url.szHost.c_str(), url.port); - - _itoa(nlc->wProxyPort, szPort, 10); - if (GetAddrInfoA(nlc->szProxyServer, szPort, &hints, &air)) { - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", nlc->szProxyServer, WSAGetLastError()); - return false; - } - } - else { - if (url.szHost.IsEmpty()) - return false; - - Netlib_Logf(nlu, "(%p) Connecting to server %s:%d....", nlc, url.szHost.c_str(), url.port); - - _itoa(url.port, szPort, 10); - - if (GetAddrInfoA(url.szHost, szPort, &hints, &air)) { - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", url.szHost.c_str(), WSAGetLastError()); - return false; - } - } - - for (ai = air; ai && !Miranda_IsTerminated(); ai = ai->ai_next) { - Netlib_Logf(nlu, "(%p) Connecting to ip %s ....", nlc, ptrA(Netlib_AddressToString((sockaddr_in*)ai->ai_addr)).get()); -retry: - nlc->s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (nlc->s == INVALID_SOCKET) { - FreeAddrInfoA(air); - return false; - } - - // return the socket to non blocking - if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) { - FreeAddrInfoA(air); - return false; - } - - if (nlu->settings.specifyOutgoingPorts && nlu->settings.szOutgoingPorts && nlu->settings.szOutgoingPorts[0]) { - SOCKET s = ai->ai_family == AF_INET ? nlc->s : INVALID_SOCKET; - SOCKET s6 = ai->ai_family == AF_INET6 ? nlc->s : INVALID_SOCKET; - if (!BindSocketToPort(nlu->settings.szOutgoingPorts, s, s6, &nlu->inportnum)) - Netlib_Logf(nlu, "Netlib connect: Not enough ports for outgoing connections specified"); - } - - // try a connect - if (connect(nlc->s, ai->ai_addr, (int)ai->ai_addrlen) == 0) { - rc = 0; - break; - } - - // didn't work, was it cos of nonblocking? - if (WSAGetLastError() != WSAEWOULDBLOCK) { - rc = SOCKET_ERROR; - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - continue; - } - - while (true) { // timeout loop - fd_set r, w, e; - FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e); - FD_SET(nlc->s, &r); - FD_SET(nlc->s, &w); - FD_SET(nlc->s, &e); - if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR) - break; - - if (rc > 0) { - if (FD_ISSET(nlc->s, &w)) { - // connection was successful - rc = 0; - lasterr = 0; - } - if (FD_ISSET(nlc->s, &r)) { - // connection was closed - rc = SOCKET_ERROR; - lasterr = WSAECONNRESET; - } - if (FD_ISSET(nlc->s, &e)) { - // connection failed. - int len = sizeof(lasterr); - rc = SOCKET_ERROR; - getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len); - if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2) { - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - goto retry; - } - } - break; - } - else if (Miranda_IsTerminated()) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - - if (--timeout == 0) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - } - - if (rc == 0) break; - - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - } - - FreeAddrInfoA(air); - - notblocking = 0; - if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking); - if (rc && lasterr) SetLastError(lasterr); - return rc == 0; -} - -static int NetlibHttpFallbackToDirect(NetlibConnection *nlc) -{ - NetlibDoCloseSocket(nlc, true); - - Netlib_Logf(nlc->nlu, "Fallback to direct connection"); - - nlc->proxyAuthNeeded = false; - nlc->proxyType = 0; - replaceStr(nlc->szProxyServer, nullptr); - if (!my_connectIP(nlc)) { - Netlib_Logf(nlc->nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); - return false; - } - return true; -} - -bool NetlibDoConnect(NetlibConnection *nlc) -{ - NetlibUser *nlu = nlc->nlu; - - replaceStr(nlc->szProxyServer, nullptr); - - bool usingProxy = false, forceHttps = false; - if (nlu->settings.useProxy) { - if (nlu->settings.proxyType == PROXYTYPE_IE) - usingProxy = NetlibGetIeProxyConn(nlc, false); - else { - if (nlu->settings.szProxyServer && nlu->settings.szProxyServer[0]) { - nlc->szProxyServer = mir_strdup(nlu->settings.szProxyServer); - nlc->wProxyPort = nlu->settings.wProxyPort; - nlc->proxyType = nlu->settings.proxyType; - usingProxy = true; - } - } - } - - while (!my_connectIP(nlc)) { - // if connection failed, the state of nlc might be unpredictable - if (GetNetlibHandleType(nlc) == NLH_CONNECTION) { - // Fallback to direct only when using HTTP proxy, as this is what used by companies - // If other type of proxy used it's an indication of security nutcase, leave him alone - if (usingProxy && (nlc->proxyType == PROXYTYPE_HTTPS || nlc->proxyType == PROXYTYPE_HTTP)) { - usingProxy = false; - nlc->proxyType = 0; - Netlib_Logf(nlu, "Fallback to direct connection"); - continue; - } - if (nlu->settings.useProxy && !usingProxy && nlu->settings.proxyType == PROXYTYPE_IE && !forceHttps) { - forceHttps = true; - usingProxy = NetlibGetIeProxyConn(nlc, true); - if (usingProxy) - continue; - } - } - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); - return false; - } - - if (usingProxy && !((nlc->url.flags & (NLOCF_HTTP | NLOCF_SSL)) == NLOCF_HTTP && (nlc->proxyType == PROXYTYPE_HTTP || nlc->proxyType == PROXYTYPE_HTTPS))) { - if (!WaitUntilWritable(nlc->s, 30000)) - return false; - - switch (nlc->proxyType) { - case PROXYTYPE_SOCKS4: - if (!NetlibInitSocks4Connection(nlc)) - return false; - break; - - case PROXYTYPE_SOCKS5: - if (!NetlibInitSocks5Connection(nlc)) - return false; - break; - - case PROXYTYPE_HTTPS: - case PROXYTYPE_HTTP: - nlc->proxyAuthNeeded = true; - if (!NetlibInitHttpsConnection(nlc)) { - usingProxy = false; - if (!NetlibHttpFallbackToDirect(nlc)) - return false; - } - break; - - default: - SetLastError(ERROR_INVALID_PARAMETER); - FreePartiallyInitedConnection(nlc); - return false; - } - } - - Netlib_Logf(nlu, "(%d) Connected to %s:%d", nlc->s, nlc->url.szHost.c_str(), nlc->url.port); - - if (GetSubscribersCount((THook*)hEventConnected)) { - NETLIBCONNECTIONEVENTINFO ncei = {}; - ncei.connected = 1; - ncei.szSettingsModule = nlu->user.szSettingsModule; - int size = sizeof(SOCKADDR_IN); - getsockname(nlc->s, (SOCKADDR *)&ncei.local, &size); - if (nlu->settings.useProxy) { - size = sizeof(SOCKADDR_IN); - getpeername(nlc->s, (SOCKADDR *)&ncei.proxy, &size); - ncei.remote.sin_family = AF_INET; - ncei.remote.sin_port = htons(nlc->url.port); - ncei.remote.sin_addr.S_un.S_addr = DnsLookup(nlu, nlc->url.szHost); - } - else { - size = sizeof(SOCKADDR_IN); - getpeername(nlc->s, (SOCKADDR *)&ncei.remote, &size); - } - NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); - } - - if (NLOCF_SSL & nlc->url.flags) - return Netlib_StartSsl(nlc, nullptr) != 0; - - return true; -} - -bool NetlibReconnect(NetlibConnection *nlc) -{ - // a connection might be freed already - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) - return false; - - char buf[4]; - bool opened = nlc->s != INVALID_SOCKET; - if (opened) { - switch (WaitUntilReadable(nlc->s, 0, true)) { - case SOCKET_ERROR: - opened = false; - break; - - case 0: - opened = true; - break; - - case 1: - opened = recv(nlc->s, buf, 1, MSG_PEEK) > 0; - break; - } - - if (!opened) - NetlibDoCloseSocket(nlc, true); - } - - if (!opened) { - if (Miranda_IsTerminated()) - return false; - - return NetlibDoConnect(nlc); - } - return true; -} - -MIR_APP_DLL(HNETLIBCONN) Netlib_OpenConnection(NetlibUser *nlu, const char *szHost, int port, int timeout, int flags) -{ - if (szHost == nullptr || port == 0) { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING)) - return nullptr; - - Netlib_Logf(nlu, "Connection request to %s:%d (Flags %x)....", szHost, port, flags); - - NetlibConnection *nlc = new NetlibConnection(); - nlc->nlu = nlu; - nlc->timeout = timeout; - nlc->url.szHost = szHost; - nlc->url.port = port; - nlc->url.flags = flags; - nlc->dnsThroughProxy = nlu->settings.dnsThroughProxy != 0; - - if (!NetlibDoConnect(nlc)) { - FreePartiallyInitedConnection(nlc); - return nullptr; - } - - if (iUPnPCleanup == 0) { - mir_cslock lck(csNetlibUser); - iUPnPCleanup = 1; - mir_forkthread(NetlibUPnPCleanup); - } - - return nlc; -} - -NetlibConnection::NetlibConnection() -{ - handleType = NLH_CONNECTION; - s = s2 = INVALID_SOCKET; - hOkToCloseEvent = CreateEvent(nullptr, TRUE, TRUE, nullptr); - NetlibInitializeNestedCS(&ncsSend); - NetlibInitializeNestedCS(&ncsRecv); -} - -NetlibConnection::~NetlibConnection() -{ - handleType = 0; - - if (s != INVALID_SOCKET) - closesocket(s); - - mir_free(szNewUrl); - mir_free(szProxyServer); - - mir_free(nlhpi.szHttpPostUrl); - mir_free(nlhpi.szHttpGetUrl); - - NetlibDeleteNestedCS(&ncsSend); - NetlibDeleteNestedCS(&ncsRecv); - - CloseHandle(hOkToCloseEvent); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+extern mir_cs csNetlibUser;
+extern uint32_t g_LastConnectionTick;
+extern int connectionTimeout;
+static int iUPnPCleanup = 0;
+
+#define RECV_DEFAULT_TIMEOUT 60000
+
+//returns in network byte order
+uint32_t DnsLookup(NetlibUser *nlu, const char *szHost)
+{
+ HOSTENT* host;
+ uint32_t ip = inet_addr(szHost);
+ if (ip != INADDR_NONE)
+ return ip;
+
+ __try {
+ host = gethostbyname(szHost);
+ if (host)
+ return *(u_long*)host->h_addr_list[0];
+
+ Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "gethostbyname", szHost, WSAGetLastError());
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER) {}
+
+ return 0;
+}
+
+int WaitUntilReadable(SOCKET s, uint32_t dwTimeout, bool check)
+{
+ fd_set readfd;
+ TIMEVAL tv;
+
+ if (s == INVALID_SOCKET) return SOCKET_ERROR;
+
+ tv.tv_sec = dwTimeout / 1000;
+ tv.tv_usec = (dwTimeout % 1000) * 1000;
+
+ FD_ZERO(&readfd);
+ FD_SET(s, &readfd);
+
+ int result = select(0, &readfd, nullptr, nullptr, &tv);
+ if (result == 0 && !check) SetLastError(ERROR_TIMEOUT);
+ return result;
+}
+
+int WaitUntilWritable(SOCKET s, uint32_t dwTimeout)
+{
+ fd_set writefd;
+ TIMEVAL tv;
+
+ tv.tv_sec = dwTimeout / 1000;
+ tv.tv_usec = (dwTimeout % 1000) * 1000;
+
+ FD_ZERO(&writefd);
+ FD_SET(s, &writefd);
+
+ switch (select(0, nullptr, &writefd, nullptr, &tv)) {
+ case 0:
+ SetLastError(ERROR_TIMEOUT);
+ case SOCKET_ERROR:
+ return 0;
+ }
+ return 1;
+}
+
+bool RecvUntilTimeout(NetlibConnection *nlc, char *buf, int len, int flags, uint32_t dwTimeout)
+{
+ int nReceived = 0;
+ uint32_t dwTimeNow, dwCompleteTime = GetTickCount() + dwTimeout;
+
+ while ((dwTimeNow = GetTickCount()) < dwCompleteTime) {
+ if (WaitUntilReadable(nlc->s, dwCompleteTime - dwTimeNow) <= 0) return false;
+ nReceived = Netlib_Recv(nlc, buf, len, flags);
+ if (nReceived <= 0) return false;
+
+ buf += nReceived;
+ len -= nReceived;
+ if (len <= 0) return true;
+ }
+ SetLastError(ERROR_TIMEOUT);
+ return false;
+}
+
+static int NetlibInitSocks4Connection(NetlibConnection *nlc)
+{
+ // http://www.socks.nec.com/protocol/socks4.protocol and http://www.socks.nec.com/protocol/socks4a.protocol
+ NetlibUrl &url = nlc->url;
+ if (url.szHost.IsEmpty())
+ return 0;
+
+ NetlibUser *nlu = nlc->nlu;
+ size_t nHostLen = mir_strlen(url.szHost) + 1;
+ size_t nUserLen = nlu->settings.szProxyAuthUser ? mir_strlen(nlu->settings.szProxyAuthUser) + 1 : 1;
+ size_t len = 8 + nUserLen;
+
+ char* pInit = (char*)alloca(len + nHostLen);
+ pInit[0] = 4; // SOCKS4
+ pInit[1] = 1; //connect
+ *(PWORD)&pInit[2] = htons(url.port);
+
+ if (nUserLen <= 1) pInit[8] = 0;
+ else memcpy(&pInit[8], nlu->settings.szProxyAuthUser, nUserLen);
+
+ //if cannot resolve host, try resolving through proxy (requires SOCKS4a)
+ uint32_t ip = DnsLookup(nlu, url.szHost);
+ *(PDWORD)&pInit[4] = ip ? ip : 0x01000000;
+ if (!ip) {
+ memcpy(&pInit[len], url.szHost, nHostLen);
+ len += nHostLen;
+ }
+
+ if (Netlib_Send(nlc, pInit, (int)len, MSG_DUMPPROXY) == SOCKET_ERROR) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError());
+ return 0;
+ }
+
+ char reply[8];
+ if (!RecvUntilTimeout(nlc, reply, sizeof(reply), MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+
+ switch ((uint8_t)reply[1]) {
+ case 90: return 1;
+ case 91: SetLastError(ERROR_ACCESS_DENIED); break;
+ case 92: SetLastError(ERROR_CONNECTION_UNAVAIL); break;
+ case 93: SetLastError(ERROR_INVALID_ACCESS); break;
+ default: SetLastError(ERROR_INVALID_DATA); break;
+ }
+ Netlib_Logf(nlu, "%s %d: Proxy connection failed (%x %u)", __FILE__, __LINE__, (uint8_t)reply[1], GetLastError());
+ return 0;
+}
+
+static int NetlibInitSocks5Connection(NetlibConnection *nlc)
+{
+ //rfc1928
+ uint8_t buf[258];
+ NetlibUser *nlu = nlc->nlu;
+
+ buf[0] = 5; //yep, socks5
+ buf[1] = 1; //one auth method
+ buf[2] = nlu->settings.useProxyAuth ? 2 : 0;
+ if (Netlib_Send(nlc, (char*)buf, 3, MSG_DUMPPROXY) == SOCKET_ERROR) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError());
+ return 0;
+ }
+
+ //confirmation of auth method
+ if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+ if ((buf[1] != 0 && buf[1] != 2)) {
+ SetLastError(ERROR_INVALID_ID_AUTHORITY);
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Recv", GetLastError());
+ return 0;
+ }
+
+ if (buf[1] == 2) { //rfc1929
+ size_t nUserLen = mir_strlen(nlu->settings.szProxyAuthUser);
+ size_t nPassLen = mir_strlen(nlu->settings.szProxyAuthPassword);
+ uint8_t *pAuthBuf = (uint8_t*)mir_alloc(3 + nUserLen + nPassLen);
+ pAuthBuf[0] = 1; //auth version
+ pAuthBuf[1] = (uint8_t)nUserLen;
+ memcpy(pAuthBuf + 2, nlu->settings.szProxyAuthUser, nUserLen);
+ pAuthBuf[2 + nUserLen] = (uint8_t)nPassLen;
+ memcpy(pAuthBuf + 3 + nUserLen, nlu->settings.szProxyAuthPassword, nPassLen);
+ if (Netlib_Send(nlc, (char*)pAuthBuf, int(3 + nUserLen + nPassLen), MSG_DUMPPROXY) == SOCKET_ERROR) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError());
+ mir_free(pAuthBuf);
+ return 0;
+ }
+ mir_free(pAuthBuf);
+
+ if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+ if (buf[1]) {
+ SetLastError(ERROR_ACCESS_DENIED);
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+ }
+
+ size_t nHostLen;
+ uint32_t hostIP;
+
+ NetlibUrl &url = nlc->url;
+ if (nlc->dnsThroughProxy) {
+ hostIP = inet_addr(url.szHost);
+ nHostLen = (hostIP == INADDR_NONE) ? mir_strlen(url.szHost) + 1 : 4;
+ }
+ else {
+ hostIP = DnsLookup(nlu, url.szHost);
+ if (hostIP == 0)
+ return 0;
+ nHostLen = 4;
+ }
+ uint8_t *pInit = (uint8_t*)mir_alloc(6 + nHostLen);
+ pInit[0] = 5; //SOCKS5
+ pInit[1] = url.flags & NLOCF_UDP ? 3 : 1; //connect or UDP
+ pInit[2] = 0; //reserved
+ if (hostIP == INADDR_NONE) { //DNS lookup through proxy
+ pInit[3] = 3;
+ pInit[4] = uint8_t(nHostLen - 1);
+ memcpy(pInit + 5, url.szHost, nHostLen - 1);
+ }
+ else {
+ pInit[3] = 1;
+ *(PDWORD)(pInit + 4) = hostIP;
+ }
+ *(PWORD)(pInit + 4 + nHostLen) = htons(url.port);
+ if (Netlib_Send(nlc, (char*)pInit, int(6 + nHostLen), MSG_DUMPPROXY) == SOCKET_ERROR) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError());
+ mir_free(pInit);
+ return 0;
+ }
+ mir_free(pInit);
+
+ if (!RecvUntilTimeout(nlc, (char*)buf, 5, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+
+ if (buf[0] != 5 || buf[1]) {
+ const char* err = "Unknown response";
+ if (buf[0] != 5)
+ SetLastError(ERROR_BAD_FORMAT);
+ else {
+ switch (buf[1]) {
+ case 1: SetLastError(ERROR_GEN_FAILURE); err = "General failure"; break;
+ case 2: SetLastError(ERROR_ACCESS_DENIED); err = "Connection not allowed by ruleset"; break;
+ case 3: SetLastError(WSAENETUNREACH); err = "Network unreachable"; break;
+ case 4: SetLastError(WSAEHOSTUNREACH); err = "Host unreachable"; break;
+ case 5: SetLastError(WSAECONNREFUSED); err = "Connection refused by destination host"; break;
+ case 6: SetLastError(WSAETIMEDOUT); err = "TTL expired"; break;
+ case 7: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); err = "Command not supported / protocol error"; break;
+ case 8: SetLastError(ERROR_INVALID_ADDRESS); err = "Address type not supported"; break;
+ default: SetLastError(ERROR_INVALID_DATA); break;
+ }
+ }
+ Netlib_Logf(nlu, "%s %d: Proxy conection failed. %s.", __FILE__, __LINE__, err);
+ return 0;
+ }
+
+ int nRecvSize = 0;
+ switch (buf[3]) {
+ case 1:// ipv4 addr
+ nRecvSize = 5;
+ break;
+ case 3:// dns name addr
+ nRecvSize = buf[4] + 2;
+ break;
+ case 4:// ipv6 addr
+ nRecvSize = 17;
+ break;
+ default:
+ Netlib_Logf(nlu, "%s %d: %s() unknown address type (%u)", __FILE__, __LINE__, "NetlibInitSocks5Connection", (int)buf[3]);
+ return 0;
+ }
+ if (!RecvUntilTimeout(nlc, (char*)buf, nRecvSize, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError());
+ return 0;
+ }
+
+ //connected
+ return 1;
+}
+
+static bool NetlibInitHttpsConnection(NetlibConnection *nlc)
+{
+ // rfc2817
+ NetlibUrl &url = nlc->url;
+ CMStringA szUrl;
+ if (nlc->dnsThroughProxy)
+ szUrl.Format("%s:%u", url.szHost.c_str(), url.port);
+ else {
+ uint32_t ip = DnsLookup(nlc->nlu, url.szHost);
+ if (ip == 0) return false;
+ szUrl.Format("%s:%u", inet_ntoa(*(PIN_ADDR)&ip), url.port);
+ }
+
+ NETLIBHTTPREQUEST nlhrSend = { 0 };
+ nlhrSend.cbSize = sizeof(nlhrSend);
+ nlhrSend.requestType = REQUEST_CONNECT;
+ nlhrSend.flags = NLHRF_DUMPPROXY | NLHRF_HTTP11 | NLHRF_NOPROXY | NLHRF_REDIRECT;
+ nlhrSend.szUrl = szUrl.GetBuffer();
+
+ if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR)
+ return false;
+
+ NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW, true);
+ if (nlhrReply == nullptr)
+ return false;
+
+ if (nlhrReply->resultCode < 200 || nlhrReply->resultCode >= 300) {
+ if (nlhrReply->resultCode == 403 && nlc->dnsThroughProxy) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ nlc->dnsThroughProxy = 0;
+ return NetlibInitHttpsConnection(nlc);
+ }
+
+ NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode);
+ Netlib_Logf(nlc->nlu, "%s %d: %s request failed (%u %s)", __FILE__, __LINE__,
+ nlc->nlu->settings.proxyType == PROXYTYPE_HTTP ? "HTTP" : "HTTPS", nlhrReply->resultCode, nlhrReply->szResultDescr);
+ Netlib_FreeHttpRequest(nlhrReply);
+ return 0;
+ }
+ Netlib_FreeHttpRequest(nlhrReply);
+ return true; // connected
+}
+
+static void FreePartiallyInitedConnection(NetlibConnection *nlc)
+{
+ uint32_t dwOriginalLastError = GetLastError();
+
+ if (GetNetlibHandleType(nlc) == NLH_CONNECTION)
+ delete nlc;
+
+ SetLastError(dwOriginalLastError);
+}
+
+static bool my_connectIP(NetlibConnection *nlc)
+{
+ NetlibUser *nlu = nlc->nlu;
+ int rc = SOCKET_ERROR, retrycnt = 0;
+ u_long notblocking = 1;
+ uint32_t lasterr = 0;
+ static const TIMEVAL tv = { 1, 0 };
+
+ // if timeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway
+ int timeout = (nlc->timeout <= 0) ? 30 : nlc->timeout;
+
+ // this is for XP SP2 where there is a default connection attempt limit of 10/second
+ if (connectionTimeout) {
+ WaitForSingleObject(hConnectionOpenMutex, 10000);
+ int waitdiff = GetTickCount() - g_LastConnectionTick;
+ if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE);
+ g_LastConnectionTick = GetTickCount();
+ ReleaseMutex(hConnectionOpenMutex);
+
+ // might have died in between the wait
+ if (Miranda_IsTerminated())
+ return false;
+ }
+
+ char szPort[6];
+ addrinfo *air = nullptr, *ai, hints = { 0 };
+
+ hints.ai_family = AF_UNSPEC;
+
+ NetlibUrl &url = nlc->url;
+ if (url.flags & NLOCF_UDP) {
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ }
+ else {
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ }
+
+ if (nlc->proxyType) {
+ if (!nlc->szProxyServer)
+ return false;
+
+ Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, url.szHost.c_str(), url.port);
+
+ _itoa(nlc->wProxyPort, szPort, 10);
+ if (GetAddrInfoA(nlc->szProxyServer, szPort, &hints, &air)) {
+ Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", nlc->szProxyServer, WSAGetLastError());
+ return false;
+ }
+ }
+ else {
+ if (url.szHost.IsEmpty())
+ return false;
+
+ Netlib_Logf(nlu, "(%p) Connecting to server %s:%d....", nlc, url.szHost.c_str(), url.port);
+
+ _itoa(url.port, szPort, 10);
+
+ if (GetAddrInfoA(url.szHost, szPort, &hints, &air)) {
+ Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", url.szHost.c_str(), WSAGetLastError());
+ return false;
+ }
+ }
+
+ for (ai = air; ai && !Miranda_IsTerminated(); ai = ai->ai_next) {
+ Netlib_Logf(nlu, "(%p) Connecting to ip %s ....", nlc, ptrA(Netlib_AddressToString((sockaddr_in*)ai->ai_addr)).get());
+retry:
+ nlc->s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (nlc->s == INVALID_SOCKET) {
+ FreeAddrInfoA(air);
+ return false;
+ }
+
+ // return the socket to non blocking
+ if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) {
+ FreeAddrInfoA(air);
+ return false;
+ }
+
+ if (nlu->settings.specifyOutgoingPorts && nlu->settings.szOutgoingPorts && nlu->settings.szOutgoingPorts[0]) {
+ SOCKET s = ai->ai_family == AF_INET ? nlc->s : INVALID_SOCKET;
+ SOCKET s6 = ai->ai_family == AF_INET6 ? nlc->s : INVALID_SOCKET;
+ if (!BindSocketToPort(nlu->settings.szOutgoingPorts, s, s6, &nlu->inportnum))
+ Netlib_Logf(nlu, "Netlib connect: Not enough ports for outgoing connections specified");
+ }
+
+ // try a connect
+ if (connect(nlc->s, ai->ai_addr, (int)ai->ai_addrlen) == 0) {
+ rc = 0;
+ break;
+ }
+
+ // didn't work, was it cos of nonblocking?
+ if (WSAGetLastError() != WSAEWOULDBLOCK) {
+ rc = SOCKET_ERROR;
+ closesocket(nlc->s);
+ nlc->s = INVALID_SOCKET;
+ continue;
+ }
+
+ while (true) { // timeout loop
+ fd_set r, w, e;
+ FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e);
+ FD_SET(nlc->s, &r);
+ FD_SET(nlc->s, &w);
+ FD_SET(nlc->s, &e);
+ if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR)
+ break;
+
+ if (rc > 0) {
+ if (FD_ISSET(nlc->s, &w)) {
+ // connection was successful
+ rc = 0;
+ lasterr = 0;
+ }
+ if (FD_ISSET(nlc->s, &r)) {
+ // connection was closed
+ rc = SOCKET_ERROR;
+ lasterr = WSAECONNRESET;
+ }
+ if (FD_ISSET(nlc->s, &e)) {
+ // connection failed.
+ int len = sizeof(lasterr);
+ rc = SOCKET_ERROR;
+ getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len);
+ if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2) {
+ closesocket(nlc->s);
+ nlc->s = INVALID_SOCKET;
+ goto retry;
+ }
+ }
+ break;
+ }
+ else if (Miranda_IsTerminated()) {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+
+ if (--timeout == 0) {
+ rc = SOCKET_ERROR;
+ lasterr = ERROR_TIMEOUT;
+ break;
+ }
+ }
+
+ if (rc == 0) break;
+
+ closesocket(nlc->s);
+ nlc->s = INVALID_SOCKET;
+ }
+
+ FreeAddrInfoA(air);
+
+ notblocking = 0;
+ if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking);
+ if (rc && lasterr) SetLastError(lasterr);
+ return rc == 0;
+}
+
+static int NetlibHttpFallbackToDirect(NetlibConnection *nlc)
+{
+ NetlibDoCloseSocket(nlc, true);
+
+ Netlib_Logf(nlc->nlu, "Fallback to direct connection");
+
+ nlc->proxyAuthNeeded = false;
+ nlc->proxyType = 0;
+ replaceStr(nlc->szProxyServer, nullptr);
+ if (!my_connectIP(nlc)) {
+ Netlib_Logf(nlc->nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError());
+ return false;
+ }
+ return true;
+}
+
+bool NetlibDoConnect(NetlibConnection *nlc)
+{
+ NetlibUser *nlu = nlc->nlu;
+
+ replaceStr(nlc->szProxyServer, nullptr);
+
+ bool usingProxy = false, forceHttps = false;
+ if (nlu->settings.useProxy) {
+ if (nlu->settings.proxyType == PROXYTYPE_IE)
+ usingProxy = NetlibGetIeProxyConn(nlc, false);
+ else {
+ if (nlu->settings.szProxyServer && nlu->settings.szProxyServer[0]) {
+ nlc->szProxyServer = mir_strdup(nlu->settings.szProxyServer);
+ nlc->wProxyPort = nlu->settings.wProxyPort;
+ nlc->proxyType = nlu->settings.proxyType;
+ usingProxy = true;
+ }
+ }
+ }
+
+ while (!my_connectIP(nlc)) {
+ // if connection failed, the state of nlc might be unpredictable
+ if (GetNetlibHandleType(nlc) == NLH_CONNECTION) {
+ // Fallback to direct only when using HTTP proxy, as this is what used by companies
+ // If other type of proxy used it's an indication of security nutcase, leave him alone
+ if (usingProxy && (nlc->proxyType == PROXYTYPE_HTTPS || nlc->proxyType == PROXYTYPE_HTTP)) {
+ usingProxy = false;
+ nlc->proxyType = 0;
+ Netlib_Logf(nlu, "Fallback to direct connection");
+ continue;
+ }
+ if (nlu->settings.useProxy && !usingProxy && nlu->settings.proxyType == PROXYTYPE_IE && !forceHttps) {
+ forceHttps = true;
+ usingProxy = NetlibGetIeProxyConn(nlc, true);
+ if (usingProxy)
+ continue;
+ }
+ }
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError());
+ return false;
+ }
+
+ if (usingProxy && !((nlc->url.flags & (NLOCF_HTTP | NLOCF_SSL)) == NLOCF_HTTP && (nlc->proxyType == PROXYTYPE_HTTP || nlc->proxyType == PROXYTYPE_HTTPS))) {
+ if (!WaitUntilWritable(nlc->s, 30000))
+ return false;
+
+ switch (nlc->proxyType) {
+ case PROXYTYPE_SOCKS4:
+ if (!NetlibInitSocks4Connection(nlc))
+ return false;
+ break;
+
+ case PROXYTYPE_SOCKS5:
+ if (!NetlibInitSocks5Connection(nlc))
+ return false;
+ break;
+
+ case PROXYTYPE_HTTPS:
+ case PROXYTYPE_HTTP:
+ nlc->proxyAuthNeeded = true;
+ if (!NetlibInitHttpsConnection(nlc)) {
+ usingProxy = false;
+ if (!NetlibHttpFallbackToDirect(nlc))
+ return false;
+ }
+ break;
+
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ FreePartiallyInitedConnection(nlc);
+ return false;
+ }
+ }
+
+ Netlib_Logf(nlu, "(%d) Connected to %s:%d", nlc->s, nlc->url.szHost.c_str(), nlc->url.port);
+
+ if (GetSubscribersCount((THook*)hEventConnected)) {
+ NETLIBCONNECTIONEVENTINFO ncei = {};
+ ncei.connected = 1;
+ ncei.szSettingsModule = nlu->user.szSettingsModule;
+ int size = sizeof(SOCKADDR_IN);
+ getsockname(nlc->s, (SOCKADDR *)&ncei.local, &size);
+ if (nlu->settings.useProxy) {
+ size = sizeof(SOCKADDR_IN);
+ getpeername(nlc->s, (SOCKADDR *)&ncei.proxy, &size);
+ ncei.remote.sin_family = AF_INET;
+ ncei.remote.sin_port = htons(nlc->url.port);
+ ncei.remote.sin_addr.S_un.S_addr = DnsLookup(nlu, nlc->url.szHost);
+ }
+ else {
+ size = sizeof(SOCKADDR_IN);
+ getpeername(nlc->s, (SOCKADDR *)&ncei.remote, &size);
+ }
+ NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0);
+ }
+
+ if (NLOCF_SSL & nlc->url.flags)
+ return Netlib_StartSsl(nlc, nullptr) != 0;
+
+ return true;
+}
+
+bool NetlibReconnect(NetlibConnection *nlc)
+{
+ // a connection might be freed already
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION)
+ return false;
+
+ char buf[4];
+ bool opened = nlc->s != INVALID_SOCKET;
+ if (opened) {
+ switch (WaitUntilReadable(nlc->s, 0, true)) {
+ case SOCKET_ERROR:
+ opened = false;
+ break;
+
+ case 0:
+ opened = true;
+ break;
+
+ case 1:
+ opened = recv(nlc->s, buf, 1, MSG_PEEK) > 0;
+ break;
+ }
+
+ if (!opened)
+ NetlibDoCloseSocket(nlc, true);
+ }
+
+ if (!opened) {
+ if (Miranda_IsTerminated())
+ return false;
+
+ return NetlibDoConnect(nlc);
+ }
+ return true;
+}
+
+MIR_APP_DLL(HNETLIBCONN) Netlib_OpenConnection(NetlibUser *nlu, const char *szHost, int port, int timeout, int flags)
+{
+ if (szHost == nullptr || port == 0) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return nullptr;
+ }
+
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING))
+ return nullptr;
+
+ Netlib_Logf(nlu, "Connection request to %s:%d (Flags %x)....", szHost, port, flags);
+
+ NetlibConnection *nlc = new NetlibConnection();
+ nlc->nlu = nlu;
+ nlc->timeout = timeout;
+ nlc->url.szHost = szHost;
+ nlc->url.port = port;
+ nlc->url.flags = flags;
+ nlc->dnsThroughProxy = nlu->settings.dnsThroughProxy != 0;
+
+ if (!NetlibDoConnect(nlc)) {
+ FreePartiallyInitedConnection(nlc);
+ return nullptr;
+ }
+
+ if (iUPnPCleanup == 0) {
+ mir_cslock lck(csNetlibUser);
+ iUPnPCleanup = 1;
+ mir_forkthread(NetlibUPnPCleanup);
+ }
+
+ return nlc;
+}
+
+NetlibConnection::NetlibConnection()
+{
+ handleType = NLH_CONNECTION;
+ s = s2 = INVALID_SOCKET;
+ hOkToCloseEvent = CreateEvent(nullptr, TRUE, TRUE, nullptr);
+ NetlibInitializeNestedCS(&ncsSend);
+ NetlibInitializeNestedCS(&ncsRecv);
+}
+
+NetlibConnection::~NetlibConnection()
+{
+ handleType = 0;
+
+ if (s != INVALID_SOCKET)
+ closesocket(s);
+
+ mir_free(szNewUrl);
+ mir_free(szProxyServer);
+
+ mir_free(nlhpi.szHttpPostUrl);
+ mir_free(nlhpi.szHttpGetUrl);
+
+ NetlibDeleteNestedCS(&ncsSend);
+ NetlibDeleteNestedCS(&ncsRecv);
+
+ CloseHandle(hOkToCloseEvent);
+}
diff --git a/src/mir_app/src/netlib_opts.cpp b/src/mir_app/src/netlib_opts.cpp index a7e0419287..be75114fac 100644 --- a/src/mir_app/src/netlib_opts.cpp +++ b/src/mir_app/src/netlib_opts.cpp @@ -1,518 +1,518 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -struct NetlibTempSettings -{ - uint32_t flags; - char *szSettingsModule; - NETLIBUSERSETTINGS settings; -}; - -static LIST <NetlibTempSettings> tempSettings(5); - -static const UINT outgoingConnectionsControls[] = -{ - IDC_STATIC12, - IDC_USEPROXY, - IDC_STATIC21, IDC_PROXYTYPE, - IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, - IDC_PROXYAUTH, - IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, - IDC_PROXYDNS, - IDC_SPECIFYPORTSO, - IDC_PORTSRANGEO, - IDC_STATIC54, - IDC_VALIDATESSL}; -static const UINT useProxyControls[] = { - IDC_STATIC21, IDC_PROXYTYPE, - IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, - IDC_PROXYAUTH, - IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, - IDC_PROXYDNS}; -static const UINT specifyOPortsControls[] = { - IDC_PORTSRANGEO, - IDC_STATIC54 -}; -static const UINT incomingConnectionsControls[] = { - IDC_STATIC43, - IDC_SPECIFYPORTS, - IDC_PORTSRANGE, - IDC_STATIC52, - IDC_ENABLEUPNP}; -static const UINT specifyPortsControls[] = { - IDC_PORTSRANGE, - IDC_STATIC52}; - -static const wchar_t* szProxyTypes[] = {LPGENW("<mixed>"), L"SOCKS4", L"SOCKS5", L"HTTP", L"HTTPS", L"Internet Explorer"}; -static const uint16_t oftenProxyPorts[] = {1080, 1080, 1080, 8080, 8080, 8080}; - -#define M_REFRESHALL (WM_USER+100) -#define M_REFRESHENABLING (WM_USER+101) - -static void ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) -{ - for (int i = 0; i < cControls; i++) - ShowWindow(GetDlgItem(hwndDlg, controls[i]), state); -} - -static void EnableMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) -{ - for (int i = 0; i < cControls; i++) - EnableWindow(GetDlgItem(hwndDlg, controls[i]), state); -} - -static void AddProxyTypeItem(HWND hwndDlg, int type, int selectType) -{ - int i = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_ADDSTRING, 0, (LPARAM)(type == 0 ? TranslateW(szProxyTypes[type]) : szProxyTypes[type])); - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETITEMDATA, i, type); - if (type == selectType) - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETCURSEL, i, 0); -} - -static void CopySettingsStruct(NETLIBUSERSETTINGS *dest, const NETLIBUSERSETTINGS *source) -{ - *dest = *source; - if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); - if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); - if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); - if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); - if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); -} - -static void CombineSettingsStrings(char **dest, char **source) -{ - if (*dest != nullptr && (*source == nullptr || mir_strcmpi(*dest, *source))) { mir_free(*dest); *dest = nullptr; } -} - -static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest, uint32_t *destFlags, NETLIBUSERSETTINGS *source, uint32_t sourceFlags) -{ - if (sourceFlags & NUF_OUTGOING) { - if (*destFlags & NUF_OUTGOING) { - if (dest->validateSSL != source->validateSSL) dest->validateSSL = 2; - if (dest->useProxy != source->useProxy) dest->useProxy = 2; - if (dest->proxyType != source->proxyType) dest->proxyType = 0; - CombineSettingsStrings(&dest->szProxyServer, &source->szProxyServer); - if (dest->wProxyPort != source->wProxyPort) dest->wProxyPort = 0; - if (dest->useProxyAuth != source->useProxyAuth) dest->useProxyAuth = 2; - CombineSettingsStrings(&dest->szProxyAuthUser, &source->szProxyAuthUser); - CombineSettingsStrings(&dest->szProxyAuthPassword, &source->szProxyAuthPassword); - if (dest->dnsThroughProxy != source->dnsThroughProxy) dest->dnsThroughProxy = 2; - if (dest->specifyOutgoingPorts != source->specifyOutgoingPorts) dest->specifyOutgoingPorts = 2; - CombineSettingsStrings(&dest->szOutgoingPorts, &source->szOutgoingPorts); - } - else { - dest->validateSSL = source->validateSSL; - dest->useProxy = source->useProxy; - dest->proxyType = source->proxyType; - dest->szProxyServer = source->szProxyServer; - if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); - dest->wProxyPort = source->wProxyPort; - dest->useProxyAuth = source->useProxyAuth; - dest->szProxyAuthUser = source->szProxyAuthUser; - if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); - dest->szProxyAuthPassword = source->szProxyAuthPassword; - if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); - dest->dnsThroughProxy = source->dnsThroughProxy; - dest->specifyOutgoingPorts = source->specifyOutgoingPorts; - dest->szOutgoingPorts = source->szOutgoingPorts; - if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); - } - } - if (sourceFlags & NUF_INCOMING) { - if (*destFlags & NUF_INCOMING) { - if (dest->enableUPnP != source->enableUPnP) dest->enableUPnP = 2; - if (dest->specifyIncomingPorts != source->specifyIncomingPorts) dest->specifyIncomingPorts = 2; - CombineSettingsStrings(&dest->szIncomingPorts, &source->szIncomingPorts); - } - else { - dest->enableUPnP = source->enableUPnP; - dest->specifyIncomingPorts = source->specifyIncomingPorts; - dest->szIncomingPorts = source->szIncomingPorts; - if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); - } - } - if ((*destFlags & NUF_NOHTTPSOPTION) != (sourceFlags & NUF_NOHTTPSOPTION)) - *destFlags = (*destFlags | sourceFlags) & ~NUF_NOHTTPSOPTION; - else *destFlags |= sourceFlags; -} - -static void ChangeSettingIntByCheckbox(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) -{ - int newValue = IsDlgButtonChecked(hwndDlg, ctrlId) != BST_CHECKED; - CheckDlgButton(hwndDlg, ctrlId, newValue ? BST_CHECKED : BST_UNCHECKED); - if (iUser == -1) { - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - *(int*)(((uint8_t*)&p->settings) + memberOffset) = newValue; - } - else *(int*)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset) = newValue; - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); -} - -static void ChangeSettingStringByEdit(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) -{ - int newValueLen = GetWindowTextLength(GetDlgItem(hwndDlg, ctrlId)); - char *szNewValue = (char*)mir_alloc(newValueLen+1); - GetDlgItemTextA(hwndDlg, ctrlId, szNewValue, newValueLen+1); - if (iUser == -1) { - for (auto &p : tempSettings) { - if (!(p->flags & NUF_NOOPTIONS)) { - char **ppszNew = (char**)(((uint8_t*)&p->settings) + memberOffset); - mir_free(*ppszNew); - *ppszNew = mir_strdup(szNewValue); - } - } - mir_free(szNewValue); - } - else { - char **ppszNew = (char**)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset); - mir_free(*ppszNew); - *ppszNew = szNewValue; - } -} - -static void WriteSettingsStructToDb(const char *szSettingsModule, NETLIBUSERSETTINGS *settings, uint32_t flags) -{ - if (flags & NUF_OUTGOING) { - db_set_b(0, szSettingsModule, "NLValidateSSL", (uint8_t)settings->validateSSL); - db_set_b(0, szSettingsModule, "NLUseProxy", (uint8_t)settings->useProxy); - db_set_b(0, szSettingsModule, "NLProxyType", (uint8_t)settings->proxyType); - db_set_s(0, szSettingsModule, "NLProxyServer", settings->szProxyServer ? settings->szProxyServer : ""); - db_set_w(0, szSettingsModule, "NLProxyPort", (uint16_t)settings->wProxyPort); - db_set_b(0, szSettingsModule, "NLUseProxyAuth", (uint8_t)settings->useProxyAuth); - db_set_s(0, szSettingsModule, "NLProxyAuthUser", settings->szProxyAuthUser ? settings->szProxyAuthUser : ""); - db_set_s(0, szSettingsModule, "NLProxyAuthPassword", settings->szProxyAuthPassword ? settings->szProxyAuthPassword : ""); - db_set_b(0, szSettingsModule, "NLDnsThroughProxy", (uint8_t)settings->dnsThroughProxy); - db_set_b(0, szSettingsModule, "NLSpecifyOutgoingPorts", (uint8_t)settings->specifyOutgoingPorts); - db_set_s(0, szSettingsModule, "NLOutgoingPorts", settings->szOutgoingPorts ? settings->szOutgoingPorts : ""); - } - if (flags & NUF_INCOMING) { - db_set_b(0, szSettingsModule, "NLEnableUPnP", (uint8_t)settings->enableUPnP); - db_set_b(0, szSettingsModule, "NLSpecifyIncomingPorts", (uint8_t)settings->specifyIncomingPorts); - db_set_s(0, szSettingsModule, "NLIncomingPorts", settings->szIncomingPorts ? settings->szIncomingPorts : ""); - } -} - -void NetlibSaveUserSettingsStruct(const char *szSettingsModule, const NETLIBUSERSETTINGS *settings) -{ - mir_cslock lck(csNetlibUser); - - NetlibUser tUser; - tUser.user.szSettingsModule = (char*)szSettingsModule; - NetlibUser *thisUser = netlibUser.find(&tUser); - if (thisUser == nullptr) - return; - - NetlibFreeUserSettingsStruct(&thisUser->settings); - CopySettingsStruct(&thisUser->settings, settings); - WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags); - - NETLIBUSERSETTINGS combinedSettings = { 0 }; - combinedSettings.cbSize = sizeof(combinedSettings); - - uint32_t flags = 0; - for (auto &p : netlibUser) { - if (p->user.flags & NUF_NOOPTIONS) - continue; - CombineSettingsStructs(&combinedSettings, &flags, &p->settings, p->user.flags); - } - if (combinedSettings.validateSSL == 2) combinedSettings.validateSSL = 0; - if (combinedSettings.useProxy == 2) combinedSettings.useProxy = 0; - if (combinedSettings.proxyType == 0) combinedSettings.proxyType = PROXYTYPE_SOCKS5; - if (combinedSettings.useProxyAuth == 2) combinedSettings.useProxyAuth = 0; - if (combinedSettings.dnsThroughProxy == 2) combinedSettings.dnsThroughProxy = 1; - if (combinedSettings.enableUPnP == 2) combinedSettings.enableUPnP = 1; - if (combinedSettings.specifyIncomingPorts == 2) combinedSettings.specifyIncomingPorts = 0; - WriteSettingsStructToDb("Netlib", &combinedSettings, flags); - NetlibFreeUserSettingsStruct(&combinedSettings); -} - -static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - int iUser; - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - { - int iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)TranslateT("<All connections>")); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, (LPARAM)-1); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETCURSEL, iItem, 0); - - mir_cslock lck(csNetlibUser); - for (auto &it : netlibUser) { - NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings)); - thisSettings->flags = it->user.flags; - thisSettings->szSettingsModule = mir_strdup(it->user.szSettingsModule); - CopySettingsStruct(&thisSettings->settings, &it->settings); - tempSettings.insert(thisSettings); - - if (it->user.flags & NUF_NOOPTIONS) - continue; - iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)it->user.szDescriptiveName.w); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, netlibUser.indexOf(&it)); - } - } - - SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - return TRUE; - - case M_REFRESHALL: - iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); - { - NETLIBUSERSETTINGS settings = { 0 }; - uint32_t flags = 0; - - if (iUser == -1) { - settings.cbSize = sizeof(settings); - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - CombineSettingsStructs(&settings, &flags, &p->settings, p->flags); - } - else { - NetlibFreeUserSettingsStruct(&settings); - CopySettingsStruct(&settings, &tempSettings[iUser]->settings); - flags = tempSettings[iUser]->flags; - } - ShowMultipleControls(hwndDlg, outgoingConnectionsControls, _countof(outgoingConnectionsControls), flags & NUF_OUTGOING ? SW_SHOW : SW_HIDE); - CheckDlgButton(hwndDlg, IDC_USEPROXY, settings.useProxy); - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_RESETCONTENT, 0, 0); - if (settings.proxyType == 0) AddProxyTypeItem(hwndDlg, 0, settings.proxyType); - AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS4, settings.proxyType); - AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS5, settings.proxyType); - if (flags & NUF_HTTPCONNS) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTP, settings.proxyType); - if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTPS, settings.proxyType); - if ((flags & NUF_HTTPCONNS) || !(flags & NUF_NOHTTPSOPTION)) - AddProxyTypeItem(hwndDlg, PROXYTYPE_IE, settings.proxyType); - SetDlgItemTextA(hwndDlg, IDC_PROXYHOST, settings.szProxyServer ? settings.szProxyServer : ""); - if (settings.wProxyPort) SetDlgItemInt(hwndDlg, IDC_PROXYPORT, settings.wProxyPort, FALSE); - else SetDlgItemTextA(hwndDlg, IDC_PROXYPORT, ""); - CheckDlgButton(hwndDlg, IDC_PROXYAUTH, settings.useProxyAuth); - SetDlgItemTextA(hwndDlg, IDC_PROXYUSER, settings.szProxyAuthUser ? settings.szProxyAuthUser : ""); - SetDlgItemTextA(hwndDlg, IDC_PROXYPASS, settings.szProxyAuthPassword ? settings.szProxyAuthPassword : ""); - CheckDlgButton(hwndDlg, IDC_PROXYDNS, settings.dnsThroughProxy); - CheckDlgButton(hwndDlg, IDC_VALIDATESSL, settings.validateSSL); - - ShowMultipleControls(hwndDlg, incomingConnectionsControls, _countof(incomingConnectionsControls), flags & NUF_INCOMING ? SW_SHOW : SW_HIDE); - CheckDlgButton(hwndDlg, IDC_SPECIFYPORTS, settings.specifyIncomingPorts); - SetDlgItemTextA(hwndDlg, IDC_PORTSRANGE, settings.szIncomingPorts ? settings.szIncomingPorts : ""); - - CheckDlgButton(hwndDlg, IDC_SPECIFYPORTSO, settings.specifyOutgoingPorts); - SetDlgItemTextA(hwndDlg, IDC_PORTSRANGEO, settings.szOutgoingPorts ? settings.szOutgoingPorts : ""); - - CheckDlgButton(hwndDlg, IDC_ENABLEUPNP, settings.enableUPnP); - - NetlibFreeUserSettingsStruct(&settings); - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); - } - break; - - case M_REFRESHENABLING: - wchar_t str[80]; - { - int selectedProxyType = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); - mir_snwprintf(str, TranslateT("(often %d)"), oftenProxyPorts[selectedProxyType]); - SetDlgItemText(hwndDlg, IDC_STOFTENPORT, str); - if (IsDlgButtonChecked(hwndDlg, IDC_USEPROXY) != BST_UNCHECKED) { - int enableAuth = 0, enableUser = 0, enablePass = 0, enableServer = 1; - EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), TRUE); - if (selectedProxyType == 0) { - for (auto &p : tempSettings) { - if (!p->settings.useProxy || p->flags & NUF_NOOPTIONS || !(p->flags & NUF_OUTGOING)) - continue; - - if (p->settings.proxyType == PROXYTYPE_SOCKS4) enableUser = 1; - else { - enableAuth = 1; - if (p->settings.useProxyAuth) - enableUser = enablePass = 1; - } - } - } - else { - if (selectedProxyType == PROXYTYPE_SOCKS4) enableUser = 1; - else { - if (selectedProxyType == PROXYTYPE_IE) enableServer = 0; - enableAuth = 1; - if (IsDlgButtonChecked(hwndDlg, IDC_PROXYAUTH) != BST_UNCHECKED) - enableUser = enablePass = 1; - } - } - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYAUTH), enableAuth); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC31), enableUser); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYUSER), enableUser); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC32), enablePass); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPASS), enablePass); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYHOST), enableServer); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPORT), enableServer); - } - else EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), FALSE); - EnableMultipleControls(hwndDlg, specifyPortsControls, _countof(specifyPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTS) != BST_UNCHECKED); - EnableMultipleControls(hwndDlg, specifyOPortsControls, _countof(specifyOPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTSO) != BST_UNCHECKED); - } - break; - - case WM_COMMAND: - iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); - switch (LOWORD(wParam)) { - case IDC_NETLIBUSERS: - if (HIWORD(wParam) == CBN_SELCHANGE) SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - return 0; - - case IDC_LOGOPTIONS: - NetlibLogShowOptions(); - return 0; - - case IDC_PROXYTYPE: - if (HIWORD(wParam) == CBN_SELCHANGE) { - int newValue = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); - if (iUser == -1) { - if (newValue == 0) - return 0; - - for (auto &p : tempSettings) { - if (p->flags & NUF_NOOPTIONS) - continue; - if (newValue == PROXYTYPE_HTTP && !(p->flags & NUF_HTTPCONNS)) - p->settings.proxyType = PROXYTYPE_HTTPS; - else if (newValue == PROXYTYPE_HTTPS && p->flags & NUF_NOHTTPSOPTION) - p->settings.proxyType = PROXYTYPE_HTTP; - else p->settings.proxyType = newValue; - } - SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - } - else { - tempSettings[iUser]->settings.proxyType = newValue; - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); - } - } - break; - case IDC_USEPROXY: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxy)); - break; - case IDC_PROXYAUTH: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxyAuth)); - break; - case IDC_PROXYDNS: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, dnsThroughProxy)); - break; - case IDC_SPECIFYPORTS: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyIncomingPorts)); - break; - case IDC_SPECIFYPORTSO: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyOutgoingPorts)); - break; - case IDC_ENABLEUPNP: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, enableUPnP)); - break; - case IDC_VALIDATESSL: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, validateSSL)); - break; - case IDC_PROXYHOST: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyServer)); - break; - case IDC_PROXYPORT: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - { - int newValue = GetDlgItemInt(hwndDlg, LOWORD(wParam), nullptr, FALSE); - if (iUser == -1) { - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - p->settings.wProxyPort = newValue; - } - else tempSettings[iUser]->settings.wProxyPort = newValue; - } - break; - case IDC_PROXYUSER: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthUser)); - break; - case IDC_PROXYPASS: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthPassword)); - break; - case IDC_PORTSRANGE: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szIncomingPorts)); - break; - case IDC_PORTSRANGEO: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szOutgoingPorts)); - break; - } - ShowWindow(GetDlgItem(hwndDlg, IDC_RECONNECTREQD), SW_SHOW); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->idFrom) { - case 0: - switch (((LPNMHDR)lParam)->code) { - case PSN_APPLY: - for (auto &p : tempSettings) - NetlibSaveUserSettingsStruct(p->szSettingsModule, &p->settings); - return TRUE; - } - break; - } - break; - - case WM_DESTROY: - for (auto &p : tempSettings) { - mir_free(p->szSettingsModule); - NetlibFreeUserSettingsStruct(&p->settings); - mir_free(p); - } - tempSettings.destroy(); - break; - } - return FALSE; -} - -int NetlibOptInitialise(WPARAM wParam, LPARAM) -{ - int optionsCount = 0; - { - mir_cslock lck(csNetlibUser); - for (auto &p : netlibUser) - if (!(p->user.flags & NUF_NOOPTIONS)) - ++optionsCount; - } - - if (optionsCount == 0) - return 0; - - OPTIONSDIALOGPAGE odp = {}; - odp.position = 900000000; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB); - odp.szTitle.a = LPGEN("Network"); - odp.pfnDlgProc = DlgProcNetlibOpts; - odp.flags = ODPF_BOLDGROUPS; - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+struct NetlibTempSettings
+{
+ uint32_t flags;
+ char *szSettingsModule;
+ NETLIBUSERSETTINGS settings;
+};
+
+static LIST <NetlibTempSettings> tempSettings(5);
+
+static const UINT outgoingConnectionsControls[] =
+{
+ IDC_STATIC12,
+ IDC_USEPROXY,
+ IDC_STATIC21, IDC_PROXYTYPE,
+ IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT,
+ IDC_PROXYAUTH,
+ IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS,
+ IDC_PROXYDNS,
+ IDC_SPECIFYPORTSO,
+ IDC_PORTSRANGEO,
+ IDC_STATIC54,
+ IDC_VALIDATESSL};
+static const UINT useProxyControls[] = {
+ IDC_STATIC21, IDC_PROXYTYPE,
+ IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT,
+ IDC_PROXYAUTH,
+ IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS,
+ IDC_PROXYDNS};
+static const UINT specifyOPortsControls[] = {
+ IDC_PORTSRANGEO,
+ IDC_STATIC54
+};
+static const UINT incomingConnectionsControls[] = {
+ IDC_STATIC43,
+ IDC_SPECIFYPORTS,
+ IDC_PORTSRANGE,
+ IDC_STATIC52,
+ IDC_ENABLEUPNP};
+static const UINT specifyPortsControls[] = {
+ IDC_PORTSRANGE,
+ IDC_STATIC52};
+
+static const wchar_t* szProxyTypes[] = {LPGENW("<mixed>"), L"SOCKS4", L"SOCKS5", L"HTTP", L"HTTPS", L"Internet Explorer"};
+static const uint16_t oftenProxyPorts[] = {1080, 1080, 1080, 8080, 8080, 8080};
+
+#define M_REFRESHALL (WM_USER+100)
+#define M_REFRESHENABLING (WM_USER+101)
+
+static void ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state)
+{
+ for (int i = 0; i < cControls; i++)
+ ShowWindow(GetDlgItem(hwndDlg, controls[i]), state);
+}
+
+static void EnableMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state)
+{
+ for (int i = 0; i < cControls; i++)
+ EnableWindow(GetDlgItem(hwndDlg, controls[i]), state);
+}
+
+static void AddProxyTypeItem(HWND hwndDlg, int type, int selectType)
+{
+ int i = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_ADDSTRING, 0, (LPARAM)(type == 0 ? TranslateW(szProxyTypes[type]) : szProxyTypes[type]));
+ SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETITEMDATA, i, type);
+ if (type == selectType)
+ SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETCURSEL, i, 0);
+}
+
+static void CopySettingsStruct(NETLIBUSERSETTINGS *dest, const NETLIBUSERSETTINGS *source)
+{
+ *dest = *source;
+ if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts);
+ if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts);
+ if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword);
+ if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser);
+ if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer);
+}
+
+static void CombineSettingsStrings(char **dest, char **source)
+{
+ if (*dest != nullptr && (*source == nullptr || mir_strcmpi(*dest, *source))) { mir_free(*dest); *dest = nullptr; }
+}
+
+static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest, uint32_t *destFlags, NETLIBUSERSETTINGS *source, uint32_t sourceFlags)
+{
+ if (sourceFlags & NUF_OUTGOING) {
+ if (*destFlags & NUF_OUTGOING) {
+ if (dest->validateSSL != source->validateSSL) dest->validateSSL = 2;
+ if (dest->useProxy != source->useProxy) dest->useProxy = 2;
+ if (dest->proxyType != source->proxyType) dest->proxyType = 0;
+ CombineSettingsStrings(&dest->szProxyServer, &source->szProxyServer);
+ if (dest->wProxyPort != source->wProxyPort) dest->wProxyPort = 0;
+ if (dest->useProxyAuth != source->useProxyAuth) dest->useProxyAuth = 2;
+ CombineSettingsStrings(&dest->szProxyAuthUser, &source->szProxyAuthUser);
+ CombineSettingsStrings(&dest->szProxyAuthPassword, &source->szProxyAuthPassword);
+ if (dest->dnsThroughProxy != source->dnsThroughProxy) dest->dnsThroughProxy = 2;
+ if (dest->specifyOutgoingPorts != source->specifyOutgoingPorts) dest->specifyOutgoingPorts = 2;
+ CombineSettingsStrings(&dest->szOutgoingPorts, &source->szOutgoingPorts);
+ }
+ else {
+ dest->validateSSL = source->validateSSL;
+ dest->useProxy = source->useProxy;
+ dest->proxyType = source->proxyType;
+ dest->szProxyServer = source->szProxyServer;
+ if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer);
+ dest->wProxyPort = source->wProxyPort;
+ dest->useProxyAuth = source->useProxyAuth;
+ dest->szProxyAuthUser = source->szProxyAuthUser;
+ if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser);
+ dest->szProxyAuthPassword = source->szProxyAuthPassword;
+ if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword);
+ dest->dnsThroughProxy = source->dnsThroughProxy;
+ dest->specifyOutgoingPorts = source->specifyOutgoingPorts;
+ dest->szOutgoingPorts = source->szOutgoingPorts;
+ if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts);
+ }
+ }
+ if (sourceFlags & NUF_INCOMING) {
+ if (*destFlags & NUF_INCOMING) {
+ if (dest->enableUPnP != source->enableUPnP) dest->enableUPnP = 2;
+ if (dest->specifyIncomingPorts != source->specifyIncomingPorts) dest->specifyIncomingPorts = 2;
+ CombineSettingsStrings(&dest->szIncomingPorts, &source->szIncomingPorts);
+ }
+ else {
+ dest->enableUPnP = source->enableUPnP;
+ dest->specifyIncomingPorts = source->specifyIncomingPorts;
+ dest->szIncomingPorts = source->szIncomingPorts;
+ if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts);
+ }
+ }
+ if ((*destFlags & NUF_NOHTTPSOPTION) != (sourceFlags & NUF_NOHTTPSOPTION))
+ *destFlags = (*destFlags | sourceFlags) & ~NUF_NOHTTPSOPTION;
+ else *destFlags |= sourceFlags;
+}
+
+static void ChangeSettingIntByCheckbox(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset)
+{
+ int newValue = IsDlgButtonChecked(hwndDlg, ctrlId) != BST_CHECKED;
+ CheckDlgButton(hwndDlg, ctrlId, newValue ? BST_CHECKED : BST_UNCHECKED);
+ if (iUser == -1) {
+ for (auto &p : tempSettings)
+ if (!(p->flags & NUF_NOOPTIONS))
+ *(int*)(((uint8_t*)&p->settings) + memberOffset) = newValue;
+ }
+ else *(int*)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset) = newValue;
+ SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
+}
+
+static void ChangeSettingStringByEdit(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset)
+{
+ int newValueLen = GetWindowTextLength(GetDlgItem(hwndDlg, ctrlId));
+ char *szNewValue = (char*)mir_alloc(newValueLen+1);
+ GetDlgItemTextA(hwndDlg, ctrlId, szNewValue, newValueLen+1);
+ if (iUser == -1) {
+ for (auto &p : tempSettings) {
+ if (!(p->flags & NUF_NOOPTIONS)) {
+ char **ppszNew = (char**)(((uint8_t*)&p->settings) + memberOffset);
+ mir_free(*ppszNew);
+ *ppszNew = mir_strdup(szNewValue);
+ }
+ }
+ mir_free(szNewValue);
+ }
+ else {
+ char **ppszNew = (char**)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset);
+ mir_free(*ppszNew);
+ *ppszNew = szNewValue;
+ }
+}
+
+static void WriteSettingsStructToDb(const char *szSettingsModule, NETLIBUSERSETTINGS *settings, uint32_t flags)
+{
+ if (flags & NUF_OUTGOING) {
+ db_set_b(0, szSettingsModule, "NLValidateSSL", (uint8_t)settings->validateSSL);
+ db_set_b(0, szSettingsModule, "NLUseProxy", (uint8_t)settings->useProxy);
+ db_set_b(0, szSettingsModule, "NLProxyType", (uint8_t)settings->proxyType);
+ db_set_s(0, szSettingsModule, "NLProxyServer", settings->szProxyServer ? settings->szProxyServer : "");
+ db_set_w(0, szSettingsModule, "NLProxyPort", (uint16_t)settings->wProxyPort);
+ db_set_b(0, szSettingsModule, "NLUseProxyAuth", (uint8_t)settings->useProxyAuth);
+ db_set_s(0, szSettingsModule, "NLProxyAuthUser", settings->szProxyAuthUser ? settings->szProxyAuthUser : "");
+ db_set_s(0, szSettingsModule, "NLProxyAuthPassword", settings->szProxyAuthPassword ? settings->szProxyAuthPassword : "");
+ db_set_b(0, szSettingsModule, "NLDnsThroughProxy", (uint8_t)settings->dnsThroughProxy);
+ db_set_b(0, szSettingsModule, "NLSpecifyOutgoingPorts", (uint8_t)settings->specifyOutgoingPorts);
+ db_set_s(0, szSettingsModule, "NLOutgoingPorts", settings->szOutgoingPorts ? settings->szOutgoingPorts : "");
+ }
+ if (flags & NUF_INCOMING) {
+ db_set_b(0, szSettingsModule, "NLEnableUPnP", (uint8_t)settings->enableUPnP);
+ db_set_b(0, szSettingsModule, "NLSpecifyIncomingPorts", (uint8_t)settings->specifyIncomingPorts);
+ db_set_s(0, szSettingsModule, "NLIncomingPorts", settings->szIncomingPorts ? settings->szIncomingPorts : "");
+ }
+}
+
+void NetlibSaveUserSettingsStruct(const char *szSettingsModule, const NETLIBUSERSETTINGS *settings)
+{
+ mir_cslock lck(csNetlibUser);
+
+ NetlibUser tUser;
+ tUser.user.szSettingsModule = (char*)szSettingsModule;
+ NetlibUser *thisUser = netlibUser.find(&tUser);
+ if (thisUser == nullptr)
+ return;
+
+ NetlibFreeUserSettingsStruct(&thisUser->settings);
+ CopySettingsStruct(&thisUser->settings, settings);
+ WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags);
+
+ NETLIBUSERSETTINGS combinedSettings = { 0 };
+ combinedSettings.cbSize = sizeof(combinedSettings);
+
+ uint32_t flags = 0;
+ for (auto &p : netlibUser) {
+ if (p->user.flags & NUF_NOOPTIONS)
+ continue;
+ CombineSettingsStructs(&combinedSettings, &flags, &p->settings, p->user.flags);
+ }
+ if (combinedSettings.validateSSL == 2) combinedSettings.validateSSL = 0;
+ if (combinedSettings.useProxy == 2) combinedSettings.useProxy = 0;
+ if (combinedSettings.proxyType == 0) combinedSettings.proxyType = PROXYTYPE_SOCKS5;
+ if (combinedSettings.useProxyAuth == 2) combinedSettings.useProxyAuth = 0;
+ if (combinedSettings.dnsThroughProxy == 2) combinedSettings.dnsThroughProxy = 1;
+ if (combinedSettings.enableUPnP == 2) combinedSettings.enableUPnP = 1;
+ if (combinedSettings.specifyIncomingPorts == 2) combinedSettings.specifyIncomingPorts = 0;
+ WriteSettingsStructToDb("Netlib", &combinedSettings, flags);
+ NetlibFreeUserSettingsStruct(&combinedSettings);
+}
+
+static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int iUser;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ int iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)TranslateT("<All connections>"));
+ SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, (LPARAM)-1);
+ SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETCURSEL, iItem, 0);
+
+ mir_cslock lck(csNetlibUser);
+ for (auto &it : netlibUser) {
+ NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings));
+ thisSettings->flags = it->user.flags;
+ thisSettings->szSettingsModule = mir_strdup(it->user.szSettingsModule);
+ CopySettingsStruct(&thisSettings->settings, &it->settings);
+ tempSettings.insert(thisSettings);
+
+ if (it->user.flags & NUF_NOOPTIONS)
+ continue;
+ iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)it->user.szDescriptiveName.w);
+ SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, netlibUser.indexOf(&it));
+ }
+ }
+
+ SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
+ return TRUE;
+
+ case M_REFRESHALL:
+ iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0);
+ {
+ NETLIBUSERSETTINGS settings = { 0 };
+ uint32_t flags = 0;
+
+ if (iUser == -1) {
+ settings.cbSize = sizeof(settings);
+ for (auto &p : tempSettings)
+ if (!(p->flags & NUF_NOOPTIONS))
+ CombineSettingsStructs(&settings, &flags, &p->settings, p->flags);
+ }
+ else {
+ NetlibFreeUserSettingsStruct(&settings);
+ CopySettingsStruct(&settings, &tempSettings[iUser]->settings);
+ flags = tempSettings[iUser]->flags;
+ }
+ ShowMultipleControls(hwndDlg, outgoingConnectionsControls, _countof(outgoingConnectionsControls), flags & NUF_OUTGOING ? SW_SHOW : SW_HIDE);
+ CheckDlgButton(hwndDlg, IDC_USEPROXY, settings.useProxy);
+ SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_RESETCONTENT, 0, 0);
+ if (settings.proxyType == 0) AddProxyTypeItem(hwndDlg, 0, settings.proxyType);
+ AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS4, settings.proxyType);
+ AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS5, settings.proxyType);
+ if (flags & NUF_HTTPCONNS) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTP, settings.proxyType);
+ if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTPS, settings.proxyType);
+ if ((flags & NUF_HTTPCONNS) || !(flags & NUF_NOHTTPSOPTION))
+ AddProxyTypeItem(hwndDlg, PROXYTYPE_IE, settings.proxyType);
+ SetDlgItemTextA(hwndDlg, IDC_PROXYHOST, settings.szProxyServer ? settings.szProxyServer : "");
+ if (settings.wProxyPort) SetDlgItemInt(hwndDlg, IDC_PROXYPORT, settings.wProxyPort, FALSE);
+ else SetDlgItemTextA(hwndDlg, IDC_PROXYPORT, "");
+ CheckDlgButton(hwndDlg, IDC_PROXYAUTH, settings.useProxyAuth);
+ SetDlgItemTextA(hwndDlg, IDC_PROXYUSER, settings.szProxyAuthUser ? settings.szProxyAuthUser : "");
+ SetDlgItemTextA(hwndDlg, IDC_PROXYPASS, settings.szProxyAuthPassword ? settings.szProxyAuthPassword : "");
+ CheckDlgButton(hwndDlg, IDC_PROXYDNS, settings.dnsThroughProxy);
+ CheckDlgButton(hwndDlg, IDC_VALIDATESSL, settings.validateSSL);
+
+ ShowMultipleControls(hwndDlg, incomingConnectionsControls, _countof(incomingConnectionsControls), flags & NUF_INCOMING ? SW_SHOW : SW_HIDE);
+ CheckDlgButton(hwndDlg, IDC_SPECIFYPORTS, settings.specifyIncomingPorts);
+ SetDlgItemTextA(hwndDlg, IDC_PORTSRANGE, settings.szIncomingPorts ? settings.szIncomingPorts : "");
+
+ CheckDlgButton(hwndDlg, IDC_SPECIFYPORTSO, settings.specifyOutgoingPorts);
+ SetDlgItemTextA(hwndDlg, IDC_PORTSRANGEO, settings.szOutgoingPorts ? settings.szOutgoingPorts : "");
+
+ CheckDlgButton(hwndDlg, IDC_ENABLEUPNP, settings.enableUPnP);
+
+ NetlibFreeUserSettingsStruct(&settings);
+ SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
+ }
+ break;
+
+ case M_REFRESHENABLING:
+ wchar_t str[80];
+ {
+ int selectedProxyType = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0);
+ mir_snwprintf(str, TranslateT("(often %d)"), oftenProxyPorts[selectedProxyType]);
+ SetDlgItemText(hwndDlg, IDC_STOFTENPORT, str);
+ if (IsDlgButtonChecked(hwndDlg, IDC_USEPROXY) != BST_UNCHECKED) {
+ int enableAuth = 0, enableUser = 0, enablePass = 0, enableServer = 1;
+ EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), TRUE);
+ if (selectedProxyType == 0) {
+ for (auto &p : tempSettings) {
+ if (!p->settings.useProxy || p->flags & NUF_NOOPTIONS || !(p->flags & NUF_OUTGOING))
+ continue;
+
+ if (p->settings.proxyType == PROXYTYPE_SOCKS4) enableUser = 1;
+ else {
+ enableAuth = 1;
+ if (p->settings.useProxyAuth)
+ enableUser = enablePass = 1;
+ }
+ }
+ }
+ else {
+ if (selectedProxyType == PROXYTYPE_SOCKS4) enableUser = 1;
+ else {
+ if (selectedProxyType == PROXYTYPE_IE) enableServer = 0;
+ enableAuth = 1;
+ if (IsDlgButtonChecked(hwndDlg, IDC_PROXYAUTH) != BST_UNCHECKED)
+ enableUser = enablePass = 1;
+ }
+ }
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYAUTH), enableAuth);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC31), enableUser);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYUSER), enableUser);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC32), enablePass);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPASS), enablePass);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYHOST), enableServer);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPORT), enableServer);
+ }
+ else EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), FALSE);
+ EnableMultipleControls(hwndDlg, specifyPortsControls, _countof(specifyPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTS) != BST_UNCHECKED);
+ EnableMultipleControls(hwndDlg, specifyOPortsControls, _countof(specifyOPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTSO) != BST_UNCHECKED);
+ }
+ break;
+
+ case WM_COMMAND:
+ iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0);
+ switch (LOWORD(wParam)) {
+ case IDC_NETLIBUSERS:
+ if (HIWORD(wParam) == CBN_SELCHANGE) SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
+ return 0;
+
+ case IDC_LOGOPTIONS:
+ NetlibLogShowOptions();
+ return 0;
+
+ case IDC_PROXYTYPE:
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ int newValue = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0);
+ if (iUser == -1) {
+ if (newValue == 0)
+ return 0;
+
+ for (auto &p : tempSettings) {
+ if (p->flags & NUF_NOOPTIONS)
+ continue;
+ if (newValue == PROXYTYPE_HTTP && !(p->flags & NUF_HTTPCONNS))
+ p->settings.proxyType = PROXYTYPE_HTTPS;
+ else if (newValue == PROXYTYPE_HTTPS && p->flags & NUF_NOHTTPSOPTION)
+ p->settings.proxyType = PROXYTYPE_HTTP;
+ else p->settings.proxyType = newValue;
+ }
+ SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
+ }
+ else {
+ tempSettings[iUser]->settings.proxyType = newValue;
+ SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
+ }
+ }
+ break;
+ case IDC_USEPROXY:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxy));
+ break;
+ case IDC_PROXYAUTH:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxyAuth));
+ break;
+ case IDC_PROXYDNS:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, dnsThroughProxy));
+ break;
+ case IDC_SPECIFYPORTS:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyIncomingPorts));
+ break;
+ case IDC_SPECIFYPORTSO:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyOutgoingPorts));
+ break;
+ case IDC_ENABLEUPNP:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, enableUPnP));
+ break;
+ case IDC_VALIDATESSL:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, validateSSL));
+ break;
+ case IDC_PROXYHOST:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyServer));
+ break;
+ case IDC_PROXYPORT:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ {
+ int newValue = GetDlgItemInt(hwndDlg, LOWORD(wParam), nullptr, FALSE);
+ if (iUser == -1) {
+ for (auto &p : tempSettings)
+ if (!(p->flags & NUF_NOOPTIONS))
+ p->settings.wProxyPort = newValue;
+ }
+ else tempSettings[iUser]->settings.wProxyPort = newValue;
+ }
+ break;
+ case IDC_PROXYUSER:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthUser));
+ break;
+ case IDC_PROXYPASS:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthPassword));
+ break;
+ case IDC_PORTSRANGE:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szIncomingPorts));
+ break;
+ case IDC_PORTSRANGEO:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szOutgoingPorts));
+ break;
+ }
+ ShowWindow(GetDlgItem(hwndDlg, IDC_RECONNECTREQD), SW_SHOW);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ for (auto &p : tempSettings)
+ NetlibSaveUserSettingsStruct(p->szSettingsModule, &p->settings);
+ return TRUE;
+ }
+ break;
+ }
+ break;
+
+ case WM_DESTROY:
+ for (auto &p : tempSettings) {
+ mir_free(p->szSettingsModule);
+ NetlibFreeUserSettingsStruct(&p->settings);
+ mir_free(p);
+ }
+ tempSettings.destroy();
+ break;
+ }
+ return FALSE;
+}
+
+int NetlibOptInitialise(WPARAM wParam, LPARAM)
+{
+ int optionsCount = 0;
+ {
+ mir_cslock lck(csNetlibUser);
+ for (auto &p : netlibUser)
+ if (!(p->user.flags & NUF_NOOPTIONS))
+ ++optionsCount;
+ }
+
+ if (optionsCount == 0)
+ return 0;
+
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 900000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB);
+ odp.szTitle.a = LPGEN("Network");
+ odp.pfnDlgProc = DlgProcNetlibOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/mir_app/src/netlib_pktrecver.cpp b/src/mir_app/src/netlib_pktrecver.cpp index 66621d43a3..3b86fed51d 100644 --- a/src/mir_app/src/netlib_pktrecver.cpp +++ b/src/mir_app/src/netlib_pktrecver.cpp @@ -1,80 +1,80 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN nlc, int iMaxSize) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION || iMaxSize == 0) { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - NetlibPacketRecver *nlpr = (struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver)); - nlpr->handleType = NLH_PACKETRECVER; - nlpr->nlc = nlc; - nlpr->packetRecver.bufferSize = iMaxSize; - nlpr->packetRecver.buffer = (uint8_t*)mir_alloc(nlpr->packetRecver.bufferSize); - nlpr->packetRecver.bytesUsed = 0; - nlpr->packetRecver.bytesAvailable = 0; - return nlpr; -} - -MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam) -{ - NetlibPacketRecver *nlpr = (NetlibPacketRecver*)hReceiver; - if (GetNetlibHandleType(nlpr) != NLH_PACKETRECVER || nlprParam == nullptr || nlprParam->bytesUsed > nlpr->packetRecver.bytesAvailable) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - if (Miranda_IsTerminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */ - SetLastError(ERROR_TIMEOUT); - return SOCKET_ERROR; - } - nlpr->packetRecver.dwTimeout = nlprParam->dwTimeout; - if (nlprParam->bytesUsed == 0) { - if (nlpr->packetRecver.bytesAvailable == nlpr->packetRecver.bufferSize) { - nlpr->packetRecver.bytesAvailable = 0; - Netlib_Logf(nlpr->nlc->nlu, "Packet recver: packet overflowed buffer, ditching"); - } - } - else { - memmove(nlpr->packetRecver.buffer, nlpr->packetRecver.buffer + nlprParam->bytesUsed, nlpr->packetRecver.bytesAvailable - nlprParam->bytesUsed); - nlpr->packetRecver.bytesAvailable -= nlprParam->bytesUsed; - } - - if (nlprParam->dwTimeout != INFINITE) { - if (!Netlib_SslPending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s, nlprParam->dwTimeout) <= 0) { - *nlprParam = nlpr->packetRecver; - return SOCKET_ERROR; - } - } - - INT_PTR recvResult = Netlib_Recv(nlpr->nlc, (char*)nlpr->packetRecver.buffer + nlpr->packetRecver.bytesAvailable, nlpr->packetRecver.bufferSize - nlpr->packetRecver.bytesAvailable, 0); - if (recvResult > 0) - nlpr->packetRecver.bytesAvailable += recvResult; - *nlprParam = nlpr->packetRecver; - return recvResult; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN nlc, int iMaxSize)
+{
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION || iMaxSize == 0) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return nullptr;
+ }
+
+ NetlibPacketRecver *nlpr = (struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver));
+ nlpr->handleType = NLH_PACKETRECVER;
+ nlpr->nlc = nlc;
+ nlpr->packetRecver.bufferSize = iMaxSize;
+ nlpr->packetRecver.buffer = (uint8_t*)mir_alloc(nlpr->packetRecver.bufferSize);
+ nlpr->packetRecver.bytesUsed = 0;
+ nlpr->packetRecver.bytesAvailable = 0;
+ return nlpr;
+}
+
+MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam)
+{
+ NetlibPacketRecver *nlpr = (NetlibPacketRecver*)hReceiver;
+ if (GetNetlibHandleType(nlpr) != NLH_PACKETRECVER || nlprParam == nullptr || nlprParam->bytesUsed > nlpr->packetRecver.bytesAvailable) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+ if (Miranda_IsTerminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */
+ SetLastError(ERROR_TIMEOUT);
+ return SOCKET_ERROR;
+ }
+ nlpr->packetRecver.dwTimeout = nlprParam->dwTimeout;
+ if (nlprParam->bytesUsed == 0) {
+ if (nlpr->packetRecver.bytesAvailable == nlpr->packetRecver.bufferSize) {
+ nlpr->packetRecver.bytesAvailable = 0;
+ Netlib_Logf(nlpr->nlc->nlu, "Packet recver: packet overflowed buffer, ditching");
+ }
+ }
+ else {
+ memmove(nlpr->packetRecver.buffer, nlpr->packetRecver.buffer + nlprParam->bytesUsed, nlpr->packetRecver.bytesAvailable - nlprParam->bytesUsed);
+ nlpr->packetRecver.bytesAvailable -= nlprParam->bytesUsed;
+ }
+
+ if (nlprParam->dwTimeout != INFINITE) {
+ if (!Netlib_SslPending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s, nlprParam->dwTimeout) <= 0) {
+ *nlprParam = nlpr->packetRecver;
+ return SOCKET_ERROR;
+ }
+ }
+
+ INT_PTR recvResult = Netlib_Recv(nlpr->nlc, (char*)nlpr->packetRecver.buffer + nlpr->packetRecver.bytesAvailable, nlpr->packetRecver.bufferSize - nlpr->packetRecver.bytesAvailable, 0);
+ if (recvResult > 0)
+ nlpr->packetRecver.bytesAvailable += recvResult;
+ *nlprParam = nlpr->packetRecver;
+ return recvResult;
+}
diff --git a/src/mir_app/src/netlib_security.cpp b/src/mir_app/src/netlib_security.cpp index 278991b7e4..90aa9c68ff 100644 --- a/src/mir_app/src/netlib_security.cpp +++ b/src/mir_app/src/netlib_security.cpp @@ -1,363 +1,363 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#define SECURITY_WIN32 -#include <security.h> -#include <rpcdce.h> - -#pragma comment(lib, "secur32.lib") - -struct NtlmHandleType -{ - CtxtHandle hClientContext; - CredHandle hClientCredential; - wchar_t* szProvider; - wchar_t* szPrincipal; - unsigned cbMaxToken; - bool hasDomain; -}; - -struct NTLM_String -{ - uint16_t len; - uint16_t allocedSpace; - uint32_t offset; -}; - -struct NtlmType2packet -{ - char sign[8]; - uint32_t type; // == 2 - NTLM_String targetName; - uint32_t flags; - uint8_t challenge[8]; - uint8_t context[8]; - NTLM_String targetInfo; -}; - -static unsigned ntlmCnt = 0; -static mir_cs csSec; - -static void ReportSecError(SECURITY_STATUS scRet, int line) -{ - wchar_t szMsgBuf[256]; - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr); - - wchar_t *p = wcschr(szMsgBuf, 13); if (p) *p = 0; - - Netlib_LogfW(nullptr, L"Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProvider, const wchar_t *szPrincipal) -{ - HANDLE hSecurity = nullptr; - - if (mir_wstrcmpi(szProvider, L"Basic") == 0) { - NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); - hNtlm->szProvider = mir_wstrdup(szProvider); - SecInvalidateHandle(&hNtlm->hClientContext); - SecInvalidateHandle(&hNtlm->hClientCredential); - ntlmCnt++; - - return hNtlm; - } - - mir_cslock lck(csSec); - - PSecPkgInfo ntlmSecurityPackageInfo; - SECURITY_STATUS sc = QuerySecurityPackageInfo((LPTSTR)szProvider, &ntlmSecurityPackageInfo); - if (sc == SEC_E_OK) { - NtlmHandleType* hNtlm; - - hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); - hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken; - FreeContextBuffer(ntlmSecurityPackageInfo); - - hNtlm->szProvider = mir_wstrdup(szProvider); - hNtlm->szPrincipal = mir_wstrdup(szPrincipal ? szPrincipal : L""); - SecInvalidateHandle(&hNtlm->hClientContext); - SecInvalidateHandle(&hNtlm->hClientCredential); - ntlmCnt++; - } - return hSecurity; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hSecurity) -{ - if (hSecurity == nullptr) - return; - - mir_cslock lck(csSec); - - if (ntlmCnt != 0) { - NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; - if (hNtlm != nullptr) { - if (SecIsValidHandle(&hNtlm->hClientContext)) - DeleteSecurityContext(&hNtlm->hClientContext); - if (SecIsValidHandle(&hNtlm->hClientCredential)) - FreeCredentialsHandle(&hNtlm->hClientCredential); - mir_free(hNtlm->szProvider); - mir_free(hNtlm->szPrincipal); - mir_free(hNtlm); - } - - --ntlmCnt; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz) -{ - if (!szChallenge || !szChallenge[0]) return nullptr; - - NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; - unsigned char inDataBuffer[1024]; - - SecBuffer inBuffers[2] = - { - { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer }, - { chlsz, SECBUFFER_STREAM, szChallenge } - }; - - SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers }; - - unsigned long qop = 0; - SECURITY_STATUS sc = DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - // unsigned char LayerMask = inDataBuffer[0]; - // unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]); - - SecPkgContext_Sizes sizes; - sc = QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer); - unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize); - - unsigned char outDataBuffer[4] = { 1, 0, 16, 0 }; - - SecBuffer outBuffers[3] = - { - { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer }, - { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer }, - { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer } - }; - SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers }; - - sc = EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - unsigned i, ressz = 0; - for (i = 0; i < outBuffersDesc.cBuffers; i++) - ressz += outBuffersDesc.pBuffers[i].cbBuffer; - - unsigned char *response = (unsigned char*)alloca(ressz), *p = response; - for (i = 0; i < outBuffersDesc.cBuffers; i++) { - memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer); - p += outBuffersDesc.pBuffers[i].cbBuffer; - } - - return mir_base64_encode(response, ressz); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const wchar_t *login, const wchar_t *psw, bool http, unsigned &complete) -{ - if (hSecurity == nullptr || ntlmCnt == 0) - return nullptr; - - SecBufferDesc outputBufferDescriptor, inputBufferDescriptor; - SecBuffer outputSecurityToken, inputSecurityToken; - char *szOutputToken; - - NtlmHandleType *hNtlm = (NtlmHandleType*)hSecurity; - - Netlib_Logf(nullptr, "NtlmCreateResponseFromChallenge (%s): chl=%s {%S:%S} => %d", hNtlm->szProvider, szChallenge, login, psw, complete); - - if (mir_wstrcmpi(hNtlm->szProvider, L"Basic")) { - bool isGSSAPI = mir_wstrcmpi(hNtlm->szProvider, L"Kerberos") == 0; - bool hasChallenge = szChallenge != nullptr && szChallenge[0] != '\0'; - if (hasChallenge) { - size_t tokenLen; - uint8_t *token = (uint8_t*)mir_base64_decode(szChallenge, &tokenLen); - if (token == nullptr) - return nullptr; - - if (isGSSAPI && complete) - return CompleteGssapi(hSecurity, token, (unsigned)tokenLen); - - inputBufferDescriptor.cBuffers = 1; - inputBufferDescriptor.pBuffers = &inputSecurityToken; - inputBufferDescriptor.ulVersion = SECBUFFER_VERSION; - inputSecurityToken.BufferType = SECBUFFER_TOKEN; - inputSecurityToken.cbBuffer = (unsigned)tokenLen; - inputSecurityToken.pvBuffer = token; - - // try to decode the domain name from the NTLM challenge - if (login != nullptr && login[0] != '\0' && !hNtlm->hasDomain) { - NtlmType2packet* pkt = (NtlmType2packet*)token; - if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2) { - - wchar_t* domainName = (wchar_t*)&token[pkt->targetName.offset]; - int domainLen = pkt->targetName.len; - - // Negotiate ANSI? if yes, convert the ANSI name to unicode - if ((pkt->flags & 1) == 0) { - int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, nullptr, 0); - wchar_t* buf = (wchar_t*)alloca((bufsz+1) * sizeof(wchar_t)); - domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1; - buf[domainLen] = 0; - domainName = buf; - } - else domainLen /= sizeof(wchar_t); - - if (domainLen) { - CMStringW wszNewLogin(FORMAT, L"%s\\%s", domainName, login); - char* szChl = NtlmCreateResponseFromChallenge(hSecurity, nullptr, wszNewLogin, psw, http, complete); - mir_free(szChl); - } - } - } - } - else { - if (SecIsValidHandle(&hNtlm->hClientContext)) - DeleteSecurityContext(&hNtlm->hClientContext); - if (SecIsValidHandle(&hNtlm->hClientCredential)) - FreeCredentialsHandle(&hNtlm->hClientCredential); - - SEC_WINNT_AUTH_IDENTITY auth; - - if (login != nullptr && login[0] != '\0') { - memset(&auth, 0, sizeof(auth)); - - Netlib_Logf(nullptr, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)"); - - const wchar_t* loginName = login; - const wchar_t* domainName = wcschr(login, '\\'); - size_t domainLen = 0; - size_t loginLen = mir_wstrlen(loginName); - if (domainName != nullptr) { - loginName = domainName + 1; - loginLen = mir_wstrlen(loginName); - domainLen = domainName - login; - domainName = login; - } - else if ((domainName = wcschr(login, '@')) != nullptr) { - loginName = login; - loginLen = domainName - login; - domainLen = mir_wstrlen(++domainName); - } - - auth.User = (PWORD)loginName; - auth.UserLength = (ULONG)loginLen; - auth.Password = (PWORD)psw; - auth.PasswordLength = (ULONG)mir_wstrlen(psw); - auth.Domain = (PWORD)domainName; - auth.DomainLength = (ULONG)domainLen; - auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; - - hNtlm->hasDomain = domainLen != 0; - } - - TimeStamp tokenExpiration; - SECURITY_STATUS sc = AcquireCredentialsHandle(nullptr, hNtlm->szProvider, SECPKG_CRED_OUTBOUND, nullptr, hNtlm->hasDomain ? &auth : nullptr, nullptr, nullptr, &hNtlm->hClientCredential, &tokenExpiration); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - } - - outputBufferDescriptor.cBuffers = 1; - outputBufferDescriptor.pBuffers = &outputSecurityToken; - outputBufferDescriptor.ulVersion = SECBUFFER_VERSION; - outputSecurityToken.BufferType = SECBUFFER_TOKEN; - outputSecurityToken.cbBuffer = hNtlm->cbMaxToken; - outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer); - - ULONG contextAttributes; - TimeStamp tokenExpiration; - SECURITY_STATUS sc = InitializeSecurityContext(&hNtlm->hClientCredential, - hasChallenge ? &hNtlm->hClientContext : nullptr, - hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP, - hasChallenge ? &inputBufferDescriptor : nullptr, 0, &hNtlm->hClientContext, - &outputBufferDescriptor, &contextAttributes, &tokenExpiration); - Netlib_Logf(nullptr, "InitializeSecurityContext(%S): 0x%x", hNtlm->szProvider, sc); - - complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED); - if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE) { - sc = CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor); - Netlib_Logf(nullptr, "CompleteAuthToken: 0x%x", sc); - } - - if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - szOutputToken = mir_base64_encode(outputSecurityToken.pvBuffer, outputSecurityToken.cbBuffer); - } - else { - if (!login || !psw) - return nullptr; - - T2Utf szAuth(CMStringW(FORMAT, L"%s:%s", login, psw)); - szOutputToken = mir_base64_encode(szAuth.get(), mir_strlen(szAuth)); - complete = true; - } - - if (szOutputToken == nullptr) - return nullptr; - - if (!http) - return szOutputToken; - - CMStringA szResult(FORMAT, "%S %s", hNtlm->szProvider, szOutputToken); - mir_free(szOutputToken); - return szResult.Detach(); -} - -MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *pwszLogin, wchar_t *pwszPassword, unsigned &complete) -{ - return NtlmCreateResponseFromChallenge(hProvider, szChallenge, pwszLogin, pwszPassword, false, complete); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#define SECURITY_WIN32
+#include <security.h>
+#include <rpcdce.h>
+
+#pragma comment(lib, "secur32.lib")
+
+struct NtlmHandleType
+{
+ CtxtHandle hClientContext;
+ CredHandle hClientCredential;
+ wchar_t* szProvider;
+ wchar_t* szPrincipal;
+ unsigned cbMaxToken;
+ bool hasDomain;
+};
+
+struct NTLM_String
+{
+ uint16_t len;
+ uint16_t allocedSpace;
+ uint32_t offset;
+};
+
+struct NtlmType2packet
+{
+ char sign[8];
+ uint32_t type; // == 2
+ NTLM_String targetName;
+ uint32_t flags;
+ uint8_t challenge[8];
+ uint8_t context[8];
+ NTLM_String targetInfo;
+};
+
+static unsigned ntlmCnt = 0;
+static mir_cs csSec;
+
+static void ReportSecError(SECURITY_STATUS scRet, int line)
+{
+ wchar_t szMsgBuf[256];
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr);
+
+ wchar_t *p = wcschr(szMsgBuf, 13); if (p) *p = 0;
+
+ Netlib_LogfW(nullptr, L"Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProvider, const wchar_t *szPrincipal)
+{
+ HANDLE hSecurity = nullptr;
+
+ if (mir_wstrcmpi(szProvider, L"Basic") == 0) {
+ NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
+ hNtlm->szProvider = mir_wstrdup(szProvider);
+ SecInvalidateHandle(&hNtlm->hClientContext);
+ SecInvalidateHandle(&hNtlm->hClientCredential);
+ ntlmCnt++;
+
+ return hNtlm;
+ }
+
+ mir_cslock lck(csSec);
+
+ PSecPkgInfo ntlmSecurityPackageInfo;
+ SECURITY_STATUS sc = QuerySecurityPackageInfo((LPTSTR)szProvider, &ntlmSecurityPackageInfo);
+ if (sc == SEC_E_OK) {
+ NtlmHandleType* hNtlm;
+
+ hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
+ hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken;
+ FreeContextBuffer(ntlmSecurityPackageInfo);
+
+ hNtlm->szProvider = mir_wstrdup(szProvider);
+ hNtlm->szPrincipal = mir_wstrdup(szPrincipal ? szPrincipal : L"");
+ SecInvalidateHandle(&hNtlm->hClientContext);
+ SecInvalidateHandle(&hNtlm->hClientCredential);
+ ntlmCnt++;
+ }
+ return hSecurity;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hSecurity)
+{
+ if (hSecurity == nullptr)
+ return;
+
+ mir_cslock lck(csSec);
+
+ if (ntlmCnt != 0) {
+ NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
+ if (hNtlm != nullptr) {
+ if (SecIsValidHandle(&hNtlm->hClientContext))
+ DeleteSecurityContext(&hNtlm->hClientContext);
+ if (SecIsValidHandle(&hNtlm->hClientCredential))
+ FreeCredentialsHandle(&hNtlm->hClientCredential);
+ mir_free(hNtlm->szProvider);
+ mir_free(hNtlm->szPrincipal);
+ mir_free(hNtlm);
+ }
+
+ --ntlmCnt;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz)
+{
+ if (!szChallenge || !szChallenge[0]) return nullptr;
+
+ NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
+ unsigned char inDataBuffer[1024];
+
+ SecBuffer inBuffers[2] =
+ {
+ { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer },
+ { chlsz, SECBUFFER_STREAM, szChallenge }
+ };
+
+ SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers };
+
+ unsigned long qop = 0;
+ SECURITY_STATUS sc = DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ // unsigned char LayerMask = inDataBuffer[0];
+ // unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]);
+
+ SecPkgContext_Sizes sizes;
+ sc = QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer);
+ unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize);
+
+ unsigned char outDataBuffer[4] = { 1, 0, 16, 0 };
+
+ SecBuffer outBuffers[3] =
+ {
+ { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer },
+ { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer },
+ { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer }
+ };
+ SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers };
+
+ sc = EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ unsigned i, ressz = 0;
+ for (i = 0; i < outBuffersDesc.cBuffers; i++)
+ ressz += outBuffersDesc.pBuffers[i].cbBuffer;
+
+ unsigned char *response = (unsigned char*)alloca(ressz), *p = response;
+ for (i = 0; i < outBuffersDesc.cBuffers; i++) {
+ memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer);
+ p += outBuffersDesc.pBuffers[i].cbBuffer;
+ }
+
+ return mir_base64_encode(response, ressz);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const wchar_t *login, const wchar_t *psw, bool http, unsigned &complete)
+{
+ if (hSecurity == nullptr || ntlmCnt == 0)
+ return nullptr;
+
+ SecBufferDesc outputBufferDescriptor, inputBufferDescriptor;
+ SecBuffer outputSecurityToken, inputSecurityToken;
+ char *szOutputToken;
+
+ NtlmHandleType *hNtlm = (NtlmHandleType*)hSecurity;
+
+ Netlib_Logf(nullptr, "NtlmCreateResponseFromChallenge (%s): chl=%s {%S:%S} => %d", hNtlm->szProvider, szChallenge, login, psw, complete);
+
+ if (mir_wstrcmpi(hNtlm->szProvider, L"Basic")) {
+ bool isGSSAPI = mir_wstrcmpi(hNtlm->szProvider, L"Kerberos") == 0;
+ bool hasChallenge = szChallenge != nullptr && szChallenge[0] != '\0';
+ if (hasChallenge) {
+ size_t tokenLen;
+ uint8_t *token = (uint8_t*)mir_base64_decode(szChallenge, &tokenLen);
+ if (token == nullptr)
+ return nullptr;
+
+ if (isGSSAPI && complete)
+ return CompleteGssapi(hSecurity, token, (unsigned)tokenLen);
+
+ inputBufferDescriptor.cBuffers = 1;
+ inputBufferDescriptor.pBuffers = &inputSecurityToken;
+ inputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+ inputSecurityToken.BufferType = SECBUFFER_TOKEN;
+ inputSecurityToken.cbBuffer = (unsigned)tokenLen;
+ inputSecurityToken.pvBuffer = token;
+
+ // try to decode the domain name from the NTLM challenge
+ if (login != nullptr && login[0] != '\0' && !hNtlm->hasDomain) {
+ NtlmType2packet* pkt = (NtlmType2packet*)token;
+ if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2) {
+
+ wchar_t* domainName = (wchar_t*)&token[pkt->targetName.offset];
+ int domainLen = pkt->targetName.len;
+
+ // Negotiate ANSI? if yes, convert the ANSI name to unicode
+ if ((pkt->flags & 1) == 0) {
+ int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, nullptr, 0);
+ wchar_t* buf = (wchar_t*)alloca((bufsz+1) * sizeof(wchar_t));
+ domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1;
+ buf[domainLen] = 0;
+ domainName = buf;
+ }
+ else domainLen /= sizeof(wchar_t);
+
+ if (domainLen) {
+ CMStringW wszNewLogin(FORMAT, L"%s\\%s", domainName, login);
+ char* szChl = NtlmCreateResponseFromChallenge(hSecurity, nullptr, wszNewLogin, psw, http, complete);
+ mir_free(szChl);
+ }
+ }
+ }
+ }
+ else {
+ if (SecIsValidHandle(&hNtlm->hClientContext))
+ DeleteSecurityContext(&hNtlm->hClientContext);
+ if (SecIsValidHandle(&hNtlm->hClientCredential))
+ FreeCredentialsHandle(&hNtlm->hClientCredential);
+
+ SEC_WINNT_AUTH_IDENTITY auth;
+
+ if (login != nullptr && login[0] != '\0') {
+ memset(&auth, 0, sizeof(auth));
+
+ Netlib_Logf(nullptr, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)");
+
+ const wchar_t* loginName = login;
+ const wchar_t* domainName = wcschr(login, '\\');
+ size_t domainLen = 0;
+ size_t loginLen = mir_wstrlen(loginName);
+ if (domainName != nullptr) {
+ loginName = domainName + 1;
+ loginLen = mir_wstrlen(loginName);
+ domainLen = domainName - login;
+ domainName = login;
+ }
+ else if ((domainName = wcschr(login, '@')) != nullptr) {
+ loginName = login;
+ loginLen = domainName - login;
+ domainLen = mir_wstrlen(++domainName);
+ }
+
+ auth.User = (PWORD)loginName;
+ auth.UserLength = (ULONG)loginLen;
+ auth.Password = (PWORD)psw;
+ auth.PasswordLength = (ULONG)mir_wstrlen(psw);
+ auth.Domain = (PWORD)domainName;
+ auth.DomainLength = (ULONG)domainLen;
+ auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+
+ hNtlm->hasDomain = domainLen != 0;
+ }
+
+ TimeStamp tokenExpiration;
+ SECURITY_STATUS sc = AcquireCredentialsHandle(nullptr, hNtlm->szProvider, SECPKG_CRED_OUTBOUND, nullptr, hNtlm->hasDomain ? &auth : nullptr, nullptr, nullptr, &hNtlm->hClientCredential, &tokenExpiration);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+ }
+
+ outputBufferDescriptor.cBuffers = 1;
+ outputBufferDescriptor.pBuffers = &outputSecurityToken;
+ outputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+ outputSecurityToken.BufferType = SECBUFFER_TOKEN;
+ outputSecurityToken.cbBuffer = hNtlm->cbMaxToken;
+ outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer);
+
+ ULONG contextAttributes;
+ TimeStamp tokenExpiration;
+ SECURITY_STATUS sc = InitializeSecurityContext(&hNtlm->hClientCredential,
+ hasChallenge ? &hNtlm->hClientContext : nullptr,
+ hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP,
+ hasChallenge ? &inputBufferDescriptor : nullptr, 0, &hNtlm->hClientContext,
+ &outputBufferDescriptor, &contextAttributes, &tokenExpiration);
+ Netlib_Logf(nullptr, "InitializeSecurityContext(%S): 0x%x", hNtlm->szProvider, sc);
+
+ complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED);
+ if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE) {
+ sc = CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor);
+ Netlib_Logf(nullptr, "CompleteAuthToken: 0x%x", sc);
+ }
+
+ if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ szOutputToken = mir_base64_encode(outputSecurityToken.pvBuffer, outputSecurityToken.cbBuffer);
+ }
+ else {
+ if (!login || !psw)
+ return nullptr;
+
+ T2Utf szAuth(CMStringW(FORMAT, L"%s:%s", login, psw));
+ szOutputToken = mir_base64_encode(szAuth.get(), mir_strlen(szAuth));
+ complete = true;
+ }
+
+ if (szOutputToken == nullptr)
+ return nullptr;
+
+ if (!http)
+ return szOutputToken;
+
+ CMStringA szResult(FORMAT, "%S %s", hNtlm->szProvider, szOutputToken);
+ mir_free(szOutputToken);
+ return szResult.Detach();
+}
+
+MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *pwszLogin, wchar_t *pwszPassword, unsigned &complete)
+{
+ return NtlmCreateResponseFromChallenge(hProvider, szChallenge, pwszLogin, pwszPassword, false, complete);
+}
diff --git a/src/mir_app/src/netlib_sock.cpp b/src/mir_app/src/netlib_sock.cpp index ebd7f3a7ea..032bfed993 100644 --- a/src/mir_app/src/netlib_sock.cpp +++ b/src/mir_app/src/netlib_sock.cpp @@ -1,287 +1,287 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -extern HANDLE hSendEvent, hRecvEvent; - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN nlc, const char *buf, int len, int flags) -{ - if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) - return SOCKET_ERROR; - - int result; - Netlib_Dump(nlc, (uint8_t*)buf, len, true, flags); - if (nlc->hSsl) - result = Netlib_SslWrite(nlc->hSsl, buf, len); - else - result = send(nlc->s, buf, len, flags & 0xFFFF); - - NetlibLeaveNestedCS(&nlc->ncsSend); - - NETLIBNOTIFY nln = { buf, len, flags, result }; - NotifyFastHook(hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); - - return result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN nlc, char *buf, int len, int flags) -{ - if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) - return SOCKET_ERROR; - - int recvResult; - if (!nlc->foreBuf.isEmpty()) { - recvResult = min(len, (int)nlc->foreBuf.length()); - memcpy(buf, nlc->foreBuf.data(), recvResult); - nlc->foreBuf.remove(recvResult); - } - else if (nlc->hSsl) - recvResult = Netlib_SslRead(nlc->hSsl, buf, len, (flags & MSG_PEEK) != 0); - else - recvResult = recv(nlc->s, buf, len, flags & 0xFFFF); - - NetlibLeaveNestedCS(&nlc->ncsRecv); - if (recvResult <= 0) - return recvResult; - - Netlib_Dump(nlc, (uint8_t*)buf, recvResult, false, flags); - - if ((flags & MSG_PEEK) == 0) { - NETLIBNOTIFY nln = { buf, len, flags, recvResult }; - NotifyFastHook(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); - } - return recvResult; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int ConnectionListToSocketList(const HNETLIBCONN *hConns, fd_set *fd, int& pending) -{ - FD_ZERO(fd); - for (int i = 0; hConns[i] && hConns[i] != INVALID_HANDLE_VALUE && i < FD_SETSIZE; i++) { - NetlibConnection *nlcCheck = hConns[i]; - if (nlcCheck->handleType != NLH_CONNECTION && nlcCheck->handleType != NLH_BOUNDPORT) { - SetLastError(ERROR_INVALID_DATA); - return 0; - } - FD_SET(nlcCheck->s, fd); - if (!nlcCheck->foreBuf.isEmpty() || Netlib_SslPending(nlcCheck->hSsl)) - pending++; - } - return 1; -} - -MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls) -{ - if (nls == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - int pending = 0; - fd_set readfd, writefd, exceptfd; - { - mir_cslock lock(csConnectionHeader); - if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) - || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) - || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) { - return SOCKET_ERROR; - } - } - if (pending) - return 1; - - TIMEVAL tv; - tv.tv_sec = nls->dwTimeout / 1000; - tv.tv_usec = (nls->dwTimeout % 1000) * 1000; - return select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); -} - -MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls) -{ - if (nls == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - int pending = 0; - fd_set readfd, writefd, exceptfd; - - TIMEVAL tv; - tv.tv_sec = nls->dwTimeout / 1000; - tv.tv_usec = (nls->dwTimeout % 1000) * 1000; - { - mir_cslock lock(csConnectionHeader); - - if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) - || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) - || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) { - return SOCKET_ERROR; - } - } - - int rc = (pending) ? pending : select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); - - mir_cslock lock(csConnectionHeader); - /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET() - to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not) - This happens for read/write/except */ - NetlibConnection *conn = nullptr; - int j; - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hReadConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - - if (Netlib_SslPending(conn->hSsl)) - nls->hReadStatus[j] = TRUE; - nls->hReadStatus[j] = FD_ISSET(conn->s, &readfd); - } - - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hWriteConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - nls->hWriteStatus[j] = FD_ISSET(conn->s, &writefd); - } - - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hExceptConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - nls->hExceptStatus[j] = FD_ISSET(conn->s, &exceptfd); - } - return rc; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, SOCKADDR_INET_M *addr) -{ - if (!str) return false; - - int len = sizeof(SOCKADDR_INET_M); - return !WSAStringToAddressA((char*)str, AF_INET6, nullptr, (PSOCKADDR)addr, &len); -} - -MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr) -{ - char saddr[128]; - DWORD len = sizeof(saddr); - if (!WSAAddressToStringA((PSOCKADDR)addr, sizeof(*addr), nullptr, saddr, &len)) - return mir_strdup(saddr); - - if (addr->sin_family == AF_INET) { - char *szIp = inet_ntoa(addr->sin_addr); - if (addr->sin_port != 0) { - mir_snprintf(saddr, "%s:%d", szIp, addr->sin_port); - return mir_strdup(saddr); - } - return mir_strdup(szIp); - } - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN nlc, NETLIBCONNINFO *connInfo) -{ - if (!nlc || !connInfo) - return 1; - - sockaddr_in sin = { 0 }; - int len = sizeof(sin); - if (!getsockname(nlc->s, (PSOCKADDR)&sin, &len)) { - connInfo->wPort = ntohs(sin.sin_port); - connInfo->dwIpv4 = sin.sin_family == AF_INET ? htonl(sin.sin_addr.s_addr) : 0; - strncpy_s(connInfo->szIpPort, ptrA(Netlib_AddressToString(&sin)), _TRUNCATE); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -inline bool IsAddrGlobal(const IN6_ADDR *a) -{ - unsigned char High = a->s6_bytes[0] & 0xf0; - return High != 0 && High != 0xf0; -} - -MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly) -{ - addrinfo *air = nullptr, *ai, hints = { 0 }; - const char *szMyHost = ""; - - hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_PASSIVE; - - if (GetAddrInfoA(szMyHost, nullptr, &hints, &air)) - return nullptr; - - unsigned n = 0; - for (ai = air; ai; ai = ai->ai_next) { - SOCKADDR_INET_M *iaddr = (SOCKADDR_INET_M*)ai->ai_addr; - if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->Ipv6.sin6_addr)))) - ++n; - } - - NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); - addr->cbNum = n; - - unsigned i = 0; - for (ai = air; ai; ai = ai->ai_next) { - sockaddr_in6 *iaddr = (sockaddr_in6*)ai->ai_addr; - if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->sin6_addr)))) { - char *szIp = Netlib_AddressToString((sockaddr_in*)iaddr); - if (szIp) - strncpy_s(addr->szIp[i++], szIp, _TRUNCATE); - mir_free(szIp); - } - } - FreeAddrInfoA(air); - return addr; -} - -static NETLIBIPLIST* GetMyIpv4(void) -{ - char hostname[256] = ""; - - gethostname(hostname, sizeof(hostname)); - PHOSTENT he = gethostbyname(hostname); - - unsigned n; - for (n = 0; he->h_addr_list[n]; ++n) - ; - - NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); - addr->cbNum = n; - - for (unsigned i = 0; i < n; i++) - strncpy_s(addr->szIp[i], inet_ntoa(*(PIN_ADDR)he->h_addr_list[i]), _TRUNCATE); - - return addr; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+extern HANDLE hSendEvent, hRecvEvent;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN nlc, const char *buf, int len, int flags)
+{
+ if (!NetlibEnterNestedCS(nlc, NLNCS_SEND))
+ return SOCKET_ERROR;
+
+ int result;
+ Netlib_Dump(nlc, (uint8_t*)buf, len, true, flags);
+ if (nlc->hSsl)
+ result = Netlib_SslWrite(nlc->hSsl, buf, len);
+ else
+ result = send(nlc->s, buf, len, flags & 0xFFFF);
+
+ NetlibLeaveNestedCS(&nlc->ncsSend);
+
+ NETLIBNOTIFY nln = { buf, len, flags, result };
+ NotifyFastHook(hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user);
+
+ return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN nlc, char *buf, int len, int flags)
+{
+ if (!NetlibEnterNestedCS(nlc, NLNCS_RECV))
+ return SOCKET_ERROR;
+
+ int recvResult;
+ if (!nlc->foreBuf.isEmpty()) {
+ recvResult = min(len, (int)nlc->foreBuf.length());
+ memcpy(buf, nlc->foreBuf.data(), recvResult);
+ nlc->foreBuf.remove(recvResult);
+ }
+ else if (nlc->hSsl)
+ recvResult = Netlib_SslRead(nlc->hSsl, buf, len, (flags & MSG_PEEK) != 0);
+ else
+ recvResult = recv(nlc->s, buf, len, flags & 0xFFFF);
+
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ if (recvResult <= 0)
+ return recvResult;
+
+ Netlib_Dump(nlc, (uint8_t*)buf, recvResult, false, flags);
+
+ if ((flags & MSG_PEEK) == 0) {
+ NETLIBNOTIFY nln = { buf, len, flags, recvResult };
+ NotifyFastHook(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user);
+ }
+ return recvResult;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int ConnectionListToSocketList(const HNETLIBCONN *hConns, fd_set *fd, int& pending)
+{
+ FD_ZERO(fd);
+ for (int i = 0; hConns[i] && hConns[i] != INVALID_HANDLE_VALUE && i < FD_SETSIZE; i++) {
+ NetlibConnection *nlcCheck = hConns[i];
+ if (nlcCheck->handleType != NLH_CONNECTION && nlcCheck->handleType != NLH_BOUNDPORT) {
+ SetLastError(ERROR_INVALID_DATA);
+ return 0;
+ }
+ FD_SET(nlcCheck->s, fd);
+ if (!nlcCheck->foreBuf.isEmpty() || Netlib_SslPending(nlcCheck->hSsl))
+ pending++;
+ }
+ return 1;
+}
+
+MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls)
+{
+ if (nls == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ int pending = 0;
+ fd_set readfd, writefd, exceptfd;
+ {
+ mir_cslock lock(csConnectionHeader);
+ if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending)
+ || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending)
+ || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) {
+ return SOCKET_ERROR;
+ }
+ }
+ if (pending)
+ return 1;
+
+ TIMEVAL tv;
+ tv.tv_sec = nls->dwTimeout / 1000;
+ tv.tv_usec = (nls->dwTimeout % 1000) * 1000;
+ return select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv);
+}
+
+MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls)
+{
+ if (nls == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ int pending = 0;
+ fd_set readfd, writefd, exceptfd;
+
+ TIMEVAL tv;
+ tv.tv_sec = nls->dwTimeout / 1000;
+ tv.tv_usec = (nls->dwTimeout % 1000) * 1000;
+ {
+ mir_cslock lock(csConnectionHeader);
+
+ if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending)
+ || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending)
+ || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) {
+ return SOCKET_ERROR;
+ }
+ }
+
+ int rc = (pending) ? pending : select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv);
+
+ mir_cslock lock(csConnectionHeader);
+ /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET()
+ to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not)
+ This happens for read/write/except */
+ NetlibConnection *conn = nullptr;
+ int j;
+ for (j = 0; j < FD_SETSIZE; j++) {
+ conn = (NetlibConnection*)nls->hReadConns[j];
+ if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
+
+ if (Netlib_SslPending(conn->hSsl))
+ nls->hReadStatus[j] = TRUE;
+ nls->hReadStatus[j] = FD_ISSET(conn->s, &readfd);
+ }
+
+ for (j = 0; j < FD_SETSIZE; j++) {
+ conn = (NetlibConnection*)nls->hWriteConns[j];
+ if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
+ nls->hWriteStatus[j] = FD_ISSET(conn->s, &writefd);
+ }
+
+ for (j = 0; j < FD_SETSIZE; j++) {
+ conn = (NetlibConnection*)nls->hExceptConns[j];
+ if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
+ nls->hExceptStatus[j] = FD_ISSET(conn->s, &exceptfd);
+ }
+ return rc;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, SOCKADDR_INET_M *addr)
+{
+ if (!str) return false;
+
+ int len = sizeof(SOCKADDR_INET_M);
+ return !WSAStringToAddressA((char*)str, AF_INET6, nullptr, (PSOCKADDR)addr, &len);
+}
+
+MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr)
+{
+ char saddr[128];
+ DWORD len = sizeof(saddr);
+ if (!WSAAddressToStringA((PSOCKADDR)addr, sizeof(*addr), nullptr, saddr, &len))
+ return mir_strdup(saddr);
+
+ if (addr->sin_family == AF_INET) {
+ char *szIp = inet_ntoa(addr->sin_addr);
+ if (addr->sin_port != 0) {
+ mir_snprintf(saddr, "%s:%d", szIp, addr->sin_port);
+ return mir_strdup(saddr);
+ }
+ return mir_strdup(szIp);
+ }
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN nlc, NETLIBCONNINFO *connInfo)
+{
+ if (!nlc || !connInfo)
+ return 1;
+
+ sockaddr_in sin = { 0 };
+ int len = sizeof(sin);
+ if (!getsockname(nlc->s, (PSOCKADDR)&sin, &len)) {
+ connInfo->wPort = ntohs(sin.sin_port);
+ connInfo->dwIpv4 = sin.sin_family == AF_INET ? htonl(sin.sin_addr.s_addr) : 0;
+ strncpy_s(connInfo->szIpPort, ptrA(Netlib_AddressToString(&sin)), _TRUNCATE);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+inline bool IsAddrGlobal(const IN6_ADDR *a)
+{
+ unsigned char High = a->s6_bytes[0] & 0xf0;
+ return High != 0 && High != 0xf0;
+}
+
+MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly)
+{
+ addrinfo *air = nullptr, *ai, hints = { 0 };
+ const char *szMyHost = "";
+
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_PASSIVE;
+
+ if (GetAddrInfoA(szMyHost, nullptr, &hints, &air))
+ return nullptr;
+
+ unsigned n = 0;
+ for (ai = air; ai; ai = ai->ai_next) {
+ SOCKADDR_INET_M *iaddr = (SOCKADDR_INET_M*)ai->ai_addr;
+ if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->Ipv6.sin6_addr))))
+ ++n;
+ }
+
+ NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4);
+ addr->cbNum = n;
+
+ unsigned i = 0;
+ for (ai = air; ai; ai = ai->ai_next) {
+ sockaddr_in6 *iaddr = (sockaddr_in6*)ai->ai_addr;
+ if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->sin6_addr)))) {
+ char *szIp = Netlib_AddressToString((sockaddr_in*)iaddr);
+ if (szIp)
+ strncpy_s(addr->szIp[i++], szIp, _TRUNCATE);
+ mir_free(szIp);
+ }
+ }
+ FreeAddrInfoA(air);
+ return addr;
+}
+
+static NETLIBIPLIST* GetMyIpv4(void)
+{
+ char hostname[256] = "";
+
+ gethostname(hostname, sizeof(hostname));
+ PHOSTENT he = gethostbyname(hostname);
+
+ unsigned n;
+ for (n = 0; he->h_addr_list[n]; ++n)
+ ;
+
+ NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4);
+ addr->cbNum = n;
+
+ for (unsigned i = 0; i < n; i++)
+ strncpy_s(addr->szIp[i], inet_ntoa(*(PIN_ADDR)he->h_addr_list[i]), _TRUNCATE);
+
+ return addr;
+}
diff --git a/src/mir_app/src/netlib_ssl.cpp b/src/mir_app/src/netlib_ssl.cpp index d2ab355ec6..14d2ca5d13 100644 --- a/src/mir_app/src/netlib_ssl.cpp +++ b/src/mir_app/src/netlib_ssl.cpp @@ -1,465 +1,465 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#include <openssl/ssl.h> -#include <openssl/err.h> -#include <openssl/rand.h> - -static bool bSslInitDone; - -enum SocketState -{ - sockOpen, - sockClosed, - sockError -}; - -struct SslHandle : public MZeroedObject -{ - ~SslHandle() - { - if (session) - SSL_free(session); - } - - SOCKET s; - SSL *session; - SocketState state; -}; - -static SSL_CTX *g_ctx; -static mir_cs csSsl; - -static void dump_error(SSL *session, int err) -{ - err = SSL_get_error(session, err); - - char buf[100]; - ERR_error_string_n(err, buf, sizeof(buf)); - Netlib_Logf(nullptr, "SSL negotiation failure: %s (%d)", buf, err); -} - -const char* SSL_GetCipherName(SslHandle *ssl) -{ - if (!ssl || !ssl->session) - return nullptr; - - return SSL_CIPHER_get_name(SSL_get_current_cipher(ssl->session)); -} - -static void ReportSslError(SECURITY_STATUS scRet, int line, bool = false) -{ - CMStringW tszMsg(FORMAT, L"SSL connection failure(%x %u) :", scRet, line); - - switch (scRet) { - case 0: - case ERROR_NOT_READY: - return; - - case SEC_E_INVALID_TOKEN: - tszMsg += TranslateW_LP(L"Client cannot decode host message. Possible causes: host does not support SSL or requires not existing security package"); - break; - - case CERT_E_CN_NO_MATCH: - case SEC_E_WRONG_PRINCIPAL: - tszMsg += TranslateW_LP(L"Host we are connecting to is not the one certificate was issued for"); - break; - - default: - wchar_t szMsgBuf[256]; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr); - tszMsg += szMsgBuf; - } - - Netlib_LogfW(nullptr, tszMsg); - - SetLastError(scRet); - PUShowMessageW(tszMsg.GetBuffer(), SM_WARNING); -} - -static PCCERT_CONTEXT SSL_X509ToCryptCert(X509 * x509) -{ - unsigned char *buf = nullptr; - PCCERT_CONTEXT pCertContext = nullptr; - - int len = i2d_X509(x509, &buf); - if ((len >= 0) && buf) { - pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, buf, len); - - CRYPTO_free(buf, __FILE__, __LINE__); - } - return pCertContext; -} - -static PCCERT_CONTEXT SSL_CertChainToCryptAnchor(SSL* session) -{ - /* convert the active certificate chain provided in the handshake of 'session' into - the format used by CryptAPI. - */ - PCCERT_CONTEXT anchor = nullptr; - // create cert store - HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, nullptr); - - if (store) { - X509 *server_cert = SSL_get_peer_certificate(session); - if (server_cert) { - // add the server's cert first, to make sure CryptAPI builds the correct chain - PCCERT_CONTEXT primary_cert; - BOOL ok = CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(server_cert), CERT_STORE_ADD_ALWAYS, &primary_cert); - if (ok && primary_cert) { - // add all remaining certs to store (note: stack needs not be freed, it is not a copy) - STACK_OF(X509) *server_chain = SSL_get_peer_cert_chain(session); - if (server_chain) { - for (int i = 0; i < OPENSSL_sk_num((OPENSSL_STACK *)server_chain); i++) { - X509 *next_cert = (X509 *)OPENSSL_sk_value((OPENSSL_STACK *)server_chain, i); - CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(next_cert), CERT_STORE_ADD_USE_EXISTING, nullptr); - } - } - - // return primary cert; MUST be freed by caller which will free the associated store - anchor = primary_cert; - } - else { - if (primary_cert) - CertFreeCertificateContext(primary_cert); - } - - X509_free(server_cert); - } - - CertCloseStore(store, 0); - } - - return anchor; -} - -static LPSTR rgszUsages[] = -{ - szOID_PKIX_KP_SERVER_AUTH, - szOID_SERVER_GATED_CRYPTO, - szOID_SGC_NETSCAPE -}; - -static bool VerifyCertificate(SslHandle *ssl, PCSTR pszServerName, uint32_t dwCertFlags) -{ - uint32_t scRet; - - ptrW pwszServerName(mir_a2u(pszServerName)); - - HTTPSPolicyCallbackData polHttps = {}; - CERT_CHAIN_POLICY_PARA PolicyPara = {}; - CERT_CHAIN_POLICY_STATUS PolicyStatus = {}; - CERT_CHAIN_PARA ChainPara = {}; - - PCCERT_CHAIN_CONTEXT pChainContext = nullptr; - PCCERT_CONTEXT pServerCert = SSL_CertChainToCryptAnchor(ssl->session); - if (pServerCert == nullptr) { - scRet = SEC_E_WRONG_PRINCIPAL; - goto cleanup; - } - - ChainPara.cbSize = sizeof(ChainPara); - ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; - ChainPara.RequestedUsage.Usage.cUsageIdentifier = _countof(rgszUsages); - ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; - - if (!CertGetCertificateChain(nullptr, pServerCert, nullptr, pServerCert->hCertStore, &ChainPara, 0, nullptr, &pChainContext)) { - scRet = GetLastError(); - goto cleanup; - } - - polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); - polHttps.dwAuthType = AUTHTYPE_SERVER; - polHttps.fdwChecks = dwCertFlags; - polHttps.pwszServerName = pwszServerName; - - PolicyPara.cbSize = sizeof(PolicyPara); - PolicyPara.pvExtraPolicyPara = &polHttps; - - PolicyStatus.cbSize = sizeof(PolicyStatus); - - if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)) { - scRet = GetLastError(); - goto cleanup; - } - - if (PolicyStatus.dwError) { - scRet = PolicyStatus.dwError; - goto cleanup; - } - - scRet = SEC_E_OK; - -cleanup: - if (pChainContext) - CertFreeCertificateChain(pChainContext); - if (pServerCert) - CertFreeCertificateContext(pServerCert); - - ReportSslError(scRet, __LINE__, true); - return scRet == SEC_E_OK; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// negotiate SSL session, verify cert, return NULL if failed - -MIR_APP_DLL(HSSL) Netlib_SslConnect(SOCKET s, const char* host, int verify) -{ - std::unique_ptr<SslHandle> ssl(new SslHandle()); - ssl->s = s; - { - mir_cslock lck(csSsl); - ssl->session = SSL_new(g_ctx); - } - - if (!ssl->session) { - Netlib_Logf(nullptr, "SSL setup failure: session"); - return false; - } - SSL_set_fd(ssl->session, ssl->s); - - SSL_set_tlsext_host_name(ssl->session, host); - - int err = SSL_connect(ssl->session); - if (err != 1) { - dump_error(ssl->session, err); - return nullptr; - } - - if (verify) { - uint32_t dwFlags = 0; - if (!host || inet_addr(host) != INADDR_NONE) - dwFlags |= 0x00001000; - if (!VerifyCertificate(ssl.get(), host, dwFlags)) - return nullptr; - } - - return ssl.release(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// return true if there is either unsend or buffered received data (ie. after peek) - -MIR_APP_DLL(BOOL) Netlib_SslPending(HSSL ssl) -{ - return ssl && ssl->session && (SSL_pending(ssl->session) > 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// reads number of bytes, keeps in buffer if peek != 0 - -MIR_APP_DLL(int) Netlib_SslRead(HSSL ssl, char *buf, int num, int peek) -{ - if (!ssl || !ssl->session) return SOCKET_ERROR; - if (num <= 0) return 0; - - int err = 0; - if (peek) - err = SSL_peek(ssl->session, buf, num); - else - err = SSL_read(ssl->session, buf, num); - - if (err <= 0) { - int err2 = SSL_get_error(ssl->session, err); - if (err2 == SSL_ERROR_ZERO_RETURN) { - Netlib_Logf(nullptr, "SSL connection gracefully closed"); - ssl->state = sockClosed; - return 0; - } - - int err3 = ERR_get_error(); - if (err3) { - Netlib_Logf(nullptr, "SSL failure recieving data (%d, %d, %d, %d)", err, err2, err3, WSAGetLastError()); - ssl->state = sockError; - return SOCKET_ERROR; - } - } - - return err; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// writes data to the SSL socket - -MIR_APP_DLL(int) Netlib_SslWrite(HSSL ssl, const char *buf, int num) -{ - if (!ssl || !ssl->session) - return SOCKET_ERROR; - if (num <= 0) - return 0; - - int err = SSL_write(ssl->session, buf, num); - if (err > 0) - return err; - - int err2 = SSL_get_error(ssl->session, err); - switch (err2) { - case SSL_ERROR_ZERO_RETURN: - Netlib_Logf(nullptr, "SSL connection gracefully closed"); - ssl->state = sockClosed; - break; - - default: - Netlib_Logf(nullptr, "SSL failure sending data (%d, %d, %d)", err, err2, WSAGetLastError()); - ssl->state = sockError; - return SOCKET_ERROR; - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// closes SSL session, but keeps socket open - -MIR_APP_DLL(void) Netlib_SslShutdown(HSSL ssl) -{ - if (ssl && ssl->session) { - SOCKET s = SSL_get_fd(ssl->session); - if (s != -1) - shutdown(s, SD_BOTH); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// frees all data associated with the SSL socket - -MIR_APP_DLL(void) Netlib_SslFree(HSSL ssl) -{ - delete ssl; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// makes connection SSL -// returns 0 on failure / 1 on success - -MIR_APP_DLL(int) Netlib_StartSsl(HNETLIBCONN hConnection, const char *szHost) -{ - NetlibConnection *nlc = (NetlibConnection*)hConnection; - if (nlc == nullptr) - return 0; - - NetlibUser *nlu = nlc->nlu; - if (szHost == nullptr) - szHost = nlc->url.szHost; - szHost = NEWSTR_ALLOCA(szHost); - - Netlib_Logf(nlu, "(%d %s) Starting SSL/TLS negotiation", int(nlc->s), szHost); - - nlc->hSsl = Netlib_SslConnect(nlc->s, szHost, nlu->settings.validateSSL); - if (nlc->hSsl == nullptr) - Netlib_Logf(nlu, "(%d %s) Failure to negotiate SSL/TLS connection", int(nlc->s), szHost); - else - Netlib_Logf(nlu, "(%d %s) SSL/TLS negotiation successful", int(nlc->s), szHost); - - return nlc->hSsl != nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// gets TLS channel binging data for a socket - -static char TLS13_Label[] = "EXPORTER-Channel-Binding"; - -MIR_APP_DLL(void*) Netlib_GetTlsUnique(HNETLIBCONN nlc, int &cbLen, int &tlsVer) -{ - if (nlc == nullptr || nlc->hSsl == nullptr) - return nullptr; - - char buf[1000]; - auto *pszVersion = SSL_get_version(nlc->hSsl->session); - if (tlsVer && !mir_strcmp(pszVersion, "TLSv1.3")) { - int res = SSL_export_keying_material(nlc->hSsl->session, - (uint8_t *)buf, 32, TLS13_Label, sizeof(TLS13_Label) - 1, 0, 0, 0); - if (res == 1) { - tlsVer = 13; - void *pBuf = mir_alloc(cbLen = 32); - memcpy(pBuf, buf, cbLen); - return pBuf; - } - } - - size_t len = SSL_get_finished(nlc->hSsl->session, buf, sizeof(buf)); - if (len == 0) - return nullptr; - - tlsVer = 12; - cbLen = (int)len; - void *pBuf = mir_alloc(len); - memcpy(pBuf, buf, len); - return pBuf; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// module entry point - -bool OpenSsl_Init(void) -{ - /* Load Library Pointers */ - if (bSslInitDone) - return true; - - if (!bSslInitDone) { // init OpenSSL - SSL_library_init(); - SSL_load_error_strings(); - // FIXME check errors - - const SSL_METHOD *meth = TLS_client_method(); - if (!meth) { - Netlib_Logf(nullptr, "SSL setup failure: client method"); - return false; - } - - g_ctx = SSL_CTX_new(meth); - if (!g_ctx) { - Netlib_Logf(nullptr, "SSL setup failure: context"); - return false; - } - - VARSW wszPemFile(L"%miranda_path%\\libs\\microsoft.pem"); - SSL_CTX_load_verify_locations(g_ctx, _T2A(wszPemFile), NULL); - - // SSL_read/write should transparently handle renegotiations - SSL_CTX_ctrl(g_ctx, SSL_CTRL_MODE, SSL_MODE_AUTO_RETRY, nullptr); - // SSL_CTX_set_quiet_shutdown(g_ctx, TRUE); - - RAND_screen(); - - bSslInitDone = true; - } - - return bSslInitDone; -} - -void OpenSsl_Unload(void) -{ - /* Load Library Pointers */ - if (!bSslInitDone) - return; - - if (g_ctx) - SSL_CTX_free(g_ctx); - - bSslInitDone = false; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+static bool bSslInitDone;
+
+enum SocketState
+{
+ sockOpen,
+ sockClosed,
+ sockError
+};
+
+struct SslHandle : public MZeroedObject
+{
+ ~SslHandle()
+ {
+ if (session)
+ SSL_free(session);
+ }
+
+ SOCKET s;
+ SSL *session;
+ SocketState state;
+};
+
+static SSL_CTX *g_ctx;
+static mir_cs csSsl;
+
+static void dump_error(SSL *session, int err)
+{
+ err = SSL_get_error(session, err);
+
+ char buf[100];
+ ERR_error_string_n(err, buf, sizeof(buf));
+ Netlib_Logf(nullptr, "SSL negotiation failure: %s (%d)", buf, err);
+}
+
+const char* SSL_GetCipherName(SslHandle *ssl)
+{
+ if (!ssl || !ssl->session)
+ return nullptr;
+
+ return SSL_CIPHER_get_name(SSL_get_current_cipher(ssl->session));
+}
+
+static void ReportSslError(SECURITY_STATUS scRet, int line, bool = false)
+{
+ CMStringW tszMsg(FORMAT, L"SSL connection failure(%x %u) :", scRet, line);
+
+ switch (scRet) {
+ case 0:
+ case ERROR_NOT_READY:
+ return;
+
+ case SEC_E_INVALID_TOKEN:
+ tszMsg += TranslateW_LP(L"Client cannot decode host message. Possible causes: host does not support SSL or requires not existing security package");
+ break;
+
+ case CERT_E_CN_NO_MATCH:
+ case SEC_E_WRONG_PRINCIPAL:
+ tszMsg += TranslateW_LP(L"Host we are connecting to is not the one certificate was issued for");
+ break;
+
+ default:
+ wchar_t szMsgBuf[256];
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr);
+ tszMsg += szMsgBuf;
+ }
+
+ Netlib_LogfW(nullptr, tszMsg);
+
+ SetLastError(scRet);
+ PUShowMessageW(tszMsg.GetBuffer(), SM_WARNING);
+}
+
+static PCCERT_CONTEXT SSL_X509ToCryptCert(X509 * x509)
+{
+ unsigned char *buf = nullptr;
+ PCCERT_CONTEXT pCertContext = nullptr;
+
+ int len = i2d_X509(x509, &buf);
+ if ((len >= 0) && buf) {
+ pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, buf, len);
+
+ CRYPTO_free(buf, __FILE__, __LINE__);
+ }
+ return pCertContext;
+}
+
+static PCCERT_CONTEXT SSL_CertChainToCryptAnchor(SSL* session)
+{
+ /* convert the active certificate chain provided in the handshake of 'session' into
+ the format used by CryptAPI.
+ */
+ PCCERT_CONTEXT anchor = nullptr;
+ // create cert store
+ HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, nullptr);
+
+ if (store) {
+ X509 *server_cert = SSL_get_peer_certificate(session);
+ if (server_cert) {
+ // add the server's cert first, to make sure CryptAPI builds the correct chain
+ PCCERT_CONTEXT primary_cert;
+ BOOL ok = CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(server_cert), CERT_STORE_ADD_ALWAYS, &primary_cert);
+ if (ok && primary_cert) {
+ // add all remaining certs to store (note: stack needs not be freed, it is not a copy)
+ STACK_OF(X509) *server_chain = SSL_get_peer_cert_chain(session);
+ if (server_chain) {
+ for (int i = 0; i < OPENSSL_sk_num((OPENSSL_STACK *)server_chain); i++) {
+ X509 *next_cert = (X509 *)OPENSSL_sk_value((OPENSSL_STACK *)server_chain, i);
+ CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(next_cert), CERT_STORE_ADD_USE_EXISTING, nullptr);
+ }
+ }
+
+ // return primary cert; MUST be freed by caller which will free the associated store
+ anchor = primary_cert;
+ }
+ else {
+ if (primary_cert)
+ CertFreeCertificateContext(primary_cert);
+ }
+
+ X509_free(server_cert);
+ }
+
+ CertCloseStore(store, 0);
+ }
+
+ return anchor;
+}
+
+static LPSTR rgszUsages[] =
+{
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+};
+
+static bool VerifyCertificate(SslHandle *ssl, PCSTR pszServerName, uint32_t dwCertFlags)
+{
+ uint32_t scRet;
+
+ ptrW pwszServerName(mir_a2u(pszServerName));
+
+ HTTPSPolicyCallbackData polHttps = {};
+ CERT_CHAIN_POLICY_PARA PolicyPara = {};
+ CERT_CHAIN_POLICY_STATUS PolicyStatus = {};
+ CERT_CHAIN_PARA ChainPara = {};
+
+ PCCERT_CHAIN_CONTEXT pChainContext = nullptr;
+ PCCERT_CONTEXT pServerCert = SSL_CertChainToCryptAnchor(ssl->session);
+ if (pServerCert == nullptr) {
+ scRet = SEC_E_WRONG_PRINCIPAL;
+ goto cleanup;
+ }
+
+ ChainPara.cbSize = sizeof(ChainPara);
+ ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ ChainPara.RequestedUsage.Usage.cUsageIdentifier = _countof(rgszUsages);
+ ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
+
+ if (!CertGetCertificateChain(nullptr, pServerCert, nullptr, pServerCert->hCertStore, &ChainPara, 0, nullptr, &pChainContext)) {
+ scRet = GetLastError();
+ goto cleanup;
+ }
+
+ polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
+ polHttps.dwAuthType = AUTHTYPE_SERVER;
+ polHttps.fdwChecks = dwCertFlags;
+ polHttps.pwszServerName = pwszServerName;
+
+ PolicyPara.cbSize = sizeof(PolicyPara);
+ PolicyPara.pvExtraPolicyPara = &polHttps;
+
+ PolicyStatus.cbSize = sizeof(PolicyStatus);
+
+ if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)) {
+ scRet = GetLastError();
+ goto cleanup;
+ }
+
+ if (PolicyStatus.dwError) {
+ scRet = PolicyStatus.dwError;
+ goto cleanup;
+ }
+
+ scRet = SEC_E_OK;
+
+cleanup:
+ if (pChainContext)
+ CertFreeCertificateChain(pChainContext);
+ if (pServerCert)
+ CertFreeCertificateContext(pServerCert);
+
+ ReportSslError(scRet, __LINE__, true);
+ return scRet == SEC_E_OK;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// negotiate SSL session, verify cert, return NULL if failed
+
+MIR_APP_DLL(HSSL) Netlib_SslConnect(SOCKET s, const char* host, int verify)
+{
+ std::unique_ptr<SslHandle> ssl(new SslHandle());
+ ssl->s = s;
+ {
+ mir_cslock lck(csSsl);
+ ssl->session = SSL_new(g_ctx);
+ }
+
+ if (!ssl->session) {
+ Netlib_Logf(nullptr, "SSL setup failure: session");
+ return false;
+ }
+ SSL_set_fd(ssl->session, ssl->s);
+
+ SSL_set_tlsext_host_name(ssl->session, host);
+
+ int err = SSL_connect(ssl->session);
+ if (err != 1) {
+ dump_error(ssl->session, err);
+ return nullptr;
+ }
+
+ if (verify) {
+ uint32_t dwFlags = 0;
+ if (!host || inet_addr(host) != INADDR_NONE)
+ dwFlags |= 0x00001000;
+ if (!VerifyCertificate(ssl.get(), host, dwFlags))
+ return nullptr;
+ }
+
+ return ssl.release();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// return true if there is either unsend or buffered received data (ie. after peek)
+
+MIR_APP_DLL(BOOL) Netlib_SslPending(HSSL ssl)
+{
+ return ssl && ssl->session && (SSL_pending(ssl->session) > 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// reads number of bytes, keeps in buffer if peek != 0
+
+MIR_APP_DLL(int) Netlib_SslRead(HSSL ssl, char *buf, int num, int peek)
+{
+ if (!ssl || !ssl->session) return SOCKET_ERROR;
+ if (num <= 0) return 0;
+
+ int err = 0;
+ if (peek)
+ err = SSL_peek(ssl->session, buf, num);
+ else
+ err = SSL_read(ssl->session, buf, num);
+
+ if (err <= 0) {
+ int err2 = SSL_get_error(ssl->session, err);
+ if (err2 == SSL_ERROR_ZERO_RETURN) {
+ Netlib_Logf(nullptr, "SSL connection gracefully closed");
+ ssl->state = sockClosed;
+ return 0;
+ }
+
+ int err3 = ERR_get_error();
+ if (err3) {
+ Netlib_Logf(nullptr, "SSL failure recieving data (%d, %d, %d, %d)", err, err2, err3, WSAGetLastError());
+ ssl->state = sockError;
+ return SOCKET_ERROR;
+ }
+ }
+
+ return err;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// writes data to the SSL socket
+
+MIR_APP_DLL(int) Netlib_SslWrite(HSSL ssl, const char *buf, int num)
+{
+ if (!ssl || !ssl->session)
+ return SOCKET_ERROR;
+ if (num <= 0)
+ return 0;
+
+ int err = SSL_write(ssl->session, buf, num);
+ if (err > 0)
+ return err;
+
+ int err2 = SSL_get_error(ssl->session, err);
+ switch (err2) {
+ case SSL_ERROR_ZERO_RETURN:
+ Netlib_Logf(nullptr, "SSL connection gracefully closed");
+ ssl->state = sockClosed;
+ break;
+
+ default:
+ Netlib_Logf(nullptr, "SSL failure sending data (%d, %d, %d)", err, err2, WSAGetLastError());
+ ssl->state = sockError;
+ return SOCKET_ERROR;
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// closes SSL session, but keeps socket open
+
+MIR_APP_DLL(void) Netlib_SslShutdown(HSSL ssl)
+{
+ if (ssl && ssl->session) {
+ SOCKET s = SSL_get_fd(ssl->session);
+ if (s != -1)
+ shutdown(s, SD_BOTH);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// frees all data associated with the SSL socket
+
+MIR_APP_DLL(void) Netlib_SslFree(HSSL ssl)
+{
+ delete ssl;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// makes connection SSL
+// returns 0 on failure / 1 on success
+
+MIR_APP_DLL(int) Netlib_StartSsl(HNETLIBCONN hConnection, const char *szHost)
+{
+ NetlibConnection *nlc = (NetlibConnection*)hConnection;
+ if (nlc == nullptr)
+ return 0;
+
+ NetlibUser *nlu = nlc->nlu;
+ if (szHost == nullptr)
+ szHost = nlc->url.szHost;
+ szHost = NEWSTR_ALLOCA(szHost);
+
+ Netlib_Logf(nlu, "(%d %s) Starting SSL/TLS negotiation", int(nlc->s), szHost);
+
+ nlc->hSsl = Netlib_SslConnect(nlc->s, szHost, nlu->settings.validateSSL);
+ if (nlc->hSsl == nullptr)
+ Netlib_Logf(nlu, "(%d %s) Failure to negotiate SSL/TLS connection", int(nlc->s), szHost);
+ else
+ Netlib_Logf(nlu, "(%d %s) SSL/TLS negotiation successful", int(nlc->s), szHost);
+
+ return nlc->hSsl != nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// gets TLS channel binging data for a socket
+
+static char TLS13_Label[] = "EXPORTER-Channel-Binding";
+
+MIR_APP_DLL(void*) Netlib_GetTlsUnique(HNETLIBCONN nlc, int &cbLen, int &tlsVer)
+{
+ if (nlc == nullptr || nlc->hSsl == nullptr)
+ return nullptr;
+
+ char buf[1000];
+ auto *pszVersion = SSL_get_version(nlc->hSsl->session);
+ if (tlsVer && !mir_strcmp(pszVersion, "TLSv1.3")) {
+ int res = SSL_export_keying_material(nlc->hSsl->session,
+ (uint8_t *)buf, 32, TLS13_Label, sizeof(TLS13_Label) - 1, 0, 0, 0);
+ if (res == 1) {
+ tlsVer = 13;
+ void *pBuf = mir_alloc(cbLen = 32);
+ memcpy(pBuf, buf, cbLen);
+ return pBuf;
+ }
+ }
+
+ size_t len = SSL_get_finished(nlc->hSsl->session, buf, sizeof(buf));
+ if (len == 0)
+ return nullptr;
+
+ tlsVer = 12;
+ cbLen = (int)len;
+ void *pBuf = mir_alloc(len);
+ memcpy(pBuf, buf, len);
+ return pBuf;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// module entry point
+
+bool OpenSsl_Init(void)
+{
+ /* Load Library Pointers */
+ if (bSslInitDone)
+ return true;
+
+ if (!bSslInitDone) { // init OpenSSL
+ SSL_library_init();
+ SSL_load_error_strings();
+ // FIXME check errors
+
+ const SSL_METHOD *meth = TLS_client_method();
+ if (!meth) {
+ Netlib_Logf(nullptr, "SSL setup failure: client method");
+ return false;
+ }
+
+ g_ctx = SSL_CTX_new(meth);
+ if (!g_ctx) {
+ Netlib_Logf(nullptr, "SSL setup failure: context");
+ return false;
+ }
+
+ VARSW wszPemFile(L"%miranda_path%\\libs\\microsoft.pem");
+ SSL_CTX_load_verify_locations(g_ctx, _T2A(wszPemFile), NULL);
+
+ // SSL_read/write should transparently handle renegotiations
+ SSL_CTX_ctrl(g_ctx, SSL_CTRL_MODE, SSL_MODE_AUTO_RETRY, nullptr);
+ // SSL_CTX_set_quiet_shutdown(g_ctx, TRUE);
+
+ RAND_screen();
+
+ bSslInitDone = true;
+ }
+
+ return bSslInitDone;
+}
+
+void OpenSsl_Unload(void)
+{
+ /* Load Library Pointers */
+ if (!bSslInitDone)
+ return;
+
+ if (g_ctx)
+ SSL_CTX_free(g_ctx);
+
+ bSslInitDone = false;
+}
diff --git a/src/mir_app/src/netlib_upnp.cpp b/src/mir_app/src/netlib_upnp.cpp index d9570b23af..17b7f5f463 100644 --- a/src/mir_app/src/netlib_upnp.cpp +++ b/src/mir_app/src/netlib_upnp.cpp @@ -1,813 +1,813 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -static const char search_request_msg[] = - "M-SEARCH * HTTP/1.1\r\n" - "HOST: 239.255.255.250:1900\r\n" - "MAN: \"ssdp:discover\"\r\n" - "MX: 1\r\n" - "ST: urn:schemas-upnp-org:service:%s\r\n" - "\r\n"; - -static const char xml_get_hdr[] = - "GET %s HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "ACCEPT-LANGUAGE: *\r\n\r\n"; - -static const char soap_post_hdr[] = - "POST %s HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "CONTENT-LENGTH: %u\r\n" - "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" - "SOAPACTION: \"%s#%s\"\r\n\r\n" - "%s"; - -static const char soap_post_hdr_m[] = - "M-POST %s URL HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "CONTENT-LENGTH: %u\r\n" - "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" - "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns = 01\r\n" - "01-SOAPACTION: \"%s#%s\"\r\n\r\n" - "%s"; - -static const char search_device[] = - "<serviceType>%s</serviceType>"; - -static const char soap_action[] = - "<?xml version = \"1.0\"?>\r\n" - "<s:Envelope\r\n" - " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n" - " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" - " <s:Body>\r\n" - " <u:%s xmlns:u = \"%s\">\r\n" - "%s" - " </u:%s>\r\n" - " </s:Body>\r\n" - "</s:Envelope>\r\n"; - -static const char soap_query[] = - "<s:Envelope\r\n" - " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n" - " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" - " <s:Body>\r\n" - " <u:QueryStateVariable xmlns:u = \"urn:schemas-upnp-org:control-1-0\">\r\n" - " <u:varName>%s</u:varName>\r\n" - " </u:QueryStateVariable>\r\n" - " </s:Body>\r\n" - "</s:Envelope>\r\n"; - -static const char add_port_mapping[] = - " <NewRemoteHost></NewRemoteHost>\r\n" - " <NewExternalPort>%i</NewExternalPort>\r\n" - " <NewProtocol>%s</NewProtocol>\r\n" - " <NewInternalPort>%i</NewInternalPort>\r\n" - " <NewInternalClient>%s</NewInternalClient>\r\n" - " <NewEnabled>1</NewEnabled>\r\n" - " <NewPortMappingDescription>Miranda</NewPortMappingDescription>\r\n" - " <NewLeaseDuration>0</NewLeaseDuration>\r\n"; - -static const char delete_port_mapping[] = - " <NewRemoteHost></NewRemoteHost>\r\n" - " <NewExternalPort>%i</NewExternalPort>\r\n" - " <NewProtocol>%s</NewProtocol>\r\n"; - -static const char get_port_mapping[] = - " <NewPortMappingIndex>%i</NewPortMappingIndex>\r\n"; - -static bool gatewayFound; -static SOCKADDR_IN locIP; -static time_t lastDiscTime; -static int expireTime = 120; - -static int retryCount; -static SOCKET sock = INVALID_SOCKET; -static char szConnHost[256]; -static unsigned short sConnPort; - -static uint16_t *portList; -static unsigned numports, numportsAlloc; -static HANDLE portListMutex; - -static char szCtlUrl[256], szDev[256]; - -typedef enum -{ - DeviceGetReq, - ControlAction, - ControlQuery -} ReqType; - -static bool txtParseParam(char* szData, char* presearch, - char* start, char* finish, char* param, size_t size) -{ - char *cp, *cp1; - size_t len; - - *param = 0; - - if (presearch != nullptr) { - cp1 = strstr(szData, presearch); - if (cp1 == nullptr) return false; - } - else - cp1 = szData; - - cp = strstr(cp1, start); - if (cp == nullptr) return false; - cp += mir_strlen(start); - while (*cp == ' ') ++cp; - - cp1 = strstr(cp, finish); - if (cp1 == nullptr) return false; - while (*(cp1-1) == ' ' && cp1 > cp) --cp1; - - len = min((size_t)(cp1 - cp), size-1); - strncpy(param, cp, len); - param[len] = 0; - - return true; -} - -void parseURL(char* szUrl, char* szHost, unsigned short* sPort, char* szPath) -{ - char *ppath, *phost, *pport; - int sz; - - phost = strstr(szUrl, "://"); - if (phost == nullptr) phost = szUrl; - else phost += 3; - - ppath = strchr(phost, '/'); - if (ppath == nullptr) ppath = phost + mir_strlen(phost); - - pport = strchr(phost, ':'); - if (pport == nullptr) pport = ppath; - - if (szHost != nullptr) { - sz = pport - phost + 1; - if (sz > 256) sz = 256; - strncpy(szHost, phost, sz); - szHost[sz - 1] = 0; - } - - if (sPort != nullptr) { - if (pport < ppath) { - long prt = atol(pport + 1); - *sPort = prt != 0 ? (unsigned short)prt : 80; - } - else - *sPort = 80; - } - - if (szPath != nullptr) { - strncpy(szPath, ppath, 256); - szPath[255] = 0; - } -} - -static void LongLog(char* szData) -{ - Netlib_Logf(nullptr, szData); -} - -static void closeRouterConnection(void) -{ - if (sock != INVALID_SOCKET) { - closesocket(sock); - sock = INVALID_SOCKET; - } -} - -static void validateSocket(void) -{ - static const TIMEVAL tv = { 0, 0 }; - fd_set rfd; - char buf[4]; - - if (sock == INVALID_SOCKET) - return; - - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - - bool opened = false; - switch (select(1, &rfd, nullptr, nullptr, &tv)) { - case 0: - opened = true; - break; - - case 1: - opened = recv(sock, buf, 1, MSG_PEEK) > 0; - break; - } - - if (!opened) - closeRouterConnection(); -} - -static int httpTransact(char* szUrl, char* szResult, int resSize, char* szActionName, ReqType reqtype) -{ - // Parse URL - char szHost[256], szPath[256], szRes[16]; - int sz = 0, res = 0; - unsigned short sPort; - bool needClose = false; - - const char* szPostHdr = soap_post_hdr; - char* szData = (char*)mir_alloc(4096); - char* szReq = nullptr; - - parseURL(szUrl, szHost, &sPort, szPath); - - if (sPort != sConnPort || _stricmp(szHost, szConnHost)) - closeRouterConnection(); - else - validateSocket(); - - while (true) { - retryCount = 0; - switch (reqtype) { - case DeviceGetReq: - sz = mir_snprintf(szData, 4096, xml_get_hdr, szPath, szHost, sPort); - break; - - case ControlAction: - { - char szData1[1024]; - - szReq = mir_strdup(szResult); - sz = mir_snprintf(szData1, soap_action, szActionName, szDev, szReq, szActionName); - sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, szDev, szActionName, szData1); - } - break; - - case ControlQuery: - { - char szData1[1024]; - sz = mir_snprintf(szData1, soap_query, szActionName); - sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, "urn:schemas-upnp-org:control-1-0", "QueryStateVariable", szData1); - } - break; - } - szResult[0] = 0; - { - static const TIMEVAL tv = { 6, 0 }; - static unsigned ttl = 4; - static u_long mode = 1; - fd_set rfd, wfd, efd; - SOCKADDR_IN enetaddr; - -retrycon: - if (sock == INVALID_SOCKET) { - sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - - enetaddr.sin_family = AF_INET; - enetaddr.sin_port = htons(sPort); - enetaddr.sin_addr.s_addr = inet_addr(szHost); - - // Resolve host name if needed - if (enetaddr.sin_addr.s_addr == INADDR_NONE) { - PHOSTENT he = gethostbyname(szHost); - if (he) - enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0]; - } - - Netlib_Logf(nullptr, "UPnP HTTP connection Host: %s Port: %u", szHost, sPort); - - FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd); - FD_SET(sock, &rfd); FD_SET(sock, &wfd); FD_SET(sock, &efd); - - // Limit the scope of the connection (does not work for - setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned)); - - // Put socket into non-blocking mode for timeout on connect - ioctlsocket(sock, FIONBIO, &mode); - - // Connect to the remote host - if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == SOCKET_ERROR) { - int err = WSAGetLastError(); - - // Socket connection failed - if (err != WSAEWOULDBLOCK) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect failed %d", err); - break; - } - // Wait for socket to connect - else if (select(1, &rfd, &wfd, &efd, &tv) != 1) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect timeout"); - break; - } - else if (!FD_ISSET(sock, &wfd)) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect failed"); - break; - } - } - strncpy_s(szConnHost, szHost, _TRUNCATE); - sConnPort = sPort; - } - - if (send(sock, szData, sz, 0) != SOCKET_ERROR) { - char *hdrend = nullptr; - int acksz = 0, pktsz = 0; - - if (szActionName == nullptr) { - int len = sizeof(locIP); - getsockname(sock, (SOCKADDR*)&locIP, &len); - if (locIP.sin_addr.S_un.S_addr == 0x0100007f) { - struct hostent *he; - - gethostname(szPath, sizeof(szPath)); - he = gethostbyname(szPath); - if (he != nullptr) - locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0]; - } - } - - LongLog(szData); - sz = 0; - while (true) { - int bytesRecv; - - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - - // Wait for the next packet - if (select(1, &rfd, nullptr, nullptr, &tv) != 1) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP recieve timeout"); - break; - } - - // - bytesRecv = recv(sock, &szResult[sz], resSize - sz, 0); - - // Connection closed or aborted, all data received - if (bytesRecv == 0 || bytesRecv == SOCKET_ERROR) { - closeRouterConnection(); - if ((bytesRecv == SOCKET_ERROR || sz == 0) && retryCount < 2) { - ++retryCount; - goto retrycon; - } - break; - } - - sz += bytesRecv; - - // Insert null terminator to use string functions - if (sz >= (resSize - 1)) { - szResult[resSize - 1] = 0; - break; - } - else - szResult[sz] = 0; - - // HTTP header found? - if (hdrend == nullptr) { - // Find HTTP header end - hdrend = strstr(szResult, "\r\n\r\n"); - if (hdrend == nullptr) { - hdrend = strstr(szResult, "\n\n"); - if (hdrend) hdrend += 2; - } - - else - hdrend += 4; - - if (hdrend != nullptr) { - // Get packet size if provided - if (txtParseParam(szResult, nullptr, "Content-Length:", "\n", szRes, sizeof(szRes)) || - txtParseParam(szResult, nullptr, "CONTENT-LENGTH:", "\n", szRes, sizeof(szRes))) { - // Add size of HTTP header to the packet size to compute full transmission size - pktsz = atol(ltrimp(szRes)) + (hdrend - szResult); - } - // Get encoding type if provided - else if (txtParseParam(szResult, nullptr, "Transfer-Encoding:", "\n", szRes, sizeof(szRes))) { - if (_stricmp(lrtrimp(szRes), "Chunked") == 0) - acksz = hdrend - szResult; - } - if (txtParseParam(szResult, nullptr, "Connection:", "\n", szRes, sizeof(szRes))) { - needClose = (_stricmp(lrtrimp(szRes), "close") == 0); - } - } - } - - // Content-Length bytes reached, all data received - if (sz >= pktsz && pktsz != 0) { - szResult[pktsz] = 0; - break; - } - - // Chunked encoding processing - if (sz > acksz && acksz != 0) { -retry: - // Parse out chunk size - char* data = szResult + acksz; - char* peol1 = data == hdrend ? data - 1 : strchr(data, '\n'); - if (peol1 != nullptr) { - char *peol2 = strchr(++peol1, '\n'); - if (peol2 != nullptr) { - // Get chunk size - int chunkBytes = strtol(peol1, nullptr, 16); - acksz += chunkBytes; - peol2++; - - memmove(data, peol2, mir_strlen(peol2) + 1); - sz -= peol2 - data; - - // Last chunk, all data received - if (chunkBytes == 0) break; - if (sz > acksz) goto retry; - } - } - } - } - LongLog(szResult); - } - else { - if (retryCount < 2) { - closeRouterConnection(); - ++retryCount; - goto retrycon; - } - else - Netlib_Logf(nullptr, "UPnP send failed %d", WSAGetLastError()); - } - } - txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes)); - res = atol(szRes); - if (szActionName != nullptr && res == 405 && szPostHdr == soap_post_hdr) - szPostHdr = soap_post_hdr_m; - else - break; - } - - if (needClose) - closeRouterConnection(); - - mir_free(szData); - mir_free(szReq); - return res; -} - -static unsigned getExtIP(void) -{ - char szExtIP[30]; - char* szData = (char*)mir_alloc(4096); szData[0] = 0; - - unsigned extip = 0; - int res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress", ControlAction); - if (res == 200 && txtParseParam(szData, "<NewExternalIPAddress", ">", "<", szExtIP, sizeof(szExtIP))) - extip = ntohl(inet_addr(szExtIP)); - - mir_free(szData); - return extip; -} - -static bool getUPnPURLs(char* szUrl, size_t sizeUrl) -{ - char* szData = (char*)mir_alloc(8192); - - gatewayFound = httpTransact(szUrl, szData, 8192, nullptr, DeviceGetReq) == 200; - if (gatewayFound) { - char szTemp[256], *rpth; - size_t ctlLen; - - txtParseParam(szData, nullptr, "<URLBase>", "</URLBase>", szTemp, sizeof(szTemp)); - strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl)); - szCtlUrl[sizeof(szCtlUrl) - 1] = 0; - - mir_snprintf(szTemp, search_device, szDev); - txtParseParam(szData, szTemp, "<controlURL>", "</controlURL>", szUrl, sizeUrl); - - // URL combining per RFC 2396 - if (szUrl[0] != 0) { - if (strstr(szUrl, "://") != nullptr) // absolute URI - rpth = szCtlUrl; - else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path - { - rpth = strstr(szCtlUrl, "//"); - if (rpth == nullptr) rpth = szCtlUrl; - } - else if (szUrl[0] == '/') // relative URI abs_path - { - rpth = strstr(szCtlUrl, "//"); - rpth = rpth ? rpth + 2 : szCtlUrl; - - rpth = strchr(rpth, '/'); - if (rpth == nullptr) rpth = szCtlUrl + mir_strlen(szCtlUrl); - } - else { // relative URI rel_path - size_t ctlCLen = mir_strlen(szCtlUrl); - rpth = szCtlUrl + ctlCLen; - if (ctlCLen != 0 && *(rpth - 1) != '/') - strncpy(rpth++, "/", sizeof(szCtlUrl) - ctlCLen); - } - - ctlLen = sizeof(szCtlUrl) - (rpth - szCtlUrl); - strncpy(rpth, szUrl, ctlLen); - szCtlUrl[sizeof(szCtlUrl) - 1] = 0; - } - else { - szCtlUrl[0] = 0; - gatewayFound = false; - } - } - mir_free(szData); - - return gatewayFound; -} - -static void discoverUPnP(void) -{ - char* buf; - int buflen; - unsigned i, j, nip = 0; - unsigned* ips = nullptr; - - static const unsigned any = INADDR_ANY; - static const TIMEVAL tv = { 1, 600000 }; - - char szUrl[256] = ""; - char hostname[256]; - PHOSTENT he; - fd_set readfd; - - SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - SOCKADDR_IN enetaddr; - enetaddr.sin_family = AF_INET; - enetaddr.sin_port = htons(1900); - enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250"); - - gethostname(hostname, sizeof(hostname)); - he = gethostbyname(hostname); - - if (he) { - while (he->h_addr_list[nip]) ++nip; - - ips = (unsigned*)mir_alloc(nip * sizeof(unsigned)); - - for (j = 0; j < nip; j++) - ips[j] = *(unsigned*)he->h_addr_list[j]; - } - - buf = (char*)mir_alloc(1500); - - for (i = 3; --i && szUrl[0] == 0;) { - for (j = 0; j < nip; j++) { - if (ips) - setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned)); - - buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1"); - sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); - LongLog(buf); - - buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1"); - sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); - LongLog(buf); - } - - if (Miranda_IsTerminated()) break; - - FD_ZERO(&readfd); - FD_SET(s, &readfd); - - while (select(1, &readfd, nullptr, nullptr, &tv) >= 1) { - buflen = recv(s, buf, 1500, 0); - if (buflen != SOCKET_ERROR) { - buf[buflen] = 0; - LongLog(buf); - - if (txtParseParam(buf, nullptr, "LOCATION:", "\n", szUrl, sizeof(szUrl)) || - txtParseParam(buf, nullptr, "Location:", "\n", szUrl, sizeof(szUrl))) { - char age[30]; - char szHostNew[256], szHostExist[256]; - - lrtrim(szUrl); - - parseURL(szUrl, szHostNew, nullptr, nullptr); - parseURL(szCtlUrl, szHostExist, nullptr, nullptr); - if (mir_strcmp(szHostNew, szHostExist) == 0) { - gatewayFound = true; - break; - } - - txtParseParam(buf, nullptr, "ST:", "\n", szDev, sizeof(szDev)); - txtParseParam(buf, "max-age", " = ", "\n", age, sizeof(age)); - expireTime = atoi(lrtrimp(age)); - lrtrim(szDev); - - if (getUPnPURLs(szUrl, sizeof(szUrl))) { - gatewayFound = getExtIP() != 0; - if (gatewayFound) break; - } - } - } - FD_ZERO(&readfd); - FD_SET(s, &readfd); - } - } - - mir_free(buf); - mir_free(ips); - setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned)); - closesocket(s); -} - -static bool findUPnPGateway(void) -{ - if ((time(0) - lastDiscTime) >= expireTime) { - WaitForSingleObject(portListMutex, INFINITE); - - time_t curTime = time(0); - - if ((curTime - lastDiscTime) >= expireTime) { - gatewayFound = false; - - discoverUPnP(); - lastDiscTime = curTime; - - Netlib_Logf(nullptr, "UPnP Gateway detected %d, Control URL: %s", gatewayFound, szCtlUrl); - } - - ReleaseMutex(portListMutex); - } - - return gatewayFound; -} - -bool NetlibUPnPAddPortMapping(uint16_t intport, char *proto, uint16_t *extport, uint32_t *extip, bool search) -{ - int res = 0, i = 5; - - if (findUPnPGateway()) { - char* szData = (char*)mir_alloc(4096); - char szExtIP[30]; - - *extport = intport - 1; - *extip = ntohl(locIP.sin_addr.S_un.S_addr); - - WaitForSingleObject(portListMutex, INFINITE); - - do { - ++*extport; - mir_snprintf(szData, 4096, add_port_mapping, - *extport, proto, intport, inet_ntoa(locIP.sin_addr)); - res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping", ControlAction); - txtParseParam(szData, nullptr, "<errorCode>", "</errorCode>", szExtIP, sizeof(szExtIP)); - - } while (search && res == 500 && atol(szExtIP) == 718 && --i); - - mir_free(szData); - - if (res == 200) { - unsigned ip = getExtIP(); - if (ip) *extip = ip; - - if (numports >= numportsAlloc) - mir_realloc(portList, sizeof(uint16_t)*(numportsAlloc += 10)); - portList[numports++] = *extport; - } - - ReleaseMutex(portListMutex); - } - - return res == 200; -} - -void NetlibUPnPDeletePortMapping(uint16_t extport, char* proto) -{ - if (extport == 0) - return; - - // findUPnPGateway(); - - if (gatewayFound) { - unsigned i; - char* szData = (char*)mir_alloc(4096); - - WaitForSingleObject(portListMutex, INFINITE); - mir_snprintf(szData, 4096, delete_port_mapping, extport, proto); - httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping", ControlAction); - - for (i = 0; i < numports; i++) - if (portList[i] == extport && --numports > 0) - memmove(&portList[i], &portList[i + 1], (numports - i) * sizeof(uint16_t)); - - mir_free(szData); - ReleaseMutex(portListMutex); - } -} - -void NetlibUPnPCleanup(void*) -{ - // upnp is disabled globally, no need for a cleanup - if (db_get_b(0, "Netlib", "NLEnableUPnP", 1) == 0) - return; - - { - int incoming = 0; - mir_cslock lck(csNetlibUser); - for (auto &p : netlibUser) - if (p->user.flags & NUF_INCOMING) { - incoming = 1; - break; - } - - if (!incoming) - return; - } - - if (findUPnPGateway()) { - char *szData = (char*)alloca(4096); - char buf[50], lip[50]; - unsigned j = 0, k, num = 100; - - strncpy_s(lip, inet_ntoa(locIP.sin_addr), _TRUNCATE); - - WaitForSingleObject(portListMutex, INFINITE); - - if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 && - txtParseParam(szData, "QueryStateVariableResponse", "<return>", "<", buf, sizeof(buf))) - num = atol(buf); - - uint16_t ports[30]; - for (unsigned i = 0; i < num && !Miranda_IsTerminated(); i++) { - mir_snprintf(szData, 4096, get_port_mapping, i); - - ReleaseMutex(portListMutex); - WaitForSingleObject(portListMutex, INFINITE); - - if (httpTransact(szCtlUrl, szData, 4096, "GetGenericPortMappingEntry", ControlAction) != 200) - break; - - if (!txtParseParam(szData, "<NewPortMappingDescription", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0) - continue; - - if (!txtParseParam(szData, "<NewInternalClient", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0) - continue; - - if (txtParseParam(szData, "<NewExternalPort", ">", "<", buf, sizeof(buf))) { - uint16_t mport = (uint16_t)atol(buf); - - if (j >= _countof(ports)) - break; - - for (k = 0; k < numports; ++k) - if (portList[k] == mport) - break; - - if (k >= numports) - ports[j++] = mport; - } - } - - ReleaseMutex(portListMutex); - - for (unsigned i = 0; i < j && !Miranda_IsTerminated(); i++) - NetlibUPnPDeletePortMapping(ports[i], "TCP"); - } -} - -void NetlibUPnPInit(void) -{ - numports = 0; - numportsAlloc = 10; - portList = (uint16_t*)mir_alloc(sizeof(uint16_t)*numportsAlloc); - - portListMutex = CreateMutex(nullptr, FALSE, nullptr); -} - -void NetlibUPnPDestroy(void) -{ - mir_free(portList); - CloseHandle(portListMutex); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+static const char search_request_msg[] =
+ "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: 239.255.255.250:1900\r\n"
+ "MAN: \"ssdp:discover\"\r\n"
+ "MX: 1\r\n"
+ "ST: urn:schemas-upnp-org:service:%s\r\n"
+ "\r\n";
+
+static const char xml_get_hdr[] =
+ "GET %s HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "ACCEPT-LANGUAGE: *\r\n\r\n";
+
+static const char soap_post_hdr[] =
+ "POST %s HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "CONTENT-LENGTH: %u\r\n"
+ "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n"
+ "SOAPACTION: \"%s#%s\"\r\n\r\n"
+ "%s";
+
+static const char soap_post_hdr_m[] =
+ "M-POST %s URL HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "CONTENT-LENGTH: %u\r\n"
+ "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n"
+ "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns = 01\r\n"
+ "01-SOAPACTION: \"%s#%s\"\r\n\r\n"
+ "%s";
+
+static const char search_device[] =
+ "<serviceType>%s</serviceType>";
+
+static const char soap_action[] =
+ "<?xml version = \"1.0\"?>\r\n"
+ "<s:Envelope\r\n"
+ " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:%s xmlns:u = \"%s\">\r\n"
+ "%s"
+ " </u:%s>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char soap_query[] =
+ "<s:Envelope\r\n"
+ " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:QueryStateVariable xmlns:u = \"urn:schemas-upnp-org:control-1-0\">\r\n"
+ " <u:varName>%s</u:varName>\r\n"
+ " </u:QueryStateVariable>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char add_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n"
+ " <NewInternalPort>%i</NewInternalPort>\r\n"
+ " <NewInternalClient>%s</NewInternalClient>\r\n"
+ " <NewEnabled>1</NewEnabled>\r\n"
+ " <NewPortMappingDescription>Miranda</NewPortMappingDescription>\r\n"
+ " <NewLeaseDuration>0</NewLeaseDuration>\r\n";
+
+static const char delete_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n";
+
+static const char get_port_mapping[] =
+ " <NewPortMappingIndex>%i</NewPortMappingIndex>\r\n";
+
+static bool gatewayFound;
+static SOCKADDR_IN locIP;
+static time_t lastDiscTime;
+static int expireTime = 120;
+
+static int retryCount;
+static SOCKET sock = INVALID_SOCKET;
+static char szConnHost[256];
+static unsigned short sConnPort;
+
+static uint16_t *portList;
+static unsigned numports, numportsAlloc;
+static HANDLE portListMutex;
+
+static char szCtlUrl[256], szDev[256];
+
+typedef enum
+{
+ DeviceGetReq,
+ ControlAction,
+ ControlQuery
+} ReqType;
+
+static bool txtParseParam(char* szData, char* presearch,
+ char* start, char* finish, char* param, size_t size)
+{
+ char *cp, *cp1;
+ size_t len;
+
+ *param = 0;
+
+ if (presearch != nullptr) {
+ cp1 = strstr(szData, presearch);
+ if (cp1 == nullptr) return false;
+ }
+ else
+ cp1 = szData;
+
+ cp = strstr(cp1, start);
+ if (cp == nullptr) return false;
+ cp += mir_strlen(start);
+ while (*cp == ' ') ++cp;
+
+ cp1 = strstr(cp, finish);
+ if (cp1 == nullptr) return false;
+ while (*(cp1-1) == ' ' && cp1 > cp) --cp1;
+
+ len = min((size_t)(cp1 - cp), size-1);
+ strncpy(param, cp, len);
+ param[len] = 0;
+
+ return true;
+}
+
+void parseURL(char* szUrl, char* szHost, unsigned short* sPort, char* szPath)
+{
+ char *ppath, *phost, *pport;
+ int sz;
+
+ phost = strstr(szUrl, "://");
+ if (phost == nullptr) phost = szUrl;
+ else phost += 3;
+
+ ppath = strchr(phost, '/');
+ if (ppath == nullptr) ppath = phost + mir_strlen(phost);
+
+ pport = strchr(phost, ':');
+ if (pport == nullptr) pport = ppath;
+
+ if (szHost != nullptr) {
+ sz = pport - phost + 1;
+ if (sz > 256) sz = 256;
+ strncpy(szHost, phost, sz);
+ szHost[sz - 1] = 0;
+ }
+
+ if (sPort != nullptr) {
+ if (pport < ppath) {
+ long prt = atol(pport + 1);
+ *sPort = prt != 0 ? (unsigned short)prt : 80;
+ }
+ else
+ *sPort = 80;
+ }
+
+ if (szPath != nullptr) {
+ strncpy(szPath, ppath, 256);
+ szPath[255] = 0;
+ }
+}
+
+static void LongLog(char* szData)
+{
+ Netlib_Logf(nullptr, szData);
+}
+
+static void closeRouterConnection(void)
+{
+ if (sock != INVALID_SOCKET) {
+ closesocket(sock);
+ sock = INVALID_SOCKET;
+ }
+}
+
+static void validateSocket(void)
+{
+ static const TIMEVAL tv = { 0, 0 };
+ fd_set rfd;
+ char buf[4];
+
+ if (sock == INVALID_SOCKET)
+ return;
+
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+
+ bool opened = false;
+ switch (select(1, &rfd, nullptr, nullptr, &tv)) {
+ case 0:
+ opened = true;
+ break;
+
+ case 1:
+ opened = recv(sock, buf, 1, MSG_PEEK) > 0;
+ break;
+ }
+
+ if (!opened)
+ closeRouterConnection();
+}
+
+static int httpTransact(char* szUrl, char* szResult, int resSize, char* szActionName, ReqType reqtype)
+{
+ // Parse URL
+ char szHost[256], szPath[256], szRes[16];
+ int sz = 0, res = 0;
+ unsigned short sPort;
+ bool needClose = false;
+
+ const char* szPostHdr = soap_post_hdr;
+ char* szData = (char*)mir_alloc(4096);
+ char* szReq = nullptr;
+
+ parseURL(szUrl, szHost, &sPort, szPath);
+
+ if (sPort != sConnPort || _stricmp(szHost, szConnHost))
+ closeRouterConnection();
+ else
+ validateSocket();
+
+ while (true) {
+ retryCount = 0;
+ switch (reqtype) {
+ case DeviceGetReq:
+ sz = mir_snprintf(szData, 4096, xml_get_hdr, szPath, szHost, sPort);
+ break;
+
+ case ControlAction:
+ {
+ char szData1[1024];
+
+ szReq = mir_strdup(szResult);
+ sz = mir_snprintf(szData1, soap_action, szActionName, szDev, szReq, szActionName);
+ sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, szDev, szActionName, szData1);
+ }
+ break;
+
+ case ControlQuery:
+ {
+ char szData1[1024];
+ sz = mir_snprintf(szData1, soap_query, szActionName);
+ sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, "urn:schemas-upnp-org:control-1-0", "QueryStateVariable", szData1);
+ }
+ break;
+ }
+ szResult[0] = 0;
+ {
+ static const TIMEVAL tv = { 6, 0 };
+ static unsigned ttl = 4;
+ static u_long mode = 1;
+ fd_set rfd, wfd, efd;
+ SOCKADDR_IN enetaddr;
+
+retrycon:
+ if (sock == INVALID_SOCKET) {
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ enetaddr.sin_family = AF_INET;
+ enetaddr.sin_port = htons(sPort);
+ enetaddr.sin_addr.s_addr = inet_addr(szHost);
+
+ // Resolve host name if needed
+ if (enetaddr.sin_addr.s_addr == INADDR_NONE) {
+ PHOSTENT he = gethostbyname(szHost);
+ if (he)
+ enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0];
+ }
+
+ Netlib_Logf(nullptr, "UPnP HTTP connection Host: %s Port: %u", szHost, sPort);
+
+ FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd);
+ FD_SET(sock, &rfd); FD_SET(sock, &wfd); FD_SET(sock, &efd);
+
+ // Limit the scope of the connection (does not work for
+ setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned));
+
+ // Put socket into non-blocking mode for timeout on connect
+ ioctlsocket(sock, FIONBIO, &mode);
+
+ // Connect to the remote host
+ if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == SOCKET_ERROR) {
+ int err = WSAGetLastError();
+
+ // Socket connection failed
+ if (err != WSAEWOULDBLOCK) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP connect failed %d", err);
+ break;
+ }
+ // Wait for socket to connect
+ else if (select(1, &rfd, &wfd, &efd, &tv) != 1) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP connect timeout");
+ break;
+ }
+ else if (!FD_ISSET(sock, &wfd)) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP connect failed");
+ break;
+ }
+ }
+ strncpy_s(szConnHost, szHost, _TRUNCATE);
+ sConnPort = sPort;
+ }
+
+ if (send(sock, szData, sz, 0) != SOCKET_ERROR) {
+ char *hdrend = nullptr;
+ int acksz = 0, pktsz = 0;
+
+ if (szActionName == nullptr) {
+ int len = sizeof(locIP);
+ getsockname(sock, (SOCKADDR*)&locIP, &len);
+ if (locIP.sin_addr.S_un.S_addr == 0x0100007f) {
+ struct hostent *he;
+
+ gethostname(szPath, sizeof(szPath));
+ he = gethostbyname(szPath);
+ if (he != nullptr)
+ locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0];
+ }
+ }
+
+ LongLog(szData);
+ sz = 0;
+ while (true) {
+ int bytesRecv;
+
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+
+ // Wait for the next packet
+ if (select(1, &rfd, nullptr, nullptr, &tv) != 1) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP recieve timeout");
+ break;
+ }
+
+ //
+ bytesRecv = recv(sock, &szResult[sz], resSize - sz, 0);
+
+ // Connection closed or aborted, all data received
+ if (bytesRecv == 0 || bytesRecv == SOCKET_ERROR) {
+ closeRouterConnection();
+ if ((bytesRecv == SOCKET_ERROR || sz == 0) && retryCount < 2) {
+ ++retryCount;
+ goto retrycon;
+ }
+ break;
+ }
+
+ sz += bytesRecv;
+
+ // Insert null terminator to use string functions
+ if (sz >= (resSize - 1)) {
+ szResult[resSize - 1] = 0;
+ break;
+ }
+ else
+ szResult[sz] = 0;
+
+ // HTTP header found?
+ if (hdrend == nullptr) {
+ // Find HTTP header end
+ hdrend = strstr(szResult, "\r\n\r\n");
+ if (hdrend == nullptr) {
+ hdrend = strstr(szResult, "\n\n");
+ if (hdrend) hdrend += 2;
+ }
+
+ else
+ hdrend += 4;
+
+ if (hdrend != nullptr) {
+ // Get packet size if provided
+ if (txtParseParam(szResult, nullptr, "Content-Length:", "\n", szRes, sizeof(szRes)) ||
+ txtParseParam(szResult, nullptr, "CONTENT-LENGTH:", "\n", szRes, sizeof(szRes))) {
+ // Add size of HTTP header to the packet size to compute full transmission size
+ pktsz = atol(ltrimp(szRes)) + (hdrend - szResult);
+ }
+ // Get encoding type if provided
+ else if (txtParseParam(szResult, nullptr, "Transfer-Encoding:", "\n", szRes, sizeof(szRes))) {
+ if (_stricmp(lrtrimp(szRes), "Chunked") == 0)
+ acksz = hdrend - szResult;
+ }
+ if (txtParseParam(szResult, nullptr, "Connection:", "\n", szRes, sizeof(szRes))) {
+ needClose = (_stricmp(lrtrimp(szRes), "close") == 0);
+ }
+ }
+ }
+
+ // Content-Length bytes reached, all data received
+ if (sz >= pktsz && pktsz != 0) {
+ szResult[pktsz] = 0;
+ break;
+ }
+
+ // Chunked encoding processing
+ if (sz > acksz && acksz != 0) {
+retry:
+ // Parse out chunk size
+ char* data = szResult + acksz;
+ char* peol1 = data == hdrend ? data - 1 : strchr(data, '\n');
+ if (peol1 != nullptr) {
+ char *peol2 = strchr(++peol1, '\n');
+ if (peol2 != nullptr) {
+ // Get chunk size
+ int chunkBytes = strtol(peol1, nullptr, 16);
+ acksz += chunkBytes;
+ peol2++;
+
+ memmove(data, peol2, mir_strlen(peol2) + 1);
+ sz -= peol2 - data;
+
+ // Last chunk, all data received
+ if (chunkBytes == 0) break;
+ if (sz > acksz) goto retry;
+ }
+ }
+ }
+ }
+ LongLog(szResult);
+ }
+ else {
+ if (retryCount < 2) {
+ closeRouterConnection();
+ ++retryCount;
+ goto retrycon;
+ }
+ else
+ Netlib_Logf(nullptr, "UPnP send failed %d", WSAGetLastError());
+ }
+ }
+ txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes));
+ res = atol(szRes);
+ if (szActionName != nullptr && res == 405 && szPostHdr == soap_post_hdr)
+ szPostHdr = soap_post_hdr_m;
+ else
+ break;
+ }
+
+ if (needClose)
+ closeRouterConnection();
+
+ mir_free(szData);
+ mir_free(szReq);
+ return res;
+}
+
+static unsigned getExtIP(void)
+{
+ char szExtIP[30];
+ char* szData = (char*)mir_alloc(4096); szData[0] = 0;
+
+ unsigned extip = 0;
+ int res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress", ControlAction);
+ if (res == 200 && txtParseParam(szData, "<NewExternalIPAddress", ">", "<", szExtIP, sizeof(szExtIP)))
+ extip = ntohl(inet_addr(szExtIP));
+
+ mir_free(szData);
+ return extip;
+}
+
+static bool getUPnPURLs(char* szUrl, size_t sizeUrl)
+{
+ char* szData = (char*)mir_alloc(8192);
+
+ gatewayFound = httpTransact(szUrl, szData, 8192, nullptr, DeviceGetReq) == 200;
+ if (gatewayFound) {
+ char szTemp[256], *rpth;
+ size_t ctlLen;
+
+ txtParseParam(szData, nullptr, "<URLBase>", "</URLBase>", szTemp, sizeof(szTemp));
+ strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl));
+ szCtlUrl[sizeof(szCtlUrl) - 1] = 0;
+
+ mir_snprintf(szTemp, search_device, szDev);
+ txtParseParam(szData, szTemp, "<controlURL>", "</controlURL>", szUrl, sizeUrl);
+
+ // URL combining per RFC 2396
+ if (szUrl[0] != 0) {
+ if (strstr(szUrl, "://") != nullptr) // absolute URI
+ rpth = szCtlUrl;
+ else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path
+ {
+ rpth = strstr(szCtlUrl, "//");
+ if (rpth == nullptr) rpth = szCtlUrl;
+ }
+ else if (szUrl[0] == '/') // relative URI abs_path
+ {
+ rpth = strstr(szCtlUrl, "//");
+ rpth = rpth ? rpth + 2 : szCtlUrl;
+
+ rpth = strchr(rpth, '/');
+ if (rpth == nullptr) rpth = szCtlUrl + mir_strlen(szCtlUrl);
+ }
+ else { // relative URI rel_path
+ size_t ctlCLen = mir_strlen(szCtlUrl);
+ rpth = szCtlUrl + ctlCLen;
+ if (ctlCLen != 0 && *(rpth - 1) != '/')
+ strncpy(rpth++, "/", sizeof(szCtlUrl) - ctlCLen);
+ }
+
+ ctlLen = sizeof(szCtlUrl) - (rpth - szCtlUrl);
+ strncpy(rpth, szUrl, ctlLen);
+ szCtlUrl[sizeof(szCtlUrl) - 1] = 0;
+ }
+ else {
+ szCtlUrl[0] = 0;
+ gatewayFound = false;
+ }
+ }
+ mir_free(szData);
+
+ return gatewayFound;
+}
+
+static void discoverUPnP(void)
+{
+ char* buf;
+ int buflen;
+ unsigned i, j, nip = 0;
+ unsigned* ips = nullptr;
+
+ static const unsigned any = INADDR_ANY;
+ static const TIMEVAL tv = { 1, 600000 };
+
+ char szUrl[256] = "";
+ char hostname[256];
+ PHOSTENT he;
+ fd_set readfd;
+
+ SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ SOCKADDR_IN enetaddr;
+ enetaddr.sin_family = AF_INET;
+ enetaddr.sin_port = htons(1900);
+ enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250");
+
+ gethostname(hostname, sizeof(hostname));
+ he = gethostbyname(hostname);
+
+ if (he) {
+ while (he->h_addr_list[nip]) ++nip;
+
+ ips = (unsigned*)mir_alloc(nip * sizeof(unsigned));
+
+ for (j = 0; j < nip; j++)
+ ips[j] = *(unsigned*)he->h_addr_list[j];
+ }
+
+ buf = (char*)mir_alloc(1500);
+
+ for (i = 3; --i && szUrl[0] == 0;) {
+ for (j = 0; j < nip; j++) {
+ if (ips)
+ setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned));
+
+ buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1");
+ sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
+ LongLog(buf);
+
+ buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1");
+ sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
+ LongLog(buf);
+ }
+
+ if (Miranda_IsTerminated()) break;
+
+ FD_ZERO(&readfd);
+ FD_SET(s, &readfd);
+
+ while (select(1, &readfd, nullptr, nullptr, &tv) >= 1) {
+ buflen = recv(s, buf, 1500, 0);
+ if (buflen != SOCKET_ERROR) {
+ buf[buflen] = 0;
+ LongLog(buf);
+
+ if (txtParseParam(buf, nullptr, "LOCATION:", "\n", szUrl, sizeof(szUrl)) ||
+ txtParseParam(buf, nullptr, "Location:", "\n", szUrl, sizeof(szUrl))) {
+ char age[30];
+ char szHostNew[256], szHostExist[256];
+
+ lrtrim(szUrl);
+
+ parseURL(szUrl, szHostNew, nullptr, nullptr);
+ parseURL(szCtlUrl, szHostExist, nullptr, nullptr);
+ if (mir_strcmp(szHostNew, szHostExist) == 0) {
+ gatewayFound = true;
+ break;
+ }
+
+ txtParseParam(buf, nullptr, "ST:", "\n", szDev, sizeof(szDev));
+ txtParseParam(buf, "max-age", " = ", "\n", age, sizeof(age));
+ expireTime = atoi(lrtrimp(age));
+ lrtrim(szDev);
+
+ if (getUPnPURLs(szUrl, sizeof(szUrl))) {
+ gatewayFound = getExtIP() != 0;
+ if (gatewayFound) break;
+ }
+ }
+ }
+ FD_ZERO(&readfd);
+ FD_SET(s, &readfd);
+ }
+ }
+
+ mir_free(buf);
+ mir_free(ips);
+ setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned));
+ closesocket(s);
+}
+
+static bool findUPnPGateway(void)
+{
+ if ((time(0) - lastDiscTime) >= expireTime) {
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ time_t curTime = time(0);
+
+ if ((curTime - lastDiscTime) >= expireTime) {
+ gatewayFound = false;
+
+ discoverUPnP();
+ lastDiscTime = curTime;
+
+ Netlib_Logf(nullptr, "UPnP Gateway detected %d, Control URL: %s", gatewayFound, szCtlUrl);
+ }
+
+ ReleaseMutex(portListMutex);
+ }
+
+ return gatewayFound;
+}
+
+bool NetlibUPnPAddPortMapping(uint16_t intport, char *proto, uint16_t *extport, uint32_t *extip, bool search)
+{
+ int res = 0, i = 5;
+
+ if (findUPnPGateway()) {
+ char* szData = (char*)mir_alloc(4096);
+ char szExtIP[30];
+
+ *extport = intport - 1;
+ *extip = ntohl(locIP.sin_addr.S_un.S_addr);
+
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ do {
+ ++*extport;
+ mir_snprintf(szData, 4096, add_port_mapping,
+ *extport, proto, intport, inet_ntoa(locIP.sin_addr));
+ res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping", ControlAction);
+ txtParseParam(szData, nullptr, "<errorCode>", "</errorCode>", szExtIP, sizeof(szExtIP));
+
+ } while (search && res == 500 && atol(szExtIP) == 718 && --i);
+
+ mir_free(szData);
+
+ if (res == 200) {
+ unsigned ip = getExtIP();
+ if (ip) *extip = ip;
+
+ if (numports >= numportsAlloc)
+ mir_realloc(portList, sizeof(uint16_t)*(numportsAlloc += 10));
+ portList[numports++] = *extport;
+ }
+
+ ReleaseMutex(portListMutex);
+ }
+
+ return res == 200;
+}
+
+void NetlibUPnPDeletePortMapping(uint16_t extport, char* proto)
+{
+ if (extport == 0)
+ return;
+
+ // findUPnPGateway();
+
+ if (gatewayFound) {
+ unsigned i;
+ char* szData = (char*)mir_alloc(4096);
+
+ WaitForSingleObject(portListMutex, INFINITE);
+ mir_snprintf(szData, 4096, delete_port_mapping, extport, proto);
+ httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping", ControlAction);
+
+ for (i = 0; i < numports; i++)
+ if (portList[i] == extport && --numports > 0)
+ memmove(&portList[i], &portList[i + 1], (numports - i) * sizeof(uint16_t));
+
+ mir_free(szData);
+ ReleaseMutex(portListMutex);
+ }
+}
+
+void NetlibUPnPCleanup(void*)
+{
+ // upnp is disabled globally, no need for a cleanup
+ if (db_get_b(0, "Netlib", "NLEnableUPnP", 1) == 0)
+ return;
+
+ {
+ int incoming = 0;
+ mir_cslock lck(csNetlibUser);
+ for (auto &p : netlibUser)
+ if (p->user.flags & NUF_INCOMING) {
+ incoming = 1;
+ break;
+ }
+
+ if (!incoming)
+ return;
+ }
+
+ if (findUPnPGateway()) {
+ char *szData = (char*)alloca(4096);
+ char buf[50], lip[50];
+ unsigned j = 0, k, num = 100;
+
+ strncpy_s(lip, inet_ntoa(locIP.sin_addr), _TRUNCATE);
+
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 &&
+ txtParseParam(szData, "QueryStateVariableResponse", "<return>", "<", buf, sizeof(buf)))
+ num = atol(buf);
+
+ uint16_t ports[30];
+ for (unsigned i = 0; i < num && !Miranda_IsTerminated(); i++) {
+ mir_snprintf(szData, 4096, get_port_mapping, i);
+
+ ReleaseMutex(portListMutex);
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ if (httpTransact(szCtlUrl, szData, 4096, "GetGenericPortMappingEntry", ControlAction) != 200)
+ break;
+
+ if (!txtParseParam(szData, "<NewPortMappingDescription", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0)
+ continue;
+
+ if (!txtParseParam(szData, "<NewInternalClient", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0)
+ continue;
+
+ if (txtParseParam(szData, "<NewExternalPort", ">", "<", buf, sizeof(buf))) {
+ uint16_t mport = (uint16_t)atol(buf);
+
+ if (j >= _countof(ports))
+ break;
+
+ for (k = 0; k < numports; ++k)
+ if (portList[k] == mport)
+ break;
+
+ if (k >= numports)
+ ports[j++] = mport;
+ }
+ }
+
+ ReleaseMutex(portListMutex);
+
+ for (unsigned i = 0; i < j && !Miranda_IsTerminated(); i++)
+ NetlibUPnPDeletePortMapping(ports[i], "TCP");
+ }
+}
+
+void NetlibUPnPInit(void)
+{
+ numports = 0;
+ numportsAlloc = 10;
+ portList = (uint16_t*)mir_alloc(sizeof(uint16_t)*numportsAlloc);
+
+ portListMutex = CreateMutex(nullptr, FALSE, nullptr);
+}
+
+void NetlibUPnPDestroy(void)
+{
+ mir_free(portList);
+ CloseHandle(portListMutex);
+}
diff --git a/src/mir_app/src/netlib_websocket.cpp b/src/mir_app/src/netlib_websocket.cpp index 4b860cc0db..cd3aaaef4e 100644 --- a/src/mir_app/src/netlib_websocket.cpp +++ b/src/mir_app/src/netlib_websocket.cpp @@ -1,174 +1,174 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#include "../../libs/zlib/src/zlib.h" - -MIR_APP_DLL(NETLIBHTTPREQUEST*) WebSocket_Connect(HNETLIBUSER nlu, const char *szHost, NETLIBHTTPHEADER *pHeaders) -{ - CMStringA tmpHost(szHost); - - // connect to the gateway server - if (!mir_strncmp(tmpHost, "wss://", 6)) - tmpHost.Delete(0, 6); - - auto *nlr = new MHttpRequest; - nlr->flags = NLHRF_PERSISTENT | NLHRF_HTTP11 | NLHRF_SSL; - nlr->szUrl = tmpHost.GetBuffer(); - nlr->AddHeader("Accept", "*/*"); - nlr->AddHeader("Upgrade", "websocket"); - nlr->AddHeader("Pragma", "no-cache"); - nlr->AddHeader("Cache-Control", "no-cache"); - nlr->AddHeader("Connection", "keep-alive, Upgrade"); - - uint8_t binNonce[16]; - Utils_GetRandom(binNonce, sizeof(binNonce)); - nlr->AddHeader("Sec-WebSocket-Key", ptrA(mir_base64_encode(binNonce, sizeof(binNonce)))); - nlr->AddHeader("Sec-WebSocket-Version", "13"); - nlr->AddHeader("Sec-WebSocket-Extensions", "permessage-deflate; client_max_window_bits"); - - if (pHeaders) { - while (pHeaders->szName != nullptr) { - nlr->AddHeader(pHeaders->szName, pHeaders->szValue); - pHeaders++; - } - } - - auto *pReply = Netlib_HttpTransaction(nlu, nlr); - delete nlr; - - if (pReply == nullptr) { - Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, send failed", tmpHost.c_str()); - return nullptr; - } - - if (pReply->resultCode != 101) - Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, status %d", tmpHost.c_str(), pReply->resultCode); - - return pReply; -} - -MIR_APP_DLL(bool) WebSocket_InitHeader(WSHeader &hdr, const void *pData, size_t bufSize) -{ - if (bufSize < 2) - return false; - - auto *buf = (const uint8_t *)pData; - hdr.bIsFinal = (buf[0] & 0x80) != 0; - hdr.bIsMasked = (buf[1] & 0x80) != 0; - hdr.opCode = buf[0] & 0x0F; - hdr.firstByte = buf[1] & 0x7F; - hdr.headerSize = 2 + (hdr.firstByte == 0x7E ? 2 : 0) + (hdr.firstByte == 0x7F ? 8 : 0) + (hdr.bIsMasked ? 4 : 0); - if (bufSize < hdr.headerSize) - return false; - - uint64_t tmpSize = 0; - switch (hdr.firstByte) { - case 0x7F: - tmpSize += ((uint64_t)buf[2]) << 56; - tmpSize += ((uint64_t)buf[3]) << 48; - tmpSize += ((uint64_t)buf[4]) << 40; - tmpSize += ((uint64_t)buf[5]) << 32; - tmpSize += ((uint64_t)buf[6]) << 24; - tmpSize += ((uint64_t)buf[7]) << 16; - tmpSize += ((uint64_t)buf[8]) << 8; - tmpSize += ((uint64_t)buf[9]); - break; - - case 0x7E: - tmpSize += ((uint64_t)buf[2]) << 8; - tmpSize += ((uint64_t)buf[3]); - break; - - default: - tmpSize = hdr.firstByte; - } - hdr.payloadSize = tmpSize; - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void WebSocket_Send(HNETLIBCONN nlc, const void *pData, size_t dataLen, uint8_t opCode) -{ - uint8_t header[20]; - size_t datalen; - - header[0] = 0x80 + (opCode & 0x7F); - if (dataLen < 126) { - header[1] = (dataLen & 0xFF); - datalen = 2; - } - else if (dataLen < 65536) { - header[1] = 0x7E; - header[2] = (dataLen >> 8) & 0xFF; - header[3] = dataLen & 0xFF; - datalen = 4; - } - else { - header[1] = 0x7F; - header[2] = (dataLen >> 56) & 0xff; - header[3] = (dataLen >> 48) & 0xff; - header[4] = (dataLen >> 40) & 0xff; - header[5] = (dataLen >> 32) & 0xff; - header[6] = (dataLen >> 24) & 0xff; - header[7] = (dataLen >> 16) & 0xff; - header[8] = (dataLen >> 8) & 0xff; - header[9] = dataLen & 0xff; - datalen = 10; - } - - union { - uint32_t dwMask; - uint8_t arMask[4]; - }; - - dwMask = crc32(rand(), (uint8_t*)pData, (unsigned)dataLen); - memcpy(header + datalen, arMask, _countof(arMask)); - datalen += _countof(arMask); - header[1] |= 0x80; - - ptrA sendBuf((char*)mir_alloc(dataLen + datalen)); - memcpy(sendBuf, header, datalen); - if (dataLen) { - memcpy(sendBuf.get() + datalen, pData, dataLen); - for (size_t i = 0; i < dataLen; i++) - sendBuf[i + datalen] ^= arMask[i & 3]; - } - Netlib_Send(nlc, sendBuf, int(dataLen + datalen), MSG_NODUMP); -} - -MIR_APP_DLL(void) WebSocket_SendText(HNETLIBCONN nlc, const char *pData) -{ - if (nlc && pData) - WebSocket_Send(nlc, pData, strlen(pData), 1); -} - -MIR_APP_DLL(void) WebSocket_SendBinary(HNETLIBCONN nlc, const void *pData, size_t dataLen) -{ - if (nlc && pData) - WebSocket_Send(nlc, pData, dataLen, 2); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#include "../../libs/zlib/src/zlib.h"
+
+MIR_APP_DLL(NETLIBHTTPREQUEST*) WebSocket_Connect(HNETLIBUSER nlu, const char *szHost, NETLIBHTTPHEADER *pHeaders)
+{
+ CMStringA tmpHost(szHost);
+
+ // connect to the gateway server
+ if (!mir_strncmp(tmpHost, "wss://", 6))
+ tmpHost.Delete(0, 6);
+
+ auto *nlr = new MHttpRequest;
+ nlr->flags = NLHRF_PERSISTENT | NLHRF_HTTP11 | NLHRF_SSL;
+ nlr->szUrl = tmpHost.GetBuffer();
+ nlr->AddHeader("Accept", "*/*");
+ nlr->AddHeader("Upgrade", "websocket");
+ nlr->AddHeader("Pragma", "no-cache");
+ nlr->AddHeader("Cache-Control", "no-cache");
+ nlr->AddHeader("Connection", "keep-alive, Upgrade");
+
+ uint8_t binNonce[16];
+ Utils_GetRandom(binNonce, sizeof(binNonce));
+ nlr->AddHeader("Sec-WebSocket-Key", ptrA(mir_base64_encode(binNonce, sizeof(binNonce))));
+ nlr->AddHeader("Sec-WebSocket-Version", "13");
+ nlr->AddHeader("Sec-WebSocket-Extensions", "permessage-deflate; client_max_window_bits");
+
+ if (pHeaders) {
+ while (pHeaders->szName != nullptr) {
+ nlr->AddHeader(pHeaders->szName, pHeaders->szValue);
+ pHeaders++;
+ }
+ }
+
+ auto *pReply = Netlib_HttpTransaction(nlu, nlr);
+ delete nlr;
+
+ if (pReply == nullptr) {
+ Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, send failed", tmpHost.c_str());
+ return nullptr;
+ }
+
+ if (pReply->resultCode != 101)
+ Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, status %d", tmpHost.c_str(), pReply->resultCode);
+
+ return pReply;
+}
+
+MIR_APP_DLL(bool) WebSocket_InitHeader(WSHeader &hdr, const void *pData, size_t bufSize)
+{
+ if (bufSize < 2)
+ return false;
+
+ auto *buf = (const uint8_t *)pData;
+ hdr.bIsFinal = (buf[0] & 0x80) != 0;
+ hdr.bIsMasked = (buf[1] & 0x80) != 0;
+ hdr.opCode = buf[0] & 0x0F;
+ hdr.firstByte = buf[1] & 0x7F;
+ hdr.headerSize = 2 + (hdr.firstByte == 0x7E ? 2 : 0) + (hdr.firstByte == 0x7F ? 8 : 0) + (hdr.bIsMasked ? 4 : 0);
+ if (bufSize < hdr.headerSize)
+ return false;
+
+ uint64_t tmpSize = 0;
+ switch (hdr.firstByte) {
+ case 0x7F:
+ tmpSize += ((uint64_t)buf[2]) << 56;
+ tmpSize += ((uint64_t)buf[3]) << 48;
+ tmpSize += ((uint64_t)buf[4]) << 40;
+ tmpSize += ((uint64_t)buf[5]) << 32;
+ tmpSize += ((uint64_t)buf[6]) << 24;
+ tmpSize += ((uint64_t)buf[7]) << 16;
+ tmpSize += ((uint64_t)buf[8]) << 8;
+ tmpSize += ((uint64_t)buf[9]);
+ break;
+
+ case 0x7E:
+ tmpSize += ((uint64_t)buf[2]) << 8;
+ tmpSize += ((uint64_t)buf[3]);
+ break;
+
+ default:
+ tmpSize = hdr.firstByte;
+ }
+ hdr.payloadSize = tmpSize;
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void WebSocket_Send(HNETLIBCONN nlc, const void *pData, size_t dataLen, uint8_t opCode)
+{
+ uint8_t header[20];
+ size_t datalen;
+
+ header[0] = 0x80 + (opCode & 0x7F);
+ if (dataLen < 126) {
+ header[1] = (dataLen & 0xFF);
+ datalen = 2;
+ }
+ else if (dataLen < 65536) {
+ header[1] = 0x7E;
+ header[2] = (dataLen >> 8) & 0xFF;
+ header[3] = dataLen & 0xFF;
+ datalen = 4;
+ }
+ else {
+ header[1] = 0x7F;
+ header[2] = (dataLen >> 56) & 0xff;
+ header[3] = (dataLen >> 48) & 0xff;
+ header[4] = (dataLen >> 40) & 0xff;
+ header[5] = (dataLen >> 32) & 0xff;
+ header[6] = (dataLen >> 24) & 0xff;
+ header[7] = (dataLen >> 16) & 0xff;
+ header[8] = (dataLen >> 8) & 0xff;
+ header[9] = dataLen & 0xff;
+ datalen = 10;
+ }
+
+ union {
+ uint32_t dwMask;
+ uint8_t arMask[4];
+ };
+
+ dwMask = crc32(rand(), (uint8_t*)pData, (unsigned)dataLen);
+ memcpy(header + datalen, arMask, _countof(arMask));
+ datalen += _countof(arMask);
+ header[1] |= 0x80;
+
+ ptrA sendBuf((char*)mir_alloc(dataLen + datalen));
+ memcpy(sendBuf, header, datalen);
+ if (dataLen) {
+ memcpy(sendBuf.get() + datalen, pData, dataLen);
+ for (size_t i = 0; i < dataLen; i++)
+ sendBuf[i + datalen] ^= arMask[i & 3];
+ }
+ Netlib_Send(nlc, sendBuf, int(dataLen + datalen), MSG_NODUMP);
+}
+
+MIR_APP_DLL(void) WebSocket_SendText(HNETLIBCONN nlc, const char *pData)
+{
+ if (nlc && pData)
+ WebSocket_Send(nlc, pData, strlen(pData), 1);
+}
+
+MIR_APP_DLL(void) WebSocket_SendBinary(HNETLIBCONN nlc, const void *pData, size_t dataLen)
+{
+ if (nlc && pData)
+ WebSocket_Send(nlc, pData, dataLen, 2);
+}
diff --git a/src/mir_app/src/newplugins.cpp b/src/mir_app/src/newplugins.cpp index 6a79d72330..367afde7b0 100644 --- a/src/mir_app/src/newplugins.cpp +++ b/src/mir_app/src/newplugins.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/options.cpp b/src/mir_app/src/options.cpp index ea7f32ac99..81c136a0ab 100644 --- a/src/mir_app/src/options.cpp +++ b/src/mir_app/src/options.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
Copyright (c) 2007 Artem Shpynov
all portions of this codebase are copyrighted to the people
diff --git a/src/mir_app/src/path.cpp b/src/mir_app/src/path.cpp index 616100510f..49e5cd7487 100644 --- a/src/mir_app/src/path.cpp +++ b/src/mir_app/src/path.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/pluginopts.cpp b/src/mir_app/src/pluginopts.cpp index c601a83072..7e58c5fca3 100644 --- a/src/mir_app/src/pluginopts.cpp +++ b/src/mir_app/src/pluginopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/popupOption.cpp b/src/mir_app/src/popupOption.cpp index 6749ba4e9c..4ac5290c6d 100644 --- a/src/mir_app/src/popupOption.cpp +++ b/src/mir_app/src/popupOption.cpp @@ -1,123 +1,123 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "plugins.h" - -struct MPopupOption -{ - MPopupOption(CMPluginBase *pPlugin, const char *pszDescr, CMOption<bool> &pVal) : - m_plugin(pPlugin), - m_val(pVal), - m_descr(pszDescr) - {} - - MPopupOption(CMPluginBase *pPlugin, const wchar_t *pwszDescr, CMOption<bool> &pVal) : - m_plugin(pPlugin), - m_val(pVal), - m_descr(pwszDescr) - {} - - CMPluginBase *m_plugin; - CMOption<bool> &m_val; - CMStringW m_descr; -}; - -static OBJLIST<MPopupOption> g_arOptions(1); - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addPopupOption(const char *pszDescr, CMOption<bool> &pVal) -{ - g_arOptions.insert(new MPopupOption(this, pszDescr, pVal)); - return 0; -} - -int CMPluginBase::addPopupOption(const wchar_t *pwszDescr, CMOption<bool> &pVal) -{ - g_arOptions.insert(new MPopupOption(this, pwszDescr, pVal)); - return 0; -} - -void KillModulePopups(CMPluginBase *pPlugin) -{ - for (auto &it : g_arOptions.rev_iter()) - if (it->m_plugin == pPlugin) - g_arOptions.remove(g_arOptions.indexOf(&it)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CPopupOptionsDlg : public CDlgBase -{ - CCtrlListView m_list; - -public: - CPopupOptionsDlg() : - CDlgBase(g_plugin, IDD_OPT_POPUPOPTION), - m_list(this, IDC_TREE) - {} - - bool OnInitDialog() override - { - m_list.SetExtendedListViewStyleEx(0, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT); - - LVITEM lvi; - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iSubItem = 0; - - for (auto &it : g_arOptions) { - lvi.pszText = TranslateW_LP(it->m_descr, it->m_plugin); - lvi.lParam = LPARAM(it); - - int iRow = m_list.InsertItem(&lvi); - m_list.SetItemState(iRow, it->m_val ? 0x2000 : 0x1000, LVIS_STATEIMAGEMASK); - } - - return true; - } - - bool OnApply() override - { - int iRows = m_list.GetItemCount(); - - for (int i = 0; i < iRows; i++) { - auto *p = (MPopupOption *)m_list.GetItemData(i); - p->m_val = m_list.GetItemState(i, LVIS_STATEIMAGEMASK) == 0x2000; - } - return true; - } -}; - -int PopupOptionsInit(WPARAM wParam) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = -1000000000; - odp.szGroup.a = LPGEN("Popups"); - odp.szTitle.a = LPGEN("Events"); - odp.pDialog = new CPopupOptionsDlg(); - odp.flags = ODPF_BOLDGROUPS; - g_plugin.addOptions(wParam, &odp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "plugins.h"
+
+struct MPopupOption
+{
+ MPopupOption(CMPluginBase *pPlugin, const char *pszDescr, CMOption<bool> &pVal) :
+ m_plugin(pPlugin),
+ m_val(pVal),
+ m_descr(pszDescr)
+ {}
+
+ MPopupOption(CMPluginBase *pPlugin, const wchar_t *pwszDescr, CMOption<bool> &pVal) :
+ m_plugin(pPlugin),
+ m_val(pVal),
+ m_descr(pwszDescr)
+ {}
+
+ CMPluginBase *m_plugin;
+ CMOption<bool> &m_val;
+ CMStringW m_descr;
+};
+
+static OBJLIST<MPopupOption> g_arOptions(1);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPluginBase::addPopupOption(const char *pszDescr, CMOption<bool> &pVal)
+{
+ g_arOptions.insert(new MPopupOption(this, pszDescr, pVal));
+ return 0;
+}
+
+int CMPluginBase::addPopupOption(const wchar_t *pwszDescr, CMOption<bool> &pVal)
+{
+ g_arOptions.insert(new MPopupOption(this, pwszDescr, pVal));
+ return 0;
+}
+
+void KillModulePopups(CMPluginBase *pPlugin)
+{
+ for (auto &it : g_arOptions.rev_iter())
+ if (it->m_plugin == pPlugin)
+ g_arOptions.remove(g_arOptions.indexOf(&it));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CPopupOptionsDlg : public CDlgBase
+{
+ CCtrlListView m_list;
+
+public:
+ CPopupOptionsDlg() :
+ CDlgBase(g_plugin, IDD_OPT_POPUPOPTION),
+ m_list(this, IDC_TREE)
+ {}
+
+ bool OnInitDialog() override
+ {
+ m_list.SetExtendedListViewStyleEx(0, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT);
+
+ LVITEM lvi;
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.iSubItem = 0;
+
+ for (auto &it : g_arOptions) {
+ lvi.pszText = TranslateW_LP(it->m_descr, it->m_plugin);
+ lvi.lParam = LPARAM(it);
+
+ int iRow = m_list.InsertItem(&lvi);
+ m_list.SetItemState(iRow, it->m_val ? 0x2000 : 0x1000, LVIS_STATEIMAGEMASK);
+ }
+
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ int iRows = m_list.GetItemCount();
+
+ for (int i = 0; i < iRows; i++) {
+ auto *p = (MPopupOption *)m_list.GetItemData(i);
+ p->m_val = m_list.GetItemState(i, LVIS_STATEIMAGEMASK) == 0x2000;
+ }
+ return true;
+ }
+};
+
+int PopupOptionsInit(WPARAM wParam)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = -1000000000;
+ odp.szGroup.a = LPGEN("Popups");
+ odp.szTitle.a = LPGEN("Events");
+ odp.pDialog = new CPopupOptionsDlg();
+ odp.flags = ODPF_BOLDGROUPS;
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/mir_app/src/popups.cpp b/src/mir_app/src/popups.cpp index bc4d5f86d8..aa7af870f3 100644 --- a/src/mir_app/src/popups.cpp +++ b/src/mir_app/src/popups.cpp @@ -1,157 +1,157 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static bool bModuleInit = false, bPopupsEnabled = true; - -MIR_APP_DLL(bool) Popup_Enabled() -{ - if (!bModuleInit) { - bModuleInit = true; - bPopupsEnabled = db_get_b(0, "Popup", "ModuleIsEnabled", 1) != 0; - } - - return bPopupsEnabled; -} - -MIR_APP_DLL(void) Popup_Enable(bool bEnable) -{ - bPopupsEnabled = bEnable; - db_set_b(0, "Popup", "ModuleIsEnabled", bEnable); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Popup v2.0 - -MIR_APP_DLL(HWND) Popup_Add(const POPUPDATA2 *ppdp, int flags) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUP2, (WPARAM)ppdp, flags); -} - -MIR_APP_DLL(void) Popup_Change(HWND hwndPopup, const POPUPDATA2 *pData) -{ - CallService(MS_POPUP_CHANGEPOPUP2, (WPARAM)hwndPopup, (LPARAM)pData); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Popups v1.0 - -MIR_APP_DLL(HWND) PUAddPopup(POPUPDATA *ppdp, int flags) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUP, (WPARAM)ppdp, flags); -} - -MIR_APP_DLL(HWND) PUAddPopupW(POPUPDATAW *ppdp, int flags) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUPW, (WPARAM)ppdp, flags); -} - -MIR_APP_DLL(int) PUChangeW(HWND hWndPopup, POPUPDATAW *newData) -{ - return (int)CallService(MS_POPUP_CHANGEW, (WPARAM)hWndPopup, (LPARAM)newData); -} - -MIR_APP_DLL(int) PUChangeTextW(HWND hWndPopup, const wchar_t *lpwzNewText) -{ - return (int)CallService(MS_POPUP_CHANGETEXTW, (WPARAM)hWndPopup, (LPARAM)lpwzNewText); -} - -MIR_APP_DLL(int) PUDeletePopup(HWND hWndPopup) -{ - return (int)CallService(MS_POPUP_DESTROYPOPUP, 0, (LPARAM)hWndPopup); -} - -MIR_APP_DLL(MCONTACT) PUGetContact(HWND hPopupWindow) -{ - return (MCONTACT)CallService(MS_POPUP_GETCONTACT, (WPARAM)hPopupWindow, 0); -} - -MIR_APP_DLL(void*) PUGetPluginData(HWND hPopupWindow) -{ - return (void*)CallService(MS_POPUP_GETPLUGINDATA, (WPARAM)hPopupWindow, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Messages - -MIR_APP_DLL(int) PUShowMessage(const char *lpzText, uint32_t kind) -{ - return (int)CallService(MS_POPUP_SHOWMESSAGE, (WPARAM)lpzText, (LPARAM)kind); -} - -MIR_APP_DLL(int) PUShowMessageW(const wchar_t *lpwzText, uint32_t kind) -{ - return (int)CallService(MS_POPUP_SHOWMESSAGEW, (WPARAM)lpwzText, (LPARAM)kind); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Actions - -MIR_APP_DLL(int) PURegisterActions(POPUPACTION *actions, int count) -{ - return (int)CallService(MS_POPUP_REGISTERACTIONS, (WPARAM)actions, (LPARAM)count); -} - -MIR_APP_DLL(HANDLE) PURegisterNotification(POPUPNOTIFICATION *notification) -{ - return (HANDLE)CallService(MS_POPUP_REGISTERNOTIFICATION, (WPARAM)notification, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Popup classes - -MIR_APP_DLL(HANDLE) Popup_RegisterClass(POPUPCLASS *pc) -{ - return (HANDLE)CallService(MS_POPUP_REGISTERCLASS, 0, LPARAM(pc)); -} - -MIR_APP_DLL(void) Popup_UnregisterClass(HANDLE ppc) -{ - if (ppc) - CallService(MS_POPUP_UNREGISTERCLASS, 0, LPARAM(ppc)); -} - -MIR_APP_DLL(HWND) Popup_AddClass(POPUPDATACLASS *pData) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)pData); -} - -MIR_APP_DLL(HWND) ShowClassPopup(const char *name, const char *title, const char *text) -{ - POPUPDATACLASS d = {}; - d.pszClassName = name; - d.szTitle.a = title; - d.szText.a = text; - return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d); -} - -MIR_APP_DLL(HWND) ShowClassPopupW(const char *name, const wchar_t *title, const wchar_t *text) -{ - POPUPDATACLASS d = {}; - d.pszClassName = name; - d.szTitle.w = title; - d.szText.w = text; - return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static bool bModuleInit = false, bPopupsEnabled = true;
+
+MIR_APP_DLL(bool) Popup_Enabled()
+{
+ if (!bModuleInit) {
+ bModuleInit = true;
+ bPopupsEnabled = db_get_b(0, "Popup", "ModuleIsEnabled", 1) != 0;
+ }
+
+ return bPopupsEnabled;
+}
+
+MIR_APP_DLL(void) Popup_Enable(bool bEnable)
+{
+ bPopupsEnabled = bEnable;
+ db_set_b(0, "Popup", "ModuleIsEnabled", bEnable);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Popup v2.0
+
+MIR_APP_DLL(HWND) Popup_Add(const POPUPDATA2 *ppdp, int flags)
+{
+ return (HWND)CallService(MS_POPUP_ADDPOPUP2, (WPARAM)ppdp, flags);
+}
+
+MIR_APP_DLL(void) Popup_Change(HWND hwndPopup, const POPUPDATA2 *pData)
+{
+ CallService(MS_POPUP_CHANGEPOPUP2, (WPARAM)hwndPopup, (LPARAM)pData);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Popups v1.0
+
+MIR_APP_DLL(HWND) PUAddPopup(POPUPDATA *ppdp, int flags)
+{
+ return (HWND)CallService(MS_POPUP_ADDPOPUP, (WPARAM)ppdp, flags);
+}
+
+MIR_APP_DLL(HWND) PUAddPopupW(POPUPDATAW *ppdp, int flags)
+{
+ return (HWND)CallService(MS_POPUP_ADDPOPUPW, (WPARAM)ppdp, flags);
+}
+
+MIR_APP_DLL(int) PUChangeW(HWND hWndPopup, POPUPDATAW *newData)
+{
+ return (int)CallService(MS_POPUP_CHANGEW, (WPARAM)hWndPopup, (LPARAM)newData);
+}
+
+MIR_APP_DLL(int) PUChangeTextW(HWND hWndPopup, const wchar_t *lpwzNewText)
+{
+ return (int)CallService(MS_POPUP_CHANGETEXTW, (WPARAM)hWndPopup, (LPARAM)lpwzNewText);
+}
+
+MIR_APP_DLL(int) PUDeletePopup(HWND hWndPopup)
+{
+ return (int)CallService(MS_POPUP_DESTROYPOPUP, 0, (LPARAM)hWndPopup);
+}
+
+MIR_APP_DLL(MCONTACT) PUGetContact(HWND hPopupWindow)
+{
+ return (MCONTACT)CallService(MS_POPUP_GETCONTACT, (WPARAM)hPopupWindow, 0);
+}
+
+MIR_APP_DLL(void*) PUGetPluginData(HWND hPopupWindow)
+{
+ return (void*)CallService(MS_POPUP_GETPLUGINDATA, (WPARAM)hPopupWindow, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Messages
+
+MIR_APP_DLL(int) PUShowMessage(const char *lpzText, uint32_t kind)
+{
+ return (int)CallService(MS_POPUP_SHOWMESSAGE, (WPARAM)lpzText, (LPARAM)kind);
+}
+
+MIR_APP_DLL(int) PUShowMessageW(const wchar_t *lpwzText, uint32_t kind)
+{
+ return (int)CallService(MS_POPUP_SHOWMESSAGEW, (WPARAM)lpwzText, (LPARAM)kind);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Actions
+
+MIR_APP_DLL(int) PURegisterActions(POPUPACTION *actions, int count)
+{
+ return (int)CallService(MS_POPUP_REGISTERACTIONS, (WPARAM)actions, (LPARAM)count);
+}
+
+MIR_APP_DLL(HANDLE) PURegisterNotification(POPUPNOTIFICATION *notification)
+{
+ return (HANDLE)CallService(MS_POPUP_REGISTERNOTIFICATION, (WPARAM)notification, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Popup classes
+
+MIR_APP_DLL(HANDLE) Popup_RegisterClass(POPUPCLASS *pc)
+{
+ return (HANDLE)CallService(MS_POPUP_REGISTERCLASS, 0, LPARAM(pc));
+}
+
+MIR_APP_DLL(void) Popup_UnregisterClass(HANDLE ppc)
+{
+ if (ppc)
+ CallService(MS_POPUP_UNREGISTERCLASS, 0, LPARAM(ppc));
+}
+
+MIR_APP_DLL(HWND) Popup_AddClass(POPUPDATACLASS *pData)
+{
+ return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)pData);
+}
+
+MIR_APP_DLL(HWND) ShowClassPopup(const char *name, const char *title, const char *text)
+{
+ POPUPDATACLASS d = {};
+ d.pszClassName = name;
+ d.szTitle.a = title;
+ d.szText.a = text;
+ return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d);
+}
+
+MIR_APP_DLL(HWND) ShowClassPopupW(const char *name, const wchar_t *title, const wchar_t *text)
+{
+ POPUPDATACLASS d = {};
+ d.pszClassName = name;
+ d.szTitle.w = title;
+ d.szText.w = text;
+ return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d);
+}
diff --git a/src/mir_app/src/profilemanager.cpp b/src/mir_app/src/profilemanager.cpp index edbd7101cb..b2ea141b6c 100644 --- a/src/mir_app/src/profilemanager.cpp +++ b/src/mir_app/src/profilemanager.cpp @@ -1,641 +1,641 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "plugins.h" -#include "langpack.h" -#include "profilemanager.h" -#include <sys/stat.h> - -#pragma warning(disable : 4512) - -#define WM_INPUTCHANGED (WM_USER + 0x3000) -#define WM_FOCUSTEXTBOX (WM_USER + 0x3001) - -///////////////////////////////////////////////////////////////////////////////////////// -// Profile creator - -static BOOL EnumProfilesForList(const wchar_t *tszFullPath, wchar_t *profile, CCtrlListView &list, const wchar_t *szProfile) -{ - wchar_t sizeBuf[64]; - bool bFileLocked; - - wchar_t *p = wcsrchr(profile, '.'); - mir_wstrcpy(sizeBuf, L"0 KB"); - if (p != nullptr) *p = 0; - - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; - item.pszText = profile; - item.iItem = 0; - - struct _stat statbuf; - if (_wstat(tszFullPath, &statbuf) == 0) { - if (statbuf.st_size > 1000000) { - mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1048576.0); - mir_wstrcpy(sizeBuf + 5, L" MB"); - } - else { - mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1024.0); - mir_wstrcpy(sizeBuf + 5, L" KB"); - } - bFileLocked = Profile_CheckOpened(tszFullPath); - } - else bFileLocked = true; - - DATABASELINK *dblink; - switch (touchDatabase(tszFullPath, &dblink)) { - case ERROR_SUCCESS: - item.iImage = (bFileLocked) ? 1 : 0; - break; - - case EGROKPRF_OBSOLETE: - item.iImage = 2; - break; - - case EGROKPRF_CANTREAD: - item.iImage = (bFileLocked) ? 1 : 3; - break; - - default: - item.iImage = 3; - } - - item.lParam = (LPARAM)dblink; - - int iItem = list.InsertItem(&item); - if (mir_wstrcmpi(szProfile, tszFullPath) == 0) - list.SetItemState(iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); - - list.SetItemText(iItem, 2, sizeBuf); - - if (dblink != nullptr) - list.SetItemText(iItem, 1, TranslateW(dblink->szFullName)); - else if (bFileLocked) // file locked - list.SetItemText(iItem, 1, TranslateT("<In use>")); - else - list.SetItemText(iItem, 1, TranslateT("<Unknown format>")); - - return TRUE; -} - -static int findProfiles(CCtrlListView &list, const wchar_t *szProfile) -{ - // find in Miranda NG profile subfolders - MFilePath searchspec; - searchspec.Format(L"%s\\*.*", g_profileDir); - - for (auto &it: searchspec.search()) { - // find all subfolders except "." and ".." - if (!it.isDir() || !wcscmp(it.getPath(), L".") || !wcscmp(it.getPath(), L"..")) - continue; - - MFilePath fullPath; - fullPath.Format(L"%s\\%s\\%s.dat", g_profileDir, it.getPath(), it.getPath()); - if (fullPath.isExist()) { - wchar_t profileName[MAX_PATH]; - mir_snwprintf(profileName, L"%s.dat", it.getPath()); - if (!EnumProfilesForList(fullPath, profileName, list, szProfile)) - break; - } - } - - return 1; -} - -static LRESULT CALLBACK ProfileNameValidate(HWND edit, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_CHAR) { - if (wcschr(L".?/\\#' ", (wchar_t)wParam) != nullptr) - return 0; - PostMessage(GetParent(edit), WM_INPUTCHANGED, 0, 0); - } - return mir_callNextSubclass(edit, ProfileNameValidate, msg, wParam, lParam); -} - -class CCreateProfileDlg : public CDlgBase -{ - CCtrlButton &m_btnOk; - PROFILEMANAGERDATA *m_pd; - - int CreateProfile(const wchar_t *profile, DATABASELINK *link) - { - // check if the file already exists - const wchar_t *file = wcsrchr(profile, '\\'); - if (file) - file++; - - int err = 0; - wchar_t buf[256]; - - if (_waccess(profile, 0) == 0) { - // file already exists! - mir_snwprintf(buf, - TranslateT("The profile '%s' already exists. Do you want to move it to the Recycle Bin?\n\nWARNING: The profile will be deleted if Recycle Bin is disabled.\nWARNING: A profile may contain confidential information and should be properly deleted."), - file); - if (MessageBoxW(m_hwnd, buf, TranslateT("The profile already exists"), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES) - return 0; - - // move the file - if (DeleteDirectoryTreeW(profile, true) != 0) { - mir_snwprintf(buf, TranslateT("Couldn't move '%s' to the Recycle Bin. Please select another profile name."), file); - MessageBoxW(m_hwnd, buf, TranslateT("Problem moving profile"), MB_ICONINFORMATION | MB_OK); - return 0; - } - // now the file should be gone! - } - // ask the database to create the profile - CreatePathToFileW(profile); - if ((err = link->makeDatabase(profile)) != ERROR_SUCCESS) { - mir_snwprintf(buf, TranslateT("Unable to create the profile '%s', the error was %x"), file, err); - MessageBoxW(m_hwnd, buf, TranslateT("Problem creating profile"), MB_ICONERROR | MB_OK); - return 0; - } - - // the profile has been created! - g_bDbCreated = true; - return 1; - } - - bool m_bFocused; - CCtrlCombo m_driverList; - CCtrlEdit m_profileName; - CCtrlBase m_warning; - -public: - CCreateProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) : - CDlgBase(g_plugin, IDD_PROFILE_NEW), - m_btnOk(_btn), - m_pd(_pd), - m_bFocused(false), - m_driverList(this, IDC_PROFILEDRIVERS), - m_profileName(this, IDC_PROFILENAME), - m_warning(this, IDC_NODBDRIVERS) - {} - - bool OnInitDialog() override - { - // what, no plugins?! - if (arDbPlugins.getCount() == 0) { - m_driverList.Enable(false); - m_profileName.Enable(false); - ShowWindow(m_warning.GetHwnd(), TRUE); - } - else { - for (auto &p : arDbPlugins) - if (p->capabilities & MDB_CAPS_CREATE) - m_driverList.AddString(TranslateW(p->szFullName), (LPARAM)p); - } - - // default item - m_driverList.SetCurSel(0); - - // subclass the profile name box - mir_subclassWindow(m_profileName.GetHwnd(), ProfileNameValidate); - - // decide if there is a default profile name given in the INI and if it should be used - if (m_pd->noProfiles || (shouldAutoCreate(m_pd->m_profile) && !m_pd->m_profile.isExist())) { - wchar_t *profile = wcsrchr(m_pd->m_profile.GetBuffer(), '\\'); - if (profile) ++profile; - else profile = m_pd->m_profile.GetBuffer(); - - wchar_t *p = wcsrchr(profile, '.'); - wchar_t c = 0; - if (p) { c = *p; *p = 0; } - - m_profileName.SetText(profile); - if (c) *p = c; - } - - // focus on the textbox - PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0); - return true; - } - - INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override - { - switch (msg) { - case WM_FOCUSTEXTBOX: - SetFocus(m_profileName.GetHwnd()); - break; - - case WM_INPUTCHANGED: // when input in the edit box changes - NotifyChange(); - m_btnOk.Enable(GetWindowTextLength(m_profileName.GetHwnd()) > 0); - break; - - case WM_SHOWWINDOW: - if (wParam) { - m_btnOk.SetText(TranslateT("&Create")); - SendMessage(m_hwnd, WM_INPUTCHANGED, 0, 0); - m_bFocused = true; - } - else m_bFocused = false; - break; - } - return CDlgBase::DlgProc(msg, wParam, lParam); - } - - bool OnApply() override - { - LRESULT curSel = m_driverList.GetCurSel(); - if (curSel == -1 || !m_bFocused) - return false; // should never happen - - ptrW szName(m_profileName.GetText()); - if (mir_wstrlen(szName) == 0) - return false; - - // profile placed in "profile_name" subfolder - m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, szName.get(), szName.get()); - m_pd->dblink = (DATABASELINK *)m_driverList.GetItemData(curSel); - - if (CreateProfile(m_pd->m_profile, m_pd->dblink) == 0) - SetWindowLongPtr(m_hwnd, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); - return true; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Profile selector - -static int numMessages[5]; - -static void stubAddMessage(int iType, const wchar_t *, ...) -{ - if (iType < 5) - numMessages[iType]++; -} - -class CChooseProfileDlg : public CDlgBase -{ - CCtrlButton &m_btnOk; - PROFILEMANAGERDATA *m_pd; - HANDLE m_hFileNotify; - - void DeleteProfile(const LVITEM &item) - { - CMStringW wszMessage(FORMAT, TranslateT("Are you sure you want to remove profile \"%s\"?"), item.pszText); - if (IDYES != MessageBoxW(nullptr, wszMessage, L"Miranda NG", MB_YESNO | MB_TASKMODAL | MB_ICONWARNING)) - return; - - wszMessage.Format(L"%s\\%s", g_profileDir, item.pszText); - DeleteDirectoryTreeW(wszMessage, true); - - m_profileList.DeleteItem(item.iItem); - } - - void CheckProfile(const wchar_t *profile) - { - CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile); - - if (TryLoadPlugin(plugin_checker, false)) - CallService(MS_DB_CHECKPROFILE, (WPARAM)wszFullName.c_str(), 0); - else - Plugin_Uninit(plugin_checker); - } - - void CompactProfile(DATABASELINK *dblink, const wchar_t *profile) - { - CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile); - - if (auto *db = dblink->Load(wszFullName, false)) { - db->Compact(); - delete db; - - MessageBoxW(nullptr, TranslateT("Database was compacted successfully"), L"Miranda NG", MB_OK | MB_ICONINFORMATION); - } - } - - void CheckRun() - { - m_btnOk.Enable(m_profileList.GetSelectedCount() == 1); - - wchar_t profile[MAX_PATH]; - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_IMAGE; - item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL); - item.pszText = profile; - item.cchTextMax = _countof(profile); - if (!m_profileList.GetItem(&item)) - return; - - switch(item.iImage) { - case 3: - m_btnOk.Enable(false); - return; - - case 2: - m_btnOk.SetText(TranslateT("&Convert")); - break; - - default: - m_btnOk.SetText(TranslateT("&Run")); - } - - // profile is placed in "profile_name" subfolder - - wchar_t tmpPath[MAX_PATH]; - mir_snwprintf(tmpPath, L"%s\\%s.dat", g_profileDir, profile); - if (_waccess(tmpPath, 2)) - m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, profile, profile); - else - m_pd->m_profile = tmpPath; - } - - void ExecuteMenu(LPARAM lParam) - { - LVHITTESTINFO lvht = { 0 }; - lvht.pt.x = GET_X_LPARAM(lParam); - lvht.pt.y = GET_Y_LPARAM(lParam); - ScreenToClient(m_profileList.GetHwnd(), &lvht.pt); - - if (m_profileList.HitTest(&lvht) == -1) - return; - - if (lvht.iItem == -1) - return; - - wchar_t profile[MAX_PATH]; - LVITEM item = { 0 }; - item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT; - item.iItem = lvht.iItem; - item.pszText = profile; - item.cchTextMax = _countof(profile); - if (!m_profileList.GetItem(&item)) - return; - - lvht.pt.x = GET_X_LPARAM(lParam); - lvht.pt.y = GET_Y_LPARAM(lParam); - - HMENU hMenu = CreatePopupMenu(); - if (item.iImage < 2) { - AppendMenu(hMenu, MF_STRING, 1, TranslateT("Run")); - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - } - - DATABASELINK *dblink = (DATABASELINK*)item.lParam; - if (dblink != nullptr) { - bool bAdded = false; - if (dblink->capabilities & MDB_CAPS_COMPACT) { - AppendMenu(hMenu, MF_STRING, 3, TranslateT("Compact database")); - bAdded = true; - } - - if (plugin_checker && (dblink->capabilities & MDB_CAPS_CHECK)) { - AppendMenu(hMenu, MF_STRING, 4, TranslateT("Check database")); - bAdded = true; - } - - if (bAdded) - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - } - - AppendMenu(hMenu, MF_STRING, 2, TranslateT("Delete")); - int index = TrackPopupMenu(hMenu, TPM_RETURNCMD, lvht.pt.x, lvht.pt.y, 0, m_hwnd, nullptr); - switch (index) { - case 1: - SendMessage(GetParent(m_hwndParent), WM_COMMAND, IDOK, 0); - break; - - case 2: - DeleteProfile(item); - break; - - case 3: - CompactProfile(dblink, profile); - break; - - case 4: - CheckProfile(profile); - break; - } - DestroyMenu(hMenu); - } - - CCtrlListView m_profileList; - -public: - CChooseProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) : - CDlgBase(g_plugin, IDD_PROFILE_SELECTION), - m_btnOk(_btn), - m_pd(_pd), - m_profileList(this, IDC_PROFILELIST) - { - m_profileList.OnItemChanged = Callback(this, &CChooseProfileDlg::list_OnItemChanged); - m_profileList.OnKeyDown = Callback(this, &CChooseProfileDlg::list_OnKeyDown); - m_profileList.OnGetInfoTip = Callback(this, &CChooseProfileDlg::list_OnGetTip); - m_profileList.OnDoubleClick = Callback(this, &CChooseProfileDlg::list_OnDblClick); - } - - bool OnInitDialog() override - { - // set columns - LVCOLUMN col; - col.mask = LVCF_TEXT | LVCF_WIDTH; - col.pszText = TranslateT("Profile"); - col.cx = 100; - m_profileList.InsertColumn(0, &col); - - col.pszText = TranslateT("Driver"); - col.cx = 150 - GetSystemMetrics(SM_CXVSCROLL); - m_profileList.InsertColumn(1, &col); - - col.pszText = TranslateT("Size"); - col.cx = 60; - m_profileList.InsertColumn(2, &col); - - // icons - HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 2, 1); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_USERDETAILS)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_DELETE)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MWARNING)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MFATAL)); - - // LV will destroy the image list - m_profileList.SetImageList(hImgList, LVSIL_SMALL); - m_profileList.SetExtendedListViewStyle(m_profileList.GetExtendedListViewStyle() | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT); - - // find all the profiles - findProfiles(m_profileList, m_pd->m_profile); - PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0); - - m_hFileNotify = FindFirstChangeNotification(g_profileDir, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE); - if (m_hFileNotify != INVALID_HANDLE_VALUE) - SetTimer(m_hwnd, 0, 1200, nullptr); - return true; - } - - void OnDestroy() - { - KillTimer(m_hwnd, 0); - FindCloseChangeNotification(m_hFileNotify); - } - - void list_OnItemChanged(CCtrlListView::TEventInfo*) - { - CheckRun(); - } - - void list_OnKeyDown(CCtrlListView::TEventInfo *evt) - { - if (evt->nmlvkey->wVKey == VK_DELETE) { - wchar_t profile[MAX_PATH]; - LVITEM item = { 0 }; - item.mask = LVIF_TEXT; - item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL); - item.pszText = profile; - item.cchTextMax = _countof(profile); - if (m_profileList.GetItem(&item)) - DeleteProfile(item); - } - } - - void list_OnGetTip(CCtrlListView::TEventInfo *evt) - { - if (auto pTip = evt->nmlvit) { - wchar_t profilename[MAX_PATH], tszFullPath[MAX_PATH]; - struct _stat statbuf; - m_profileList.GetItemText(pTip->iItem, 0, profilename, _countof(profilename)); - mir_snwprintf(tszFullPath, L"%s\\%s\\%s.dat", g_profileDir, profilename, profilename); - _wstat(tszFullPath, &statbuf); - mir_snwprintf(pTip->pszText, pTip->cchTextMax, L"%s\n%s: %s\n%s: %s", tszFullPath, TranslateT("Created"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_ctime))), TranslateT("Modified"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_mtime)))); - } - } - - void list_OnDblClick(CCtrlListView::TEventInfo*) - { - CheckRun(); - EndDialog(GetParent(m_hwndParent), 1); - } - - INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override - { - switch (msg) { - case WM_TIMER: - if (WaitForSingleObject(m_hFileNotify, 0) == WAIT_OBJECT_0) { - m_profileList.DeleteAllItems(); - findProfiles(m_profileList, m_pd->m_profile); - FindNextChangeNotification(m_hFileNotify); - } - break; - - case WM_FOCUSTEXTBOX: - SetFocus(m_profileList.GetHwnd()); - if (m_pd->m_profile.IsEmpty() || m_profileList.GetSelectedCount() == 0) - m_profileList.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); - break; - - case WM_SHOWWINDOW: - if (wParam) - CheckRun(); - break; - - case WM_CONTEXTMENU: - ExecuteMenu(lParam); - break; - } - - return CDlgBase::DlgProc(msg, wParam, lParam); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Tab manager + its envelope - -class CProfileManager : public CDlgBase -{ - PROFILEMANAGERDATA *m_pd; - - CCtrlPages m_tab; - CCtrlButton m_btnOk; - CCtrlCheck m_chkSmEnabled; - CCtrlCombo m_servicePlugs; - CCtrlBase m_warning; - -public: - CProfileManager(PROFILEMANAGERDATA *_pd) : - CDlgBase(g_plugin, IDD_PROFILEMANAGER), - m_btnOk(this, IDOK), - m_pd(_pd), - m_tab(this, IDC_TABS), - m_warning(this, IDC_SM_LABEL), - m_servicePlugs(this, IDC_SM_COMBO), - m_chkSmEnabled(this, IDC_SM_ENABLED) - { - m_chkSmEnabled.OnChange = Callback(this, &CProfileManager::onChanged); - - m_tab.AddPage(LPGENW("My profiles"), nullptr, new CChooseProfileDlg(m_btnOk, m_pd)); - m_tab.AddPage(LPGENW("New profile"), nullptr, new CCreateProfileDlg(m_btnOk, m_pd)); - } - - bool OnInitDialog() override - { - // MUST NOT be replaced with Window_SetIcon_IcoLib!!! - SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconSX, g_iIconSY, 0)); - SendMessage(m_hwnd, WM_SETICON, ICON_BIG, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconX, g_iIconY, 0)); - - if (m_pd->noProfiles || shouldAutoCreate(m_pd->m_profile)) - m_tab.ActivatePage(1); - - // service mode combobox - if (servicePlugins.getCount() == 0) { - ShowWindow(m_warning.GetHwnd(), FALSE); - ShowWindow(m_chkSmEnabled.GetHwnd(), FALSE); - ShowWindow(m_servicePlugs.GetHwnd(), FALSE); - } - else { - for (int i = 0; i < servicePlugins.getCount(); i++) { - pluginEntry *p = servicePlugins[i]; - m_servicePlugs.AddStringA(p->pluginname, i); - } - - m_servicePlugs.Disable(); - m_servicePlugs.SetCurSel(0); - } - return true; - } - - void OnDestroy() - { - if (m_chkSmEnabled.GetState()) { - int idx = m_servicePlugs.GetCurData(); - if (idx != -1) - plugin_service = servicePlugins[idx]; - } - - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); - } - - void onChanged(CCtrlCheck*) - { - m_servicePlugs.Enable(m_chkSmEnabled.GetState()); - } -}; - -int getProfileManager(PROFILEMANAGERDATA *pd) -{ - return CProfileManager(pd).DoModal(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "plugins.h"
+#include "langpack.h"
+#include "profilemanager.h"
+#include <sys/stat.h>
+
+#pragma warning(disable : 4512)
+
+#define WM_INPUTCHANGED (WM_USER + 0x3000)
+#define WM_FOCUSTEXTBOX (WM_USER + 0x3001)
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Profile creator
+
+static BOOL EnumProfilesForList(const wchar_t *tszFullPath, wchar_t *profile, CCtrlListView &list, const wchar_t *szProfile)
+{
+ wchar_t sizeBuf[64];
+ bool bFileLocked;
+
+ wchar_t *p = wcsrchr(profile, '.');
+ mir_wstrcpy(sizeBuf, L"0 KB");
+ if (p != nullptr) *p = 0;
+
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
+ item.pszText = profile;
+ item.iItem = 0;
+
+ struct _stat statbuf;
+ if (_wstat(tszFullPath, &statbuf) == 0) {
+ if (statbuf.st_size > 1000000) {
+ mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1048576.0);
+ mir_wstrcpy(sizeBuf + 5, L" MB");
+ }
+ else {
+ mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1024.0);
+ mir_wstrcpy(sizeBuf + 5, L" KB");
+ }
+ bFileLocked = Profile_CheckOpened(tszFullPath);
+ }
+ else bFileLocked = true;
+
+ DATABASELINK *dblink;
+ switch (touchDatabase(tszFullPath, &dblink)) {
+ case ERROR_SUCCESS:
+ item.iImage = (bFileLocked) ? 1 : 0;
+ break;
+
+ case EGROKPRF_OBSOLETE:
+ item.iImage = 2;
+ break;
+
+ case EGROKPRF_CANTREAD:
+ item.iImage = (bFileLocked) ? 1 : 3;
+ break;
+
+ default:
+ item.iImage = 3;
+ }
+
+ item.lParam = (LPARAM)dblink;
+
+ int iItem = list.InsertItem(&item);
+ if (mir_wstrcmpi(szProfile, tszFullPath) == 0)
+ list.SetItemState(iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
+
+ list.SetItemText(iItem, 2, sizeBuf);
+
+ if (dblink != nullptr)
+ list.SetItemText(iItem, 1, TranslateW(dblink->szFullName));
+ else if (bFileLocked) // file locked
+ list.SetItemText(iItem, 1, TranslateT("<In use>"));
+ else
+ list.SetItemText(iItem, 1, TranslateT("<Unknown format>"));
+
+ return TRUE;
+}
+
+static int findProfiles(CCtrlListView &list, const wchar_t *szProfile)
+{
+ // find in Miranda NG profile subfolders
+ MFilePath searchspec;
+ searchspec.Format(L"%s\\*.*", g_profileDir);
+
+ for (auto &it: searchspec.search()) {
+ // find all subfolders except "." and ".."
+ if (!it.isDir() || !wcscmp(it.getPath(), L".") || !wcscmp(it.getPath(), L".."))
+ continue;
+
+ MFilePath fullPath;
+ fullPath.Format(L"%s\\%s\\%s.dat", g_profileDir, it.getPath(), it.getPath());
+ if (fullPath.isExist()) {
+ wchar_t profileName[MAX_PATH];
+ mir_snwprintf(profileName, L"%s.dat", it.getPath());
+ if (!EnumProfilesForList(fullPath, profileName, list, szProfile))
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static LRESULT CALLBACK ProfileNameValidate(HWND edit, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_CHAR) {
+ if (wcschr(L".?/\\#' ", (wchar_t)wParam) != nullptr)
+ return 0;
+ PostMessage(GetParent(edit), WM_INPUTCHANGED, 0, 0);
+ }
+ return mir_callNextSubclass(edit, ProfileNameValidate, msg, wParam, lParam);
+}
+
+class CCreateProfileDlg : public CDlgBase
+{
+ CCtrlButton &m_btnOk;
+ PROFILEMANAGERDATA *m_pd;
+
+ int CreateProfile(const wchar_t *profile, DATABASELINK *link)
+ {
+ // check if the file already exists
+ const wchar_t *file = wcsrchr(profile, '\\');
+ if (file)
+ file++;
+
+ int err = 0;
+ wchar_t buf[256];
+
+ if (_waccess(profile, 0) == 0) {
+ // file already exists!
+ mir_snwprintf(buf,
+ TranslateT("The profile '%s' already exists. Do you want to move it to the Recycle Bin?\n\nWARNING: The profile will be deleted if Recycle Bin is disabled.\nWARNING: A profile may contain confidential information and should be properly deleted."),
+ file);
+ if (MessageBoxW(m_hwnd, buf, TranslateT("The profile already exists"), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES)
+ return 0;
+
+ // move the file
+ if (DeleteDirectoryTreeW(profile, true) != 0) {
+ mir_snwprintf(buf, TranslateT("Couldn't move '%s' to the Recycle Bin. Please select another profile name."), file);
+ MessageBoxW(m_hwnd, buf, TranslateT("Problem moving profile"), MB_ICONINFORMATION | MB_OK);
+ return 0;
+ }
+ // now the file should be gone!
+ }
+ // ask the database to create the profile
+ CreatePathToFileW(profile);
+ if ((err = link->makeDatabase(profile)) != ERROR_SUCCESS) {
+ mir_snwprintf(buf, TranslateT("Unable to create the profile '%s', the error was %x"), file, err);
+ MessageBoxW(m_hwnd, buf, TranslateT("Problem creating profile"), MB_ICONERROR | MB_OK);
+ return 0;
+ }
+
+ // the profile has been created!
+ g_bDbCreated = true;
+ return 1;
+ }
+
+ bool m_bFocused;
+ CCtrlCombo m_driverList;
+ CCtrlEdit m_profileName;
+ CCtrlBase m_warning;
+
+public:
+ CCreateProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) :
+ CDlgBase(g_plugin, IDD_PROFILE_NEW),
+ m_btnOk(_btn),
+ m_pd(_pd),
+ m_bFocused(false),
+ m_driverList(this, IDC_PROFILEDRIVERS),
+ m_profileName(this, IDC_PROFILENAME),
+ m_warning(this, IDC_NODBDRIVERS)
+ {}
+
+ bool OnInitDialog() override
+ {
+ // what, no plugins?!
+ if (arDbPlugins.getCount() == 0) {
+ m_driverList.Enable(false);
+ m_profileName.Enable(false);
+ ShowWindow(m_warning.GetHwnd(), TRUE);
+ }
+ else {
+ for (auto &p : arDbPlugins)
+ if (p->capabilities & MDB_CAPS_CREATE)
+ m_driverList.AddString(TranslateW(p->szFullName), (LPARAM)p);
+ }
+
+ // default item
+ m_driverList.SetCurSel(0);
+
+ // subclass the profile name box
+ mir_subclassWindow(m_profileName.GetHwnd(), ProfileNameValidate);
+
+ // decide if there is a default profile name given in the INI and if it should be used
+ if (m_pd->noProfiles || (shouldAutoCreate(m_pd->m_profile) && !m_pd->m_profile.isExist())) {
+ wchar_t *profile = wcsrchr(m_pd->m_profile.GetBuffer(), '\\');
+ if (profile) ++profile;
+ else profile = m_pd->m_profile.GetBuffer();
+
+ wchar_t *p = wcsrchr(profile, '.');
+ wchar_t c = 0;
+ if (p) { c = *p; *p = 0; }
+
+ m_profileName.SetText(profile);
+ if (c) *p = c;
+ }
+
+ // focus on the textbox
+ PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0);
+ return true;
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
+ {
+ switch (msg) {
+ case WM_FOCUSTEXTBOX:
+ SetFocus(m_profileName.GetHwnd());
+ break;
+
+ case WM_INPUTCHANGED: // when input in the edit box changes
+ NotifyChange();
+ m_btnOk.Enable(GetWindowTextLength(m_profileName.GetHwnd()) > 0);
+ break;
+
+ case WM_SHOWWINDOW:
+ if (wParam) {
+ m_btnOk.SetText(TranslateT("&Create"));
+ SendMessage(m_hwnd, WM_INPUTCHANGED, 0, 0);
+ m_bFocused = true;
+ }
+ else m_bFocused = false;
+ break;
+ }
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+ }
+
+ bool OnApply() override
+ {
+ LRESULT curSel = m_driverList.GetCurSel();
+ if (curSel == -1 || !m_bFocused)
+ return false; // should never happen
+
+ ptrW szName(m_profileName.GetText());
+ if (mir_wstrlen(szName) == 0)
+ return false;
+
+ // profile placed in "profile_name" subfolder
+ m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, szName.get(), szName.get());
+ m_pd->dblink = (DATABASELINK *)m_driverList.GetItemData(curSel);
+
+ if (CreateProfile(m_pd->m_profile, m_pd->dblink) == 0)
+ SetWindowLongPtr(m_hwnd, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
+ return true;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Profile selector
+
+static int numMessages[5];
+
+static void stubAddMessage(int iType, const wchar_t *, ...)
+{
+ if (iType < 5)
+ numMessages[iType]++;
+}
+
+class CChooseProfileDlg : public CDlgBase
+{
+ CCtrlButton &m_btnOk;
+ PROFILEMANAGERDATA *m_pd;
+ HANDLE m_hFileNotify;
+
+ void DeleteProfile(const LVITEM &item)
+ {
+ CMStringW wszMessage(FORMAT, TranslateT("Are you sure you want to remove profile \"%s\"?"), item.pszText);
+ if (IDYES != MessageBoxW(nullptr, wszMessage, L"Miranda NG", MB_YESNO | MB_TASKMODAL | MB_ICONWARNING))
+ return;
+
+ wszMessage.Format(L"%s\\%s", g_profileDir, item.pszText);
+ DeleteDirectoryTreeW(wszMessage, true);
+
+ m_profileList.DeleteItem(item.iItem);
+ }
+
+ void CheckProfile(const wchar_t *profile)
+ {
+ CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile);
+
+ if (TryLoadPlugin(plugin_checker, false))
+ CallService(MS_DB_CHECKPROFILE, (WPARAM)wszFullName.c_str(), 0);
+ else
+ Plugin_Uninit(plugin_checker);
+ }
+
+ void CompactProfile(DATABASELINK *dblink, const wchar_t *profile)
+ {
+ CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile);
+
+ if (auto *db = dblink->Load(wszFullName, false)) {
+ db->Compact();
+ delete db;
+
+ MessageBoxW(nullptr, TranslateT("Database was compacted successfully"), L"Miranda NG", MB_OK | MB_ICONINFORMATION);
+ }
+ }
+
+ void CheckRun()
+ {
+ m_btnOk.Enable(m_profileList.GetSelectedCount() == 1);
+
+ wchar_t profile[MAX_PATH];
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT | LVIF_IMAGE;
+ item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL);
+ item.pszText = profile;
+ item.cchTextMax = _countof(profile);
+ if (!m_profileList.GetItem(&item))
+ return;
+
+ switch(item.iImage) {
+ case 3:
+ m_btnOk.Enable(false);
+ return;
+
+ case 2:
+ m_btnOk.SetText(TranslateT("&Convert"));
+ break;
+
+ default:
+ m_btnOk.SetText(TranslateT("&Run"));
+ }
+
+ // profile is placed in "profile_name" subfolder
+
+ wchar_t tmpPath[MAX_PATH];
+ mir_snwprintf(tmpPath, L"%s\\%s.dat", g_profileDir, profile);
+ if (_waccess(tmpPath, 2))
+ m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, profile, profile);
+ else
+ m_pd->m_profile = tmpPath;
+ }
+
+ void ExecuteMenu(LPARAM lParam)
+ {
+ LVHITTESTINFO lvht = { 0 };
+ lvht.pt.x = GET_X_LPARAM(lParam);
+ lvht.pt.y = GET_Y_LPARAM(lParam);
+ ScreenToClient(m_profileList.GetHwnd(), &lvht.pt);
+
+ if (m_profileList.HitTest(&lvht) == -1)
+ return;
+
+ if (lvht.iItem == -1)
+ return;
+
+ wchar_t profile[MAX_PATH];
+ LVITEM item = { 0 };
+ item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT;
+ item.iItem = lvht.iItem;
+ item.pszText = profile;
+ item.cchTextMax = _countof(profile);
+ if (!m_profileList.GetItem(&item))
+ return;
+
+ lvht.pt.x = GET_X_LPARAM(lParam);
+ lvht.pt.y = GET_Y_LPARAM(lParam);
+
+ HMENU hMenu = CreatePopupMenu();
+ if (item.iImage < 2) {
+ AppendMenu(hMenu, MF_STRING, 1, TranslateT("Run"));
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ }
+
+ DATABASELINK *dblink = (DATABASELINK*)item.lParam;
+ if (dblink != nullptr) {
+ bool bAdded = false;
+ if (dblink->capabilities & MDB_CAPS_COMPACT) {
+ AppendMenu(hMenu, MF_STRING, 3, TranslateT("Compact database"));
+ bAdded = true;
+ }
+
+ if (plugin_checker && (dblink->capabilities & MDB_CAPS_CHECK)) {
+ AppendMenu(hMenu, MF_STRING, 4, TranslateT("Check database"));
+ bAdded = true;
+ }
+
+ if (bAdded)
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ }
+
+ AppendMenu(hMenu, MF_STRING, 2, TranslateT("Delete"));
+ int index = TrackPopupMenu(hMenu, TPM_RETURNCMD, lvht.pt.x, lvht.pt.y, 0, m_hwnd, nullptr);
+ switch (index) {
+ case 1:
+ SendMessage(GetParent(m_hwndParent), WM_COMMAND, IDOK, 0);
+ break;
+
+ case 2:
+ DeleteProfile(item);
+ break;
+
+ case 3:
+ CompactProfile(dblink, profile);
+ break;
+
+ case 4:
+ CheckProfile(profile);
+ break;
+ }
+ DestroyMenu(hMenu);
+ }
+
+ CCtrlListView m_profileList;
+
+public:
+ CChooseProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) :
+ CDlgBase(g_plugin, IDD_PROFILE_SELECTION),
+ m_btnOk(_btn),
+ m_pd(_pd),
+ m_profileList(this, IDC_PROFILELIST)
+ {
+ m_profileList.OnItemChanged = Callback(this, &CChooseProfileDlg::list_OnItemChanged);
+ m_profileList.OnKeyDown = Callback(this, &CChooseProfileDlg::list_OnKeyDown);
+ m_profileList.OnGetInfoTip = Callback(this, &CChooseProfileDlg::list_OnGetTip);
+ m_profileList.OnDoubleClick = Callback(this, &CChooseProfileDlg::list_OnDblClick);
+ }
+
+ bool OnInitDialog() override
+ {
+ // set columns
+ LVCOLUMN col;
+ col.mask = LVCF_TEXT | LVCF_WIDTH;
+ col.pszText = TranslateT("Profile");
+ col.cx = 100;
+ m_profileList.InsertColumn(0, &col);
+
+ col.pszText = TranslateT("Driver");
+ col.cx = 150 - GetSystemMetrics(SM_CXVSCROLL);
+ m_profileList.InsertColumn(1, &col);
+
+ col.pszText = TranslateT("Size");
+ col.cx = 60;
+ m_profileList.InsertColumn(2, &col);
+
+ // icons
+ HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 2, 1);
+ ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_USERDETAILS));
+ ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_DELETE));
+ ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MWARNING));
+ ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MFATAL));
+
+ // LV will destroy the image list
+ m_profileList.SetImageList(hImgList, LVSIL_SMALL);
+ m_profileList.SetExtendedListViewStyle(m_profileList.GetExtendedListViewStyle() | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT);
+
+ // find all the profiles
+ findProfiles(m_profileList, m_pd->m_profile);
+ PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0);
+
+ m_hFileNotify = FindFirstChangeNotification(g_profileDir, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE);
+ if (m_hFileNotify != INVALID_HANDLE_VALUE)
+ SetTimer(m_hwnd, 0, 1200, nullptr);
+ return true;
+ }
+
+ void OnDestroy()
+ {
+ KillTimer(m_hwnd, 0);
+ FindCloseChangeNotification(m_hFileNotify);
+ }
+
+ void list_OnItemChanged(CCtrlListView::TEventInfo*)
+ {
+ CheckRun();
+ }
+
+ void list_OnKeyDown(CCtrlListView::TEventInfo *evt)
+ {
+ if (evt->nmlvkey->wVKey == VK_DELETE) {
+ wchar_t profile[MAX_PATH];
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT;
+ item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL);
+ item.pszText = profile;
+ item.cchTextMax = _countof(profile);
+ if (m_profileList.GetItem(&item))
+ DeleteProfile(item);
+ }
+ }
+
+ void list_OnGetTip(CCtrlListView::TEventInfo *evt)
+ {
+ if (auto pTip = evt->nmlvit) {
+ wchar_t profilename[MAX_PATH], tszFullPath[MAX_PATH];
+ struct _stat statbuf;
+ m_profileList.GetItemText(pTip->iItem, 0, profilename, _countof(profilename));
+ mir_snwprintf(tszFullPath, L"%s\\%s\\%s.dat", g_profileDir, profilename, profilename);
+ _wstat(tszFullPath, &statbuf);
+ mir_snwprintf(pTip->pszText, pTip->cchTextMax, L"%s\n%s: %s\n%s: %s", tszFullPath, TranslateT("Created"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_ctime))), TranslateT("Modified"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_mtime))));
+ }
+ }
+
+ void list_OnDblClick(CCtrlListView::TEventInfo*)
+ {
+ CheckRun();
+ EndDialog(GetParent(m_hwndParent), 1);
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
+ {
+ switch (msg) {
+ case WM_TIMER:
+ if (WaitForSingleObject(m_hFileNotify, 0) == WAIT_OBJECT_0) {
+ m_profileList.DeleteAllItems();
+ findProfiles(m_profileList, m_pd->m_profile);
+ FindNextChangeNotification(m_hFileNotify);
+ }
+ break;
+
+ case WM_FOCUSTEXTBOX:
+ SetFocus(m_profileList.GetHwnd());
+ if (m_pd->m_profile.IsEmpty() || m_profileList.GetSelectedCount() == 0)
+ m_profileList.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
+ break;
+
+ case WM_SHOWWINDOW:
+ if (wParam)
+ CheckRun();
+ break;
+
+ case WM_CONTEXTMENU:
+ ExecuteMenu(lParam);
+ break;
+ }
+
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Tab manager + its envelope
+
+class CProfileManager : public CDlgBase
+{
+ PROFILEMANAGERDATA *m_pd;
+
+ CCtrlPages m_tab;
+ CCtrlButton m_btnOk;
+ CCtrlCheck m_chkSmEnabled;
+ CCtrlCombo m_servicePlugs;
+ CCtrlBase m_warning;
+
+public:
+ CProfileManager(PROFILEMANAGERDATA *_pd) :
+ CDlgBase(g_plugin, IDD_PROFILEMANAGER),
+ m_btnOk(this, IDOK),
+ m_pd(_pd),
+ m_tab(this, IDC_TABS),
+ m_warning(this, IDC_SM_LABEL),
+ m_servicePlugs(this, IDC_SM_COMBO),
+ m_chkSmEnabled(this, IDC_SM_ENABLED)
+ {
+ m_chkSmEnabled.OnChange = Callback(this, &CProfileManager::onChanged);
+
+ m_tab.AddPage(LPGENW("My profiles"), nullptr, new CChooseProfileDlg(m_btnOk, m_pd));
+ m_tab.AddPage(LPGENW("New profile"), nullptr, new CCreateProfileDlg(m_btnOk, m_pd));
+ }
+
+ bool OnInitDialog() override
+ {
+ // MUST NOT be replaced with Window_SetIcon_IcoLib!!!
+ SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconSX, g_iIconSY, 0));
+ SendMessage(m_hwnd, WM_SETICON, ICON_BIG, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconX, g_iIconY, 0));
+
+ if (m_pd->noProfiles || shouldAutoCreate(m_pd->m_profile))
+ m_tab.ActivatePage(1);
+
+ // service mode combobox
+ if (servicePlugins.getCount() == 0) {
+ ShowWindow(m_warning.GetHwnd(), FALSE);
+ ShowWindow(m_chkSmEnabled.GetHwnd(), FALSE);
+ ShowWindow(m_servicePlugs.GetHwnd(), FALSE);
+ }
+ else {
+ for (int i = 0; i < servicePlugins.getCount(); i++) {
+ pluginEntry *p = servicePlugins[i];
+ m_servicePlugs.AddStringA(p->pluginname, i);
+ }
+
+ m_servicePlugs.Disable();
+ m_servicePlugs.SetCurSel(0);
+ }
+ return true;
+ }
+
+ void OnDestroy()
+ {
+ if (m_chkSmEnabled.GetState()) {
+ int idx = m_servicePlugs.GetCurData();
+ if (idx != -1)
+ plugin_service = servicePlugins[idx];
+ }
+
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0));
+ DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0));
+ }
+
+ void onChanged(CCtrlCheck*)
+ {
+ m_servicePlugs.Enable(m_chkSmEnabled.GetState());
+ }
+};
+
+int getProfileManager(PROFILEMANAGERDATA *pd)
+{
+ return CProfileManager(pd).DoModal();
+}
diff --git a/src/mir_app/src/profilemanager.h b/src/mir_app/src/profilemanager.h index 39685e82bc..ab7e9e8a14 100644 --- a/src/mir_app/src/profilemanager.h +++ b/src/mir_app/src/profilemanager.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_accs.cpp b/src/mir_app/src/proto_accs.cpp index 8947568395..501497361c 100644 --- a/src/mir_app/src/proto_accs.cpp +++ b/src/mir_app/src/proto_accs.cpp @@ -1,435 +1,435 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "clc.h" - -bool CheckProtocolOrder(void); -void BuildProtoMenus(); - -HICON Proto_GetIcon(PROTO_INTERFACE *ppro, int iconIndex); - -static bool bModuleInitialized = false; -static HANDLE hHooks[4]; - -static int CompareAccounts(const PROTOACCOUNT* p1, const PROTOACCOUNT* p2) -{ - return mir_strcmp(p1->szModuleName, p2->szModuleName); -} - -LIST<PROTOACCOUNT> g_arAccounts(10, CompareAccounts); - -///////////////////////////////////////////////////////////////////////////////////////// - -static int EnumDbModules(const char *szModuleName, void*) -{ - ptrA szProtoName(db_get_sa(0, szModuleName, "AM_BaseProto")); - if (szProtoName) { - if (!Proto_GetAccount(szModuleName)) { - PROTOACCOUNT *pa = new PROTOACCOUNT(szModuleName); - pa->szProtoName = szProtoName.detach(); - pa->tszAccountName = mir_a2u(szModuleName); - pa->bIsVisible = true; - pa->bIsEnabled = false; - pa->iOrder = g_arAccounts.getCount(); - g_arAccounts.insert(pa); - } - } - return 0; -} - -void LoadDbAccounts(void) -{ - int ver = db_get_dw(0, "Protocols", "PrVer", -1); - int count = db_get_dw(0, "Protocols", "ProtoCount", 0); - - for (int i = 0; i < count; i++) { - char buf[10]; - _itoa(i, buf, 10); - ptrA szModuleName(db_get_sa(0, "Protocols", buf)); - if (szModuleName == nullptr) - continue; - - PROTOACCOUNT *pa = Proto_GetAccount(szModuleName); - if (pa == nullptr) { - pa = new PROTOACCOUNT(szModuleName); - g_arAccounts.insert(pa); - } - - _itoa(OFFSET_VISIBLE + i, buf, 10); - pa->bIsVisible = db_get_dw(0, "Protocols", buf, 1) != 0; - - _itoa(OFFSET_PROTOPOS + i, buf, 10); - pa->iOrder = db_get_dw(0, "Protocols", buf, 1); - - if (ver >= 4) { - _itoa(OFFSET_NAME + i, buf, 10); - pa->tszAccountName = db_get_wsa(0, "Protocols", buf); - - _itoa(OFFSET_ENABLED + i, buf, 10); - pa->bIsEnabled = db_get_dw(0, "Protocols", buf, 1) != 0; - if (!pa->bIsEnabled && !mir_strcmp(pa->szModuleName, META_PROTO)) { - pa->bIsEnabled = true; - db_set_dw(0, "Protocols", buf, 1); - } - pa->szProtoName = db_get_sa(0, szModuleName, "AM_BaseProto"); - } - else pa->bIsEnabled = true; - - if (!pa->szProtoName) { - pa->szProtoName = mir_strdup(szModuleName); - db_set_s(0, szModuleName, "AM_BaseProto", pa->szProtoName); - } - - if (!pa->tszAccountName) - pa->tszAccountName = mir_a2u(szModuleName); - } - - if (CheckProtocolOrder()) - WriteDbAccounts(); - - int anum = g_arAccounts.getCount(); - db_enum_modules(EnumDbModules); - if (anum != g_arAccounts.getCount()) - WriteDbAccounts(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WriteDbAccounts() -{ - // enum all old settings to delete - db_delete_module(0, "Protocols"); - - // write new data - for (int i = 0; i < g_arAccounts.getCount(); i++) { - PROTOACCOUNT *pa = g_arAccounts[i]; - - char buf[20]; - _itoa(i, buf, 10); - db_set_s(0, "Protocols", buf, pa->szModuleName); - - _itoa(OFFSET_PROTOPOS + i, buf, 10); - db_set_dw(0, "Protocols", buf, pa->iOrder); - - _itoa(OFFSET_VISIBLE + i, buf, 10); - db_set_dw(0, "Protocols", buf, pa->bIsVisible); - - _itoa(OFFSET_ENABLED + i, buf, 10); - db_set_dw(0, "Protocols", buf, pa->bIsEnabled); - - _itoa(OFFSET_NAME + i, buf, 10); - db_set_ws(0, "Protocols", buf, pa->tszAccountName); - } - - db_set_dw(0, "Protocols", "ProtoCount", g_arAccounts.getCount()); - db_set_dw(0, "Protocols", "PrVer", 4); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int OnContactDeleted(WPARAM hContact, LPARAM) -{ - if (auto *ppro = Proto_GetInstance(hContact)) - ppro->OnContactDeleted(hContact); - return 0; -} - -static int OnEventEdited(WPARAM hContact, LPARAM hDbEvent) -{ - if (auto *ppro = Proto_GetInstance(hContact)) - ppro->OnEventEdited(hContact, hDbEvent); - return 0; -} - -void InitStaticAccounts() -{ - int count = 0; - - for (auto &pa : g_arAccounts) { - if (!pa->ppro || !pa->IsEnabled()) - continue; - - pa->ppro->OnModulesLoaded(); - - if (!pa->bOldProto) - count++; - - if (pa->IsVisible()) - pa->ppro->OnBuildProtoMenu(); - } - - if (count == 0 && !db_get_b(0, "FirstRun", "AccManager", 0)) { - db_set_b(0, "FirstRun", "AccManager", 1); - CallService(MS_PROTO_SHOWACCMGR, 0, 0); - } - // This is for pack creators with a profile with predefined g_arAccounts - else if (db_get_b(0, "FirstRun", "ForceShowAccManager", 0)) { - CallService(MS_PROTO_SHOWACCMGR, 0, 0); - db_unset(0, "FirstRun", "ForceShowAccManager"); - } -} - -static int UninitializeStaticAccounts(WPARAM, LPARAM) -{ - // request permission to exit first - for (auto &pa : g_arAccounts) - if (pa->ppro && pa->IsEnabled()) - if (!pa->ppro->IsReadyToExit()) - return 1; - - // okay, all protocols are ready, exiting - for (auto &pa : g_arAccounts) - if (pa->ppro && pa->IsEnabled()) - pa->ppro->OnShutdown(); - - return 0; -} - -int LoadAccountsModule(void) -{ - bModuleInitialized = true; - - for (auto &pa : g_arAccounts) { - pa->bDynDisabled = !Proto_IsProtocolLoaded(pa->szProtoName); - if (pa->ppro) - continue; - - if (!pa->IsEnabled()) - continue; - - if (!ActivateAccount(pa, false)) - pa->bDynDisabled = true; - } - - hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, UninitializeStaticAccounts); - hHooks[2] = HookEvent(ME_DB_CONTACT_DELETED, OnContactDeleted); - hHooks[3] = HookEvent(ME_DB_EVENT_EDITED, OnEventEdited); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static HANDLE CreateProtoServiceEx(const char* szModule, const char* szService, MIRANDASERVICEOBJ pFunc, void* param) -{ - char tmp[100]; - mir_snprintf(tmp, "%s%s", szModule, szService); - return CreateServiceFunctionObj(tmp, pFunc, param); -} - -bool ActivateAccount(PROTOACCOUNT *pa, bool bIsDynamic) -{ - MBaseProto *ppd = Proto_GetProto(pa->szProtoName); - if (ppd == nullptr) - return false; - - if (ppd->fnInit == nullptr) - return false; - - PROTO_INTERFACE *ppi = pa->ppro; - if (ppi == nullptr) { - ppi = ppd->fnInit(pa->szModuleName, pa->tszAccountName); - if (ppi == nullptr) - return false; - - pa->ppro = ppi; - - if (bIsDynamic) { - if (g_bModulesLoadedFired) - pa->ppro->OnModulesLoaded(); - if (!db_get_b(0, "CList", "MoveProtoMenus", true)) - pa->ppro->OnBuildProtoMenu(); - pa->bDynDisabled = false; - } - } - - if (ppi->m_hProtoIcon == nullptr) - ppi->m_hProtoIcon = IcoLib_IsManaged(Skin_LoadProtoIcon(pa->szModuleName, ID_STATUS_ONLINE)); - ppi->m_iDesiredStatus = ppi->m_iStatus = ID_STATUS_OFFLINE; - return true; -} - -MIR_APP_DLL(int) Proto_GetAverageStatus(int *pAccountNumber) -{ - int netProtoCount = 0, averageMode = 0; - - for (auto &pa : g_arAccounts) { - if (!pa->IsVisible() || pa->IsLocked()) - continue; - - netProtoCount++; - if (averageMode == 0) - averageMode = pa->iRealStatus; - else if (averageMode > 0 && averageMode != pa->iRealStatus) { - averageMode = -1; - if (pAccountNumber == nullptr) - break; - } - } - - if (pAccountNumber) - *pAccountNumber = netProtoCount; - return averageMode; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct DeactivationThreadParam -{ - PROTO_INTERFACE *ppro; - pfnUninitProto fnUninit; - int flags; -}; - -pfnUninitProto GetProtocolDestructor(char *szProto); - -static void __cdecl DeactivationThread(DeactivationThreadParam *param) -{ - PROTO_INTERFACE *p = (PROTO_INTERFACE*)param->ppro; - p->SetStatus(ID_STATUS_OFFLINE); - - char *szModuleName = NEWSTR_ALLOCA(p->m_szModuleName); - - if (param->flags & DAF_DYNAMIC) { - while (!p->IsReadyToExit()) - SleepEx(100, TRUE); - - p->OnShutdown(); - } - - KillObjectThreads(p); // waits for them before terminating - KillObjectEventHooks(p); // untie an object from the outside world - - if (param->flags & DAF_ERASE) - p->OnErase(); - - if (param->fnUninit) - param->fnUninit(p); - - KillObjectServices(p); - - if (param->flags & DAF_ERASE) - EraseAccount(szModuleName); - - delete param; -} - -void DeactivateAccount(PROTOACCOUNT *pa, int flags) -{ - if (pa->hwndAccMgrUI) { - DestroyWindow(pa->hwndAccMgrUI); - pa->hwndAccMgrUI = nullptr; - pa->bAccMgrUIChanged = FALSE; - } - - if (flags & DAF_DYNAMIC) - NotifyEventHooks(hAccListChanged, PRAC_REMOVED, (LPARAM)pa); - else - pa->iIconBase = -1; - - if (pa->ppro == nullptr) { - if (flags & DAF_ERASE) - EraseAccount(pa->szModuleName); - return; - } - - DeactivationThreadParam *param = new DeactivationThreadParam; - param->ppro = pa->ppro; - param->fnUninit = GetProtocolDestructor(pa->szProtoName); - param->flags = flags; - pa->ppro = nullptr; - if (flags & DAF_FORK) - mir_forkThread<DeactivationThreadParam>(DeactivationThread, param); - else - DeactivationThread(param); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void KillModuleAccounts(HINSTANCE hInst) -{ - for (auto &pd : g_arProtos.rev_iter()) { - if (pd->hInst != hInst) - continue; - - for (auto &pa : g_arAccounts.rev_iter()) { - if (!mir_strcmp(pa->szProtoName, pd->szName)) { - pa->bDynDisabled = true; - DeactivateAccount(pa, DAF_DYNAMIC); - } - } - - g_arProtos.removeItem(&pd); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void EraseAccount(const char *pszModuleName) -{ - // remove protocol contacts first - for (MCONTACT hContact = db_find_first(pszModuleName); hContact != 0;) { - MCONTACT hNext = db_find_next(hContact, pszModuleName); - db_delete_contact(hContact); - hContact = hNext; - } - - // remove all protocol settings - db_delete_module(0, pszModuleName); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void UnloadAccount(PROTOACCOUNT *pa, int flags) -{ - DeactivateAccount(pa, flags); - - // szModuleName should be freed only on a program's exit. - // otherwise many plugins dependand on static protocol names will crash! - // do NOT fix this 'leak', please - if (!(flags & DAF_DYNAMIC)) - delete pa; - else { - replaceStrW(pa->tszAccountName, 0); - replaceStr(pa->szProtoName, 0); - replaceStr(pa->szUniqueId, 0); - } -} - -void UnloadAccountsModule() -{ - if (!bModuleInitialized) - return; - - auto T = g_arAccounts.rev_iter(); - for (auto &it : T) { - UnloadAccount(it, 0); - g_arAccounts.removeItem(&it); - } - g_arAccounts.destroy(); - - for (auto &it : hHooks) - UnhookEvent(it); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "clc.h"
+
+bool CheckProtocolOrder(void);
+void BuildProtoMenus();
+
+HICON Proto_GetIcon(PROTO_INTERFACE *ppro, int iconIndex);
+
+static bool bModuleInitialized = false;
+static HANDLE hHooks[4];
+
+static int CompareAccounts(const PROTOACCOUNT* p1, const PROTOACCOUNT* p2)
+{
+ return mir_strcmp(p1->szModuleName, p2->szModuleName);
+}
+
+LIST<PROTOACCOUNT> g_arAccounts(10, CompareAccounts);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int EnumDbModules(const char *szModuleName, void*)
+{
+ ptrA szProtoName(db_get_sa(0, szModuleName, "AM_BaseProto"));
+ if (szProtoName) {
+ if (!Proto_GetAccount(szModuleName)) {
+ PROTOACCOUNT *pa = new PROTOACCOUNT(szModuleName);
+ pa->szProtoName = szProtoName.detach();
+ pa->tszAccountName = mir_a2u(szModuleName);
+ pa->bIsVisible = true;
+ pa->bIsEnabled = false;
+ pa->iOrder = g_arAccounts.getCount();
+ g_arAccounts.insert(pa);
+ }
+ }
+ return 0;
+}
+
+void LoadDbAccounts(void)
+{
+ int ver = db_get_dw(0, "Protocols", "PrVer", -1);
+ int count = db_get_dw(0, "Protocols", "ProtoCount", 0);
+
+ for (int i = 0; i < count; i++) {
+ char buf[10];
+ _itoa(i, buf, 10);
+ ptrA szModuleName(db_get_sa(0, "Protocols", buf));
+ if (szModuleName == nullptr)
+ continue;
+
+ PROTOACCOUNT *pa = Proto_GetAccount(szModuleName);
+ if (pa == nullptr) {
+ pa = new PROTOACCOUNT(szModuleName);
+ g_arAccounts.insert(pa);
+ }
+
+ _itoa(OFFSET_VISIBLE + i, buf, 10);
+ pa->bIsVisible = db_get_dw(0, "Protocols", buf, 1) != 0;
+
+ _itoa(OFFSET_PROTOPOS + i, buf, 10);
+ pa->iOrder = db_get_dw(0, "Protocols", buf, 1);
+
+ if (ver >= 4) {
+ _itoa(OFFSET_NAME + i, buf, 10);
+ pa->tszAccountName = db_get_wsa(0, "Protocols", buf);
+
+ _itoa(OFFSET_ENABLED + i, buf, 10);
+ pa->bIsEnabled = db_get_dw(0, "Protocols", buf, 1) != 0;
+ if (!pa->bIsEnabled && !mir_strcmp(pa->szModuleName, META_PROTO)) {
+ pa->bIsEnabled = true;
+ db_set_dw(0, "Protocols", buf, 1);
+ }
+ pa->szProtoName = db_get_sa(0, szModuleName, "AM_BaseProto");
+ }
+ else pa->bIsEnabled = true;
+
+ if (!pa->szProtoName) {
+ pa->szProtoName = mir_strdup(szModuleName);
+ db_set_s(0, szModuleName, "AM_BaseProto", pa->szProtoName);
+ }
+
+ if (!pa->tszAccountName)
+ pa->tszAccountName = mir_a2u(szModuleName);
+ }
+
+ if (CheckProtocolOrder())
+ WriteDbAccounts();
+
+ int anum = g_arAccounts.getCount();
+ db_enum_modules(EnumDbModules);
+ if (anum != g_arAccounts.getCount())
+ WriteDbAccounts();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WriteDbAccounts()
+{
+ // enum all old settings to delete
+ db_delete_module(0, "Protocols");
+
+ // write new data
+ for (int i = 0; i < g_arAccounts.getCount(); i++) {
+ PROTOACCOUNT *pa = g_arAccounts[i];
+
+ char buf[20];
+ _itoa(i, buf, 10);
+ db_set_s(0, "Protocols", buf, pa->szModuleName);
+
+ _itoa(OFFSET_PROTOPOS + i, buf, 10);
+ db_set_dw(0, "Protocols", buf, pa->iOrder);
+
+ _itoa(OFFSET_VISIBLE + i, buf, 10);
+ db_set_dw(0, "Protocols", buf, pa->bIsVisible);
+
+ _itoa(OFFSET_ENABLED + i, buf, 10);
+ db_set_dw(0, "Protocols", buf, pa->bIsEnabled);
+
+ _itoa(OFFSET_NAME + i, buf, 10);
+ db_set_ws(0, "Protocols", buf, pa->tszAccountName);
+ }
+
+ db_set_dw(0, "Protocols", "ProtoCount", g_arAccounts.getCount());
+ db_set_dw(0, "Protocols", "PrVer", 4);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int OnContactDeleted(WPARAM hContact, LPARAM)
+{
+ if (auto *ppro = Proto_GetInstance(hContact))
+ ppro->OnContactDeleted(hContact);
+ return 0;
+}
+
+static int OnEventEdited(WPARAM hContact, LPARAM hDbEvent)
+{
+ if (auto *ppro = Proto_GetInstance(hContact))
+ ppro->OnEventEdited(hContact, hDbEvent);
+ return 0;
+}
+
+void InitStaticAccounts()
+{
+ int count = 0;
+
+ for (auto &pa : g_arAccounts) {
+ if (!pa->ppro || !pa->IsEnabled())
+ continue;
+
+ pa->ppro->OnModulesLoaded();
+
+ if (!pa->bOldProto)
+ count++;
+
+ if (pa->IsVisible())
+ pa->ppro->OnBuildProtoMenu();
+ }
+
+ if (count == 0 && !db_get_b(0, "FirstRun", "AccManager", 0)) {
+ db_set_b(0, "FirstRun", "AccManager", 1);
+ CallService(MS_PROTO_SHOWACCMGR, 0, 0);
+ }
+ // This is for pack creators with a profile with predefined g_arAccounts
+ else if (db_get_b(0, "FirstRun", "ForceShowAccManager", 0)) {
+ CallService(MS_PROTO_SHOWACCMGR, 0, 0);
+ db_unset(0, "FirstRun", "ForceShowAccManager");
+ }
+}
+
+static int UninitializeStaticAccounts(WPARAM, LPARAM)
+{
+ // request permission to exit first
+ for (auto &pa : g_arAccounts)
+ if (pa->ppro && pa->IsEnabled())
+ if (!pa->ppro->IsReadyToExit())
+ return 1;
+
+ // okay, all protocols are ready, exiting
+ for (auto &pa : g_arAccounts)
+ if (pa->ppro && pa->IsEnabled())
+ pa->ppro->OnShutdown();
+
+ return 0;
+}
+
+int LoadAccountsModule(void)
+{
+ bModuleInitialized = true;
+
+ for (auto &pa : g_arAccounts) {
+ pa->bDynDisabled = !Proto_IsProtocolLoaded(pa->szProtoName);
+ if (pa->ppro)
+ continue;
+
+ if (!pa->IsEnabled())
+ continue;
+
+ if (!ActivateAccount(pa, false))
+ pa->bDynDisabled = true;
+ }
+
+ hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, UninitializeStaticAccounts);
+ hHooks[2] = HookEvent(ME_DB_CONTACT_DELETED, OnContactDeleted);
+ hHooks[3] = HookEvent(ME_DB_EVENT_EDITED, OnEventEdited);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static HANDLE CreateProtoServiceEx(const char* szModule, const char* szService, MIRANDASERVICEOBJ pFunc, void* param)
+{
+ char tmp[100];
+ mir_snprintf(tmp, "%s%s", szModule, szService);
+ return CreateServiceFunctionObj(tmp, pFunc, param);
+}
+
+bool ActivateAccount(PROTOACCOUNT *pa, bool bIsDynamic)
+{
+ MBaseProto *ppd = Proto_GetProto(pa->szProtoName);
+ if (ppd == nullptr)
+ return false;
+
+ if (ppd->fnInit == nullptr)
+ return false;
+
+ PROTO_INTERFACE *ppi = pa->ppro;
+ if (ppi == nullptr) {
+ ppi = ppd->fnInit(pa->szModuleName, pa->tszAccountName);
+ if (ppi == nullptr)
+ return false;
+
+ pa->ppro = ppi;
+
+ if (bIsDynamic) {
+ if (g_bModulesLoadedFired)
+ pa->ppro->OnModulesLoaded();
+ if (!db_get_b(0, "CList", "MoveProtoMenus", true))
+ pa->ppro->OnBuildProtoMenu();
+ pa->bDynDisabled = false;
+ }
+ }
+
+ if (ppi->m_hProtoIcon == nullptr)
+ ppi->m_hProtoIcon = IcoLib_IsManaged(Skin_LoadProtoIcon(pa->szModuleName, ID_STATUS_ONLINE));
+ ppi->m_iDesiredStatus = ppi->m_iStatus = ID_STATUS_OFFLINE;
+ return true;
+}
+
+MIR_APP_DLL(int) Proto_GetAverageStatus(int *pAccountNumber)
+{
+ int netProtoCount = 0, averageMode = 0;
+
+ for (auto &pa : g_arAccounts) {
+ if (!pa->IsVisible() || pa->IsLocked())
+ continue;
+
+ netProtoCount++;
+ if (averageMode == 0)
+ averageMode = pa->iRealStatus;
+ else if (averageMode > 0 && averageMode != pa->iRealStatus) {
+ averageMode = -1;
+ if (pAccountNumber == nullptr)
+ break;
+ }
+ }
+
+ if (pAccountNumber)
+ *pAccountNumber = netProtoCount;
+ return averageMode;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct DeactivationThreadParam
+{
+ PROTO_INTERFACE *ppro;
+ pfnUninitProto fnUninit;
+ int flags;
+};
+
+pfnUninitProto GetProtocolDestructor(char *szProto);
+
+static void __cdecl DeactivationThread(DeactivationThreadParam *param)
+{
+ PROTO_INTERFACE *p = (PROTO_INTERFACE*)param->ppro;
+ p->SetStatus(ID_STATUS_OFFLINE);
+
+ char *szModuleName = NEWSTR_ALLOCA(p->m_szModuleName);
+
+ if (param->flags & DAF_DYNAMIC) {
+ while (!p->IsReadyToExit())
+ SleepEx(100, TRUE);
+
+ p->OnShutdown();
+ }
+
+ KillObjectThreads(p); // waits for them before terminating
+ KillObjectEventHooks(p); // untie an object from the outside world
+
+ if (param->flags & DAF_ERASE)
+ p->OnErase();
+
+ if (param->fnUninit)
+ param->fnUninit(p);
+
+ KillObjectServices(p);
+
+ if (param->flags & DAF_ERASE)
+ EraseAccount(szModuleName);
+
+ delete param;
+}
+
+void DeactivateAccount(PROTOACCOUNT *pa, int flags)
+{
+ if (pa->hwndAccMgrUI) {
+ DestroyWindow(pa->hwndAccMgrUI);
+ pa->hwndAccMgrUI = nullptr;
+ pa->bAccMgrUIChanged = FALSE;
+ }
+
+ if (flags & DAF_DYNAMIC)
+ NotifyEventHooks(hAccListChanged, PRAC_REMOVED, (LPARAM)pa);
+ else
+ pa->iIconBase = -1;
+
+ if (pa->ppro == nullptr) {
+ if (flags & DAF_ERASE)
+ EraseAccount(pa->szModuleName);
+ return;
+ }
+
+ DeactivationThreadParam *param = new DeactivationThreadParam;
+ param->ppro = pa->ppro;
+ param->fnUninit = GetProtocolDestructor(pa->szProtoName);
+ param->flags = flags;
+ pa->ppro = nullptr;
+ if (flags & DAF_FORK)
+ mir_forkThread<DeactivationThreadParam>(DeactivationThread, param);
+ else
+ DeactivationThread(param);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void KillModuleAccounts(HINSTANCE hInst)
+{
+ for (auto &pd : g_arProtos.rev_iter()) {
+ if (pd->hInst != hInst)
+ continue;
+
+ for (auto &pa : g_arAccounts.rev_iter()) {
+ if (!mir_strcmp(pa->szProtoName, pd->szName)) {
+ pa->bDynDisabled = true;
+ DeactivateAccount(pa, DAF_DYNAMIC);
+ }
+ }
+
+ g_arProtos.removeItem(&pd);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void EraseAccount(const char *pszModuleName)
+{
+ // remove protocol contacts first
+ for (MCONTACT hContact = db_find_first(pszModuleName); hContact != 0;) {
+ MCONTACT hNext = db_find_next(hContact, pszModuleName);
+ db_delete_contact(hContact);
+ hContact = hNext;
+ }
+
+ // remove all protocol settings
+ db_delete_module(0, pszModuleName);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void UnloadAccount(PROTOACCOUNT *pa, int flags)
+{
+ DeactivateAccount(pa, flags);
+
+ // szModuleName should be freed only on a program's exit.
+ // otherwise many plugins dependand on static protocol names will crash!
+ // do NOT fix this 'leak', please
+ if (!(flags & DAF_DYNAMIC))
+ delete pa;
+ else {
+ replaceStrW(pa->tszAccountName, 0);
+ replaceStr(pa->szProtoName, 0);
+ replaceStr(pa->szUniqueId, 0);
+ }
+}
+
+void UnloadAccountsModule()
+{
+ if (!bModuleInitialized)
+ return;
+
+ auto T = g_arAccounts.rev_iter();
+ for (auto &it : T) {
+ UnloadAccount(it, 0);
+ g_arAccounts.removeItem(&it);
+ }
+ g_arAccounts.destroy();
+
+ for (auto &it : hHooks)
+ UnhookEvent(it);
+}
diff --git a/src/mir_app/src/proto_chains.cpp b/src/mir_app/src/proto_chains.cpp index 0764a01164..5417c07995 100644 --- a/src/mir_app/src/proto_chains.cpp +++ b/src/mir_app/src/proto_chains.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_interface.cpp b/src/mir_app/src/proto_interface.cpp index ed500cf69e..4ccab724de 100644 --- a/src/mir_app/src/proto_interface.cpp +++ b/src/mir_app/src/proto_interface.cpp @@ -1,351 +1,351 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static HGENMENU hReqAuth = nullptr, hGrantAuth = nullptr, hRevokeAuth = nullptr, hServerHist = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// -// protocol constructor & destructor - -PROTO_INTERFACE::PROTO_INTERFACE(const char *pszModuleName, const wchar_t *ptszUserName) -{ - m_iVersion = 2; - m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; - m_szModuleName = mir_strdup(pszModuleName); - m_tszUserName = mir_wstrdup(ptszUserName); - db_set_resident(m_szModuleName, "Status"); -} - -PROTO_INTERFACE::~PROTO_INTERFACE() -{ - if (m_hNetlibUser) - Netlib_CloseHandle(m_hNetlibUser); - - mir_free(m_szModuleName); - mir_free(m_tszUserName); - - WindowList_Destroy(m_hWindowList); -} - -HGENMENU PROTO_INTERFACE::GetMenuItem(ProtoMenuItemType aType) -{ - switch (aType) { - case PROTO_MENU_REQ_AUTH: return hReqAuth; - case PROTO_MENU_GRANT_AUTH: return hGrantAuth; - case PROTO_MENU_REVOKE_AUTH: return hRevokeAuth; - case PROTO_MENU_LOAD_HISTORY: return hServerHist; - } - - return nullptr; -} - -void PROTO_INTERFACE::OnBuildProtoMenu() -{} - -void PROTO_INTERFACE::OnContactAdded(MCONTACT) -{} - -void PROTO_INTERFACE::OnContactDeleted(MCONTACT) -{} - -void PROTO_INTERFACE::OnEventEdited(MCONTACT, MEVENT) -{} - -void PROTO_INTERFACE::OnErase() -{} - -void PROTO_INTERFACE::OnModulesLoaded() -{} - -bool PROTO_INTERFACE::IsReadyToExit() -{ - return true; -} - -void PROTO_INTERFACE::OnShutdown() -{} - -///////////////////////////////////////////////////////////////////////////////////////// -// default PROTO_INTERFACE method implementations - -MCONTACT PROTO_INTERFACE::AddToList(int, PROTOSEARCHRESULT*) -{ - return 0; // error -} - -MCONTACT PROTO_INTERFACE::AddToListByEvent(int, int, MEVENT) -{ - return 0; // error -} - -int PROTO_INTERFACE::Authorize(MEVENT) -{ - return 1; // error -} - -int PROTO_INTERFACE::AuthDeny(MEVENT, const wchar_t*) -{ - return 1; // error -} - -int PROTO_INTERFACE::AuthRecv(MCONTACT, PROTORECVEVENT*) -{ - return 1; // error -} - -int PROTO_INTERFACE::AuthRequest(MCONTACT, const wchar_t*) -{ - return 1; // error -} - -HANDLE PROTO_INTERFACE::FileAllow(MCONTACT, HANDLE, const wchar_t*) -{ - return nullptr; // error -} - -int PROTO_INTERFACE::FileCancel(MCONTACT, HANDLE) -{ - return 1; // error -} - -int PROTO_INTERFACE::FileDeny(MCONTACT, HANDLE, const wchar_t*) -{ - return 1; // error -} - -int PROTO_INTERFACE::FileResume(HANDLE, int, const wchar_t*) -{ - return 1; // error -} - -INT_PTR PROTO_INTERFACE::GetCaps(int, MCONTACT) -{ - return 0; // empty value -} - -int PROTO_INTERFACE::GetInfo(MCONTACT, int) -{ - return 1; // error -} - -HANDLE PROTO_INTERFACE::SearchBasic(const wchar_t*) -{ - return nullptr; // error -} - -HANDLE PROTO_INTERFACE::SearchByEmail(const wchar_t*) -{ - return nullptr; // error -} - -HANDLE PROTO_INTERFACE::SearchByName(const wchar_t*, const wchar_t*, const wchar_t*) -{ - return nullptr; // error -} - -HWND PROTO_INTERFACE::SearchAdvanced(HWND) -{ - return nullptr; // error -} - -HWND PROTO_INTERFACE::CreateExtendedSearchUI(HWND) -{ - return nullptr; // error -} - -int PROTO_INTERFACE::RecvContacts(MCONTACT, PROTORECVEVENT*) -{ - return 1; // error -} - -int PROTO_INTERFACE::RecvFile(MCONTACT hContact, PROTORECVFILE *pcre) -{ - CCSDATA ccs = { hContact, PSR_FILE, 0, (LPARAM)pcre }; - return CallService(MS_PROTO_RECVFILET, 0, (LPARAM)&ccs); -} - -MEVENT PROTO_INTERFACE::RecvMsg(MCONTACT hContact, PROTORECVEVENT *pre) -{ - if (pre->szMessage == nullptr) - return 0; - - ptrA pszTemp; - mir_ptr<uint8_t> pszBlob; - - DBEVENTINFO dbei = {}; - dbei.flags = DBEF_UTF; - dbei.szModule = Proto_GetBaseAccountName(hContact); - dbei.timestamp = pre->timestamp; - dbei.eventType = EVENTTYPE_MESSAGE; - dbei.cbBlob = (uint32_t)mir_strlen(pre->szMessage) + 1; - dbei.pBlob = (uint8_t*)pre->szMessage; - - if (pre->flags & PREF_CREATEREAD) - dbei.flags |= DBEF_READ; - if (pre->flags & PREF_SENT) - dbei.flags |= DBEF_SENT; - - // if it's possible to find an existing event by its id, do that - if ((GetCaps(PFLAGNUM_4) & PF4_SERVERMSGID) && pre->szMsgId != nullptr) { - MEVENT hDbEvent = db_event_getById(m_szModuleName, pre->szMsgId); - if (hDbEvent == 0 || db_event_edit(hContact, hDbEvent, &dbei)) { - dbei.szId = pre->szMsgId; - hDbEvent = db_event_add(hContact, &dbei); - } - return hDbEvent; - } - - // event is new? add it - return (INT_PTR)db_event_add(hContact, &dbei); -} - -int PROTO_INTERFACE::SendContacts(MCONTACT, int, int, MCONTACT*) -{ - return 1; // error -} - -HANDLE PROTO_INTERFACE::SendFile(MCONTACT, const wchar_t*, wchar_t**) -{ - return nullptr; // error -} - -int PROTO_INTERFACE::SendMsg(MCONTACT, int, const char*) -{ - return 0; // error -} - -int PROTO_INTERFACE::SetApparentMode(MCONTACT, int) -{ - return 1; // error -} - -int PROTO_INTERFACE::SetStatus(int) -{ - return 1; // you better declare it -} - -HANDLE PROTO_INTERFACE::GetAwayMsg(MCONTACT) -{ - return nullptr; // no away message -} - -int PROTO_INTERFACE::RecvAwayMsg(MCONTACT, int, PROTORECVEVENT*) -{ - return 1; // error -} - -int PROTO_INTERFACE::SetAwayMsg(int, const wchar_t*) -{ - return 1; // error -} - -int PROTO_INTERFACE::UserIsTyping(MCONTACT, int) -{ - return 1; // error -} - -///////////////////////////////////////////////////////////////////////////////////////// -// protocol menus - -static INT_PTR __cdecl stubRequestAuth(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_REQAUTH, hContact, 0); - return 0; -} - -static INT_PTR __cdecl stubGrantAuth(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_GRANTAUTH, hContact, 0); - return 0; -} - -static INT_PTR __cdecl stubRevokeAuth(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_REVOKEAUTH, hContact, 0); - return 0; -} - -static INT_PTR __cdecl stubLoadHistory(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_LOADHISTORY, hContact, 0); - return 0; -} - -static int __cdecl ProtoPrebuildContactMenu(WPARAM hContact, LPARAM) -{ - Menu_ShowItem(hReqAuth, false); - Menu_ShowItem(hGrantAuth, false); - Menu_ShowItem(hRevokeAuth, false); - - const char *szProto = Proto_GetBaseAccountName(hContact); - Menu_ShowItem(hServerHist, ProtoServiceExists(szProto, PS_MENU_LOADHISTORY)); - return 0; -} - -void InitProtoMenus(void) -{ - // "Request authorization" - CMenuItem mi(&g_plugin); - SET_UID(mi, 0x36375a1f, 0xc142, 0x4d6e, 0xa6, 0x57, 0xe4, 0x76, 0x5d, 0xbc, 0x59, 0x8e); - mi.pszService = "Proto/Menu/ReqAuth"; - mi.name.a = LPGEN("Request authorization"); - mi.position = -2000001002; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REQUEST); - hReqAuth = Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, stubRequestAuth); - - // "Grant authorization" - SET_UID(mi, 0x4c90452a, 0x869a, 0x4a81, 0xaf, 0xa8, 0x28, 0x34, 0xaf, 0x2b, 0x6b, 0x30); - mi.pszService = "Proto/Menu/GrantAuth"; - mi.name.a = LPGEN("Grant authorization"); - mi.position = -2000001001; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_GRANT); - hGrantAuth = Menu_AddContactMenuItem(&mi); - - // "Revoke authorization" - SET_UID(mi, 0x619efdcb, 0x99c0, 0x44a8, 0xbf, 0x28, 0xc3, 0xe0, 0x2f, 0xb3, 0x7e, 0x77); - mi.pszService = "Proto/Menu/RevokeAuth"; - mi.name.a = LPGEN("Revoke authorization"); - mi.position = -2000001000; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REVOKE); - hRevokeAuth = Menu_AddContactMenuItem(&mi); - - SET_UID(mi, 0xd15b841d, 0xb0fc, 0x4ab5, 0x96, 0x94, 0xcf, 0x6c, 0x6e, 0x99, 0x4b, 0x3c); // {D15B841D-B0FC-4AB5-9694-CF6C6E994B3C} - mi.pszService = "Proto/Menu/LoadHistory"; - mi.name.a = LPGEN("Load server history"); - mi.position = -200001004; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HISTORY); - hServerHist = Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, stubLoadHistory); - - HookEvent(ME_CLIST_PREBUILDCONTACTMENU, ProtoPrebuildContactMenu); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static HGENMENU hReqAuth = nullptr, hGrantAuth = nullptr, hRevokeAuth = nullptr, hServerHist = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// protocol constructor & destructor
+
+PROTO_INTERFACE::PROTO_INTERFACE(const char *pszModuleName, const wchar_t *ptszUserName)
+{
+ m_iVersion = 2;
+ m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
+ m_szModuleName = mir_strdup(pszModuleName);
+ m_tszUserName = mir_wstrdup(ptszUserName);
+ db_set_resident(m_szModuleName, "Status");
+}
+
+PROTO_INTERFACE::~PROTO_INTERFACE()
+{
+ if (m_hNetlibUser)
+ Netlib_CloseHandle(m_hNetlibUser);
+
+ mir_free(m_szModuleName);
+ mir_free(m_tszUserName);
+
+ WindowList_Destroy(m_hWindowList);
+}
+
+HGENMENU PROTO_INTERFACE::GetMenuItem(ProtoMenuItemType aType)
+{
+ switch (aType) {
+ case PROTO_MENU_REQ_AUTH: return hReqAuth;
+ case PROTO_MENU_GRANT_AUTH: return hGrantAuth;
+ case PROTO_MENU_REVOKE_AUTH: return hRevokeAuth;
+ case PROTO_MENU_LOAD_HISTORY: return hServerHist;
+ }
+
+ return nullptr;
+}
+
+void PROTO_INTERFACE::OnBuildProtoMenu()
+{}
+
+void PROTO_INTERFACE::OnContactAdded(MCONTACT)
+{}
+
+void PROTO_INTERFACE::OnContactDeleted(MCONTACT)
+{}
+
+void PROTO_INTERFACE::OnEventEdited(MCONTACT, MEVENT)
+{}
+
+void PROTO_INTERFACE::OnErase()
+{}
+
+void PROTO_INTERFACE::OnModulesLoaded()
+{}
+
+bool PROTO_INTERFACE::IsReadyToExit()
+{
+ return true;
+}
+
+void PROTO_INTERFACE::OnShutdown()
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// default PROTO_INTERFACE method implementations
+
+MCONTACT PROTO_INTERFACE::AddToList(int, PROTOSEARCHRESULT*)
+{
+ return 0; // error
+}
+
+MCONTACT PROTO_INTERFACE::AddToListByEvent(int, int, MEVENT)
+{
+ return 0; // error
+}
+
+int PROTO_INTERFACE::Authorize(MEVENT)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::AuthDeny(MEVENT, const wchar_t*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::AuthRecv(MCONTACT, PROTORECVEVENT*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::AuthRequest(MCONTACT, const wchar_t*)
+{
+ return 1; // error
+}
+
+HANDLE PROTO_INTERFACE::FileAllow(MCONTACT, HANDLE, const wchar_t*)
+{
+ return nullptr; // error
+}
+
+int PROTO_INTERFACE::FileCancel(MCONTACT, HANDLE)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::FileDeny(MCONTACT, HANDLE, const wchar_t*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::FileResume(HANDLE, int, const wchar_t*)
+{
+ return 1; // error
+}
+
+INT_PTR PROTO_INTERFACE::GetCaps(int, MCONTACT)
+{
+ return 0; // empty value
+}
+
+int PROTO_INTERFACE::GetInfo(MCONTACT, int)
+{
+ return 1; // error
+}
+
+HANDLE PROTO_INTERFACE::SearchBasic(const wchar_t*)
+{
+ return nullptr; // error
+}
+
+HANDLE PROTO_INTERFACE::SearchByEmail(const wchar_t*)
+{
+ return nullptr; // error
+}
+
+HANDLE PROTO_INTERFACE::SearchByName(const wchar_t*, const wchar_t*, const wchar_t*)
+{
+ return nullptr; // error
+}
+
+HWND PROTO_INTERFACE::SearchAdvanced(HWND)
+{
+ return nullptr; // error
+}
+
+HWND PROTO_INTERFACE::CreateExtendedSearchUI(HWND)
+{
+ return nullptr; // error
+}
+
+int PROTO_INTERFACE::RecvContacts(MCONTACT, PROTORECVEVENT*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::RecvFile(MCONTACT hContact, PROTORECVFILE *pcre)
+{
+ CCSDATA ccs = { hContact, PSR_FILE, 0, (LPARAM)pcre };
+ return CallService(MS_PROTO_RECVFILET, 0, (LPARAM)&ccs);
+}
+
+MEVENT PROTO_INTERFACE::RecvMsg(MCONTACT hContact, PROTORECVEVENT *pre)
+{
+ if (pre->szMessage == nullptr)
+ return 0;
+
+ ptrA pszTemp;
+ mir_ptr<uint8_t> pszBlob;
+
+ DBEVENTINFO dbei = {};
+ dbei.flags = DBEF_UTF;
+ dbei.szModule = Proto_GetBaseAccountName(hContact);
+ dbei.timestamp = pre->timestamp;
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.cbBlob = (uint32_t)mir_strlen(pre->szMessage) + 1;
+ dbei.pBlob = (uint8_t*)pre->szMessage;
+
+ if (pre->flags & PREF_CREATEREAD)
+ dbei.flags |= DBEF_READ;
+ if (pre->flags & PREF_SENT)
+ dbei.flags |= DBEF_SENT;
+
+ // if it's possible to find an existing event by its id, do that
+ if ((GetCaps(PFLAGNUM_4) & PF4_SERVERMSGID) && pre->szMsgId != nullptr) {
+ MEVENT hDbEvent = db_event_getById(m_szModuleName, pre->szMsgId);
+ if (hDbEvent == 0 || db_event_edit(hContact, hDbEvent, &dbei)) {
+ dbei.szId = pre->szMsgId;
+ hDbEvent = db_event_add(hContact, &dbei);
+ }
+ return hDbEvent;
+ }
+
+ // event is new? add it
+ return (INT_PTR)db_event_add(hContact, &dbei);
+}
+
+int PROTO_INTERFACE::SendContacts(MCONTACT, int, int, MCONTACT*)
+{
+ return 1; // error
+}
+
+HANDLE PROTO_INTERFACE::SendFile(MCONTACT, const wchar_t*, wchar_t**)
+{
+ return nullptr; // error
+}
+
+int PROTO_INTERFACE::SendMsg(MCONTACT, int, const char*)
+{
+ return 0; // error
+}
+
+int PROTO_INTERFACE::SetApparentMode(MCONTACT, int)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::SetStatus(int)
+{
+ return 1; // you better declare it
+}
+
+HANDLE PROTO_INTERFACE::GetAwayMsg(MCONTACT)
+{
+ return nullptr; // no away message
+}
+
+int PROTO_INTERFACE::RecvAwayMsg(MCONTACT, int, PROTORECVEVENT*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::SetAwayMsg(int, const wchar_t*)
+{
+ return 1; // error
+}
+
+int PROTO_INTERFACE::UserIsTyping(MCONTACT, int)
+{
+ return 1; // error
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// protocol menus
+
+static INT_PTR __cdecl stubRequestAuth(WPARAM hContact, LPARAM)
+{
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto)
+ ProtoCallService(szProto, PS_MENU_REQAUTH, hContact, 0);
+ return 0;
+}
+
+static INT_PTR __cdecl stubGrantAuth(WPARAM hContact, LPARAM)
+{
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto)
+ ProtoCallService(szProto, PS_MENU_GRANTAUTH, hContact, 0);
+ return 0;
+}
+
+static INT_PTR __cdecl stubRevokeAuth(WPARAM hContact, LPARAM)
+{
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto)
+ ProtoCallService(szProto, PS_MENU_REVOKEAUTH, hContact, 0);
+ return 0;
+}
+
+static INT_PTR __cdecl stubLoadHistory(WPARAM hContact, LPARAM)
+{
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto)
+ ProtoCallService(szProto, PS_MENU_LOADHISTORY, hContact, 0);
+ return 0;
+}
+
+static int __cdecl ProtoPrebuildContactMenu(WPARAM hContact, LPARAM)
+{
+ Menu_ShowItem(hReqAuth, false);
+ Menu_ShowItem(hGrantAuth, false);
+ Menu_ShowItem(hRevokeAuth, false);
+
+ const char *szProto = Proto_GetBaseAccountName(hContact);
+ Menu_ShowItem(hServerHist, ProtoServiceExists(szProto, PS_MENU_LOADHISTORY));
+ return 0;
+}
+
+void InitProtoMenus(void)
+{
+ // "Request authorization"
+ CMenuItem mi(&g_plugin);
+ SET_UID(mi, 0x36375a1f, 0xc142, 0x4d6e, 0xa6, 0x57, 0xe4, 0x76, 0x5d, 0xbc, 0x59, 0x8e);
+ mi.pszService = "Proto/Menu/ReqAuth";
+ mi.name.a = LPGEN("Request authorization");
+ mi.position = -2000001002;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REQUEST);
+ hReqAuth = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, stubRequestAuth);
+
+ // "Grant authorization"
+ SET_UID(mi, 0x4c90452a, 0x869a, 0x4a81, 0xaf, 0xa8, 0x28, 0x34, 0xaf, 0x2b, 0x6b, 0x30);
+ mi.pszService = "Proto/Menu/GrantAuth";
+ mi.name.a = LPGEN("Grant authorization");
+ mi.position = -2000001001;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_GRANT);
+ hGrantAuth = Menu_AddContactMenuItem(&mi);
+
+ // "Revoke authorization"
+ SET_UID(mi, 0x619efdcb, 0x99c0, 0x44a8, 0xbf, 0x28, 0xc3, 0xe0, 0x2f, 0xb3, 0x7e, 0x77);
+ mi.pszService = "Proto/Menu/RevokeAuth";
+ mi.name.a = LPGEN("Revoke authorization");
+ mi.position = -2000001000;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REVOKE);
+ hRevokeAuth = Menu_AddContactMenuItem(&mi);
+
+ SET_UID(mi, 0xd15b841d, 0xb0fc, 0x4ab5, 0x96, 0x94, 0xcf, 0x6c, 0x6e, 0x99, 0x4b, 0x3c); // {D15B841D-B0FC-4AB5-9694-CF6C6E994B3C}
+ mi.pszService = "Proto/Menu/LoadHistory";
+ mi.name.a = LPGEN("Load server history");
+ mi.position = -200001004;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HISTORY);
+ hServerHist = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, stubLoadHistory);
+
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, ProtoPrebuildContactMenu);
+}
diff --git a/src/mir_app/src/proto_internal.cpp b/src/mir_app/src/proto_internal.cpp index b7a74d8d4d..0b40f9cee1 100644 --- a/src/mir_app/src/proto_internal.cpp +++ b/src/mir_app/src/proto_internal.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_opts.cpp b/src/mir_app/src/proto_opts.cpp index c25a6c937b..c2dfe73e72 100644 --- a/src/mir_app/src/proto_opts.cpp +++ b/src/mir_app/src/proto_opts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_order.cpp b/src/mir_app/src/proto_order.cpp index 9096b0cb61..3024e62b0e 100644 --- a/src/mir_app/src/proto_order.cpp +++ b/src/mir_app/src/proto_order.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_ui.cpp b/src/mir_app/src/proto_ui.cpp index 3ef0a00233..13319b83a1 100644 --- a/src/mir_app/src/proto_ui.cpp +++ b/src/mir_app/src/proto_ui.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/proto_utils.cpp b/src/mir_app/src/proto_utils.cpp index 79bd7012fa..281d76719e 100644 --- a/src/mir_app/src/proto_utils.cpp +++ b/src/mir_app/src/proto_utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/protocols.cpp b/src/mir_app/src/protocols.cpp index 12fe246fb1..f3a1d60f5c 100644 --- a/src/mir_app/src/protocols.cpp +++ b/src/mir_app/src/protocols.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/pu_utils.cpp b/src/mir_app/src/pu_utils.cpp index 9495031fe7..1ed944ea3a 100644 --- a/src/mir_app/src/pu_utils.cpp +++ b/src/mir_app/src/pu_utils.cpp @@ -1,331 +1,331 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// pu_stub.exe interface - -#include "stdafx.h" - -static HANDLE g_hPipe = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// -// are we running with admin priviledges? - -static bool IsRunAsAdmin() -{ - BOOL bIsRunAsAdmin = false; - uint32_t dwError = ERROR_SUCCESS; - PSID pAdministratorsGroup = nullptr; - - // Allocate and initialize a SID of the administrators group. - SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; - if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdministratorsGroup)) { - dwError = GetLastError(); - goto Cleanup; - } - - // Determine whether the SID of administrators group is bEnabled in - // the primary access token of the process. - if (!CheckTokenMembership(nullptr, pAdministratorsGroup, &bIsRunAsAdmin)) { - dwError = GetLastError(); - goto Cleanup; - } - -Cleanup: - // Centralized cleanup for all allocated resources. - if (pAdministratorsGroup) { - FreeSid(pAdministratorsGroup); - pAdministratorsGroup = nullptr; - } - - // Throw the error if something failed in the function. - if (ERROR_SUCCESS != dwError) { - throw dwError; - } - - return bIsRunAsAdmin != 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Checks if we're working via pu_stub or not - -MIR_APP_DLL(bool) PU::IsDirect() -{ - return g_hPipe == nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Checks if Miranda's folder is writeable - -MIR_APP_DLL(bool) PU::IsMirandaFolderWritable() -{ - if (!IsWinVerVistaPlus()) - return true; - - wchar_t wszPath[MAX_PATH]; - GetModuleFileNameW(nullptr, wszPath, _countof(wszPath)); - wchar_t *ext = wcsrchr(wszPath, '.'); - if (ext != nullptr) - *ext = '\0'; - wcscat(wszPath, L".test"); - HANDLE hFile = CreateFileW(wszPath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (hFile == INVALID_HANDLE_VALUE) - return false; - - CloseHandle(hFile); - DeleteFileW(wszPath); - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Checks if a process has enough rights to write into Miranda's folder - -MIR_APP_DLL(bool) PU::IsProcessElevated() -{ - bool bIsElevated = false; - HANDLE hToken = nullptr; - - // Open the primary access token of the process with TOKEN_QUERY. - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) - goto Cleanup; - - // Retrieve token elevation information. - TOKEN_ELEVATION elevation; - DWORD dwSize; - if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) { - // When the process is run on operating systems prior to Windows - // Vista, GetTokenInformation returns FALSE with the - // ERROR_INVALID_PARAMETER error code because TokenElevation is - // not supported on those operating systems. - goto Cleanup; - } - - bIsElevated = elevation.TokenIsElevated != 0; - -Cleanup: - // Centralized cleanup for all allocated resources. - if (hToken) - CloseHandle(hToken); - - return bIsElevated; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Launches pu_stub.exe with elevated priviledges if needed - -MIR_APP_DLL(bool) PU::PrepareEscalation(const wchar_t *pwszFile) -{ - CMStringW wszFilePath; - // First try to create a file near Miranda32.exe - if (pwszFile == nullptr) { - wchar_t szPath[MAX_PATH]; - GetModuleFileName(nullptr, szPath, _countof(szPath)); - wchar_t *ext = wcsrchr(szPath, '.'); - if (ext != nullptr) - *ext = '\0'; - wszFilePath = szPath; - } - else wszFilePath = pwszFile; - - wszFilePath.Append(L".test"); - - HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (hFile != INVALID_HANDLE_VALUE) { - // we are admins or UAC is disable, cool - CloseHandle(hFile); - DeleteFileW(wszFilePath); - return true; - } - - // Check the current process's "run as administrator" status. - if (IsRunAsAdmin()) - return true; - - // if pipe already opened? - if (g_hPipe != nullptr) - return true; - - // Elevate the process. Create a pipe for a stub first - wchar_t wzPipeName[MAX_PATH]; - mir_snwprintf(wzPipeName, L"\\\\.\\pipe\\Miranda_Pu_%d", GetCurrentProcessId()); - g_hPipe = CreateNamedPipe(wzPipeName, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, nullptr); - if (g_hPipe == INVALID_HANDLE_VALUE) { - g_hPipe = nullptr; - } - else { - wchar_t cmdLine[100], *p; - wchar_t szPath[MAX_PATH]; - GetModuleFileName(nullptr, szPath, _countof(szPath)); - if ((p = wcsrchr(szPath, '\\')) != nullptr) - wcscpy(p + 1, L"pu_stub.exe"); - mir_snwprintf(cmdLine, L"%d", GetCurrentProcessId()); - - // Launch a stub - SHELLEXECUTEINFO sei = { sizeof(sei) }; - sei.lpVerb = L"runas"; - sei.lpFile = szPath; - sei.lpParameters = cmdLine; - sei.hwnd = nullptr; - sei.nShow = SW_NORMAL; - if (ShellExecuteEx(&sei)) { - if (g_hPipe != nullptr) - ConnectNamedPipe(g_hPipe, nullptr); - return true; - } - - uint32_t dwError = GetLastError(); - if (dwError == ERROR_CANCELLED) { - // The user refused to allow privileges elevation. - // Do nothing ... - } - } - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int TransactPipe(int opcode, const wchar_t *p1, const wchar_t *p2) -{ - uint8_t buf[1024]; - uint32_t l1 = lstrlen(p1), l2 = lstrlen(p2); - if (l1 > MAX_PATH || l2 > MAX_PATH) - return ERROR_BAD_ARGUMENTS; - - *(uint32_t *)buf = opcode; - wchar_t *dst = (wchar_t *)&buf[sizeof(uint32_t)]; - lstrcpy(dst, p1); - dst += l1 + 1; - if (p2) { - lstrcpy(dst, p2); - dst += l2 + 1; - } - else *dst++ = 0; - - DWORD dwBytes = 0, dwError; - if (!WriteFile(g_hPipe, buf, (uint32_t)((uint8_t *)dst - buf), &dwBytes, nullptr)) - return GetLastError(); - - dwError = 0; - if (!ReadFile(g_hPipe, &dwError, sizeof(uint32_t), &dwBytes, nullptr)) - return GetLastError(); - if (dwBytes != sizeof(uint32_t)) - return ERROR_BAD_ARGUMENTS; - - return dwError; -} - -MIR_APP_DLL(int) PU::SafeCopyFile(const wchar_t *pSrc, const wchar_t *pDst) -{ - if (g_hPipe == nullptr) - return CopyFileW(pSrc, pDst, FALSE); - - return TransactPipe(1, pSrc, pDst); -} - -MIR_APP_DLL(int) PU::SafeMoveFile(const wchar_t *pSrc, const wchar_t *pDst) -{ - if (g_hPipe == nullptr) { - if (!DeleteFileW(pDst)) { - uint32_t dwError = GetLastError(); - if (dwError != ERROR_ACCESS_DENIED && dwError != ERROR_FILE_NOT_FOUND) - return dwError; - } - - if (!MoveFileW(pSrc, pDst)) { // use copy on error - switch (uint32_t dwError = GetLastError()) { - case ERROR_ALREADY_EXISTS: - case ERROR_FILE_NOT_FOUND: - return 0; // this file was included into many archives, so Miranda tries to move it again & again - - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - case ERROR_LOCK_VIOLATION: - // use copy routine if a move operation isn't available - // for example, when files are on different disks - if (!CopyFileW(pSrc, pDst, FALSE)) - return GetLastError(); - - if (!DeleteFileW(pSrc)) - return GetLastError(); - break; - - default: - return dwError; - } - } - - return ERROR_SUCCESS; - } - - return TransactPipe(2, pSrc, pDst); -} - -MIR_APP_DLL(int) PU::SafeDeleteFile(const wchar_t *pwszFile) -{ - if (g_hPipe == nullptr) - return DeleteFileW(pwszFile); - - return TransactPipe(3, pwszFile, nullptr); -} - -MIR_APP_DLL(int) PU::SafeRecycleBin(const wchar_t *pwszFile) -{ - if (g_hPipe == nullptr) { - CMStringW tmpPath(pwszFile); - tmpPath.AppendChar(0); - - SHFILEOPSTRUCT shfo = {}; - shfo.wFunc = FO_DELETE; - shfo.pFrom = tmpPath; - shfo.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_ALLOWUNDO; - return SHFileOperation(&shfo); - } - - return TransactPipe(7, pwszFile, nullptr); -} - -MIR_APP_DLL(int) PU::SafeCreateDirectory(const wchar_t *pwszFolder) -{ - if (g_hPipe == nullptr) - return CreateDirectoryTreeW(pwszFolder); - - return TransactPipe(4, pwszFolder, nullptr); -} - -MIR_APP_DLL(int) PU::SafeDeleteDirectory(const wchar_t *pwszDirName) -{ - if (g_hPipe == nullptr) - return DeleteDirectoryTreeW(pwszDirName); - - return TransactPipe(6, pwszDirName, nullptr); -} - -MIR_APP_DLL(int) PU::SafeCreateFilePath(const wchar_t *pwszFolder) -{ - if (g_hPipe == nullptr) { - CreatePathToFileW(pwszFolder); - return 0; - } - - return TransactPipe(5, pwszFolder, nullptr); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// pu_stub.exe interface
+
+#include "stdafx.h"
+
+static HANDLE g_hPipe = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// are we running with admin priviledges?
+
+static bool IsRunAsAdmin()
+{
+ BOOL bIsRunAsAdmin = false;
+ uint32_t dwError = ERROR_SUCCESS;
+ PSID pAdministratorsGroup = nullptr;
+
+ // Allocate and initialize a SID of the administrators group.
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+ if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdministratorsGroup)) {
+ dwError = GetLastError();
+ goto Cleanup;
+ }
+
+ // Determine whether the SID of administrators group is bEnabled in
+ // the primary access token of the process.
+ if (!CheckTokenMembership(nullptr, pAdministratorsGroup, &bIsRunAsAdmin)) {
+ dwError = GetLastError();
+ goto Cleanup;
+ }
+
+Cleanup:
+ // Centralized cleanup for all allocated resources.
+ if (pAdministratorsGroup) {
+ FreeSid(pAdministratorsGroup);
+ pAdministratorsGroup = nullptr;
+ }
+
+ // Throw the error if something failed in the function.
+ if (ERROR_SUCCESS != dwError) {
+ throw dwError;
+ }
+
+ return bIsRunAsAdmin != 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Checks if we're working via pu_stub or not
+
+MIR_APP_DLL(bool) PU::IsDirect()
+{
+ return g_hPipe == nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Checks if Miranda's folder is writeable
+
+MIR_APP_DLL(bool) PU::IsMirandaFolderWritable()
+{
+ if (!IsWinVerVistaPlus())
+ return true;
+
+ wchar_t wszPath[MAX_PATH];
+ GetModuleFileNameW(nullptr, wszPath, _countof(wszPath));
+ wchar_t *ext = wcsrchr(wszPath, '.');
+ if (ext != nullptr)
+ *ext = '\0';
+ wcscat(wszPath, L".test");
+ HANDLE hFile = CreateFileW(wszPath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (hFile == INVALID_HANDLE_VALUE)
+ return false;
+
+ CloseHandle(hFile);
+ DeleteFileW(wszPath);
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Checks if a process has enough rights to write into Miranda's folder
+
+MIR_APP_DLL(bool) PU::IsProcessElevated()
+{
+ bool bIsElevated = false;
+ HANDLE hToken = nullptr;
+
+ // Open the primary access token of the process with TOKEN_QUERY.
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
+ goto Cleanup;
+
+ // Retrieve token elevation information.
+ TOKEN_ELEVATION elevation;
+ DWORD dwSize;
+ if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) {
+ // When the process is run on operating systems prior to Windows
+ // Vista, GetTokenInformation returns FALSE with the
+ // ERROR_INVALID_PARAMETER error code because TokenElevation is
+ // not supported on those operating systems.
+ goto Cleanup;
+ }
+
+ bIsElevated = elevation.TokenIsElevated != 0;
+
+Cleanup:
+ // Centralized cleanup for all allocated resources.
+ if (hToken)
+ CloseHandle(hToken);
+
+ return bIsElevated;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Launches pu_stub.exe with elevated priviledges if needed
+
+MIR_APP_DLL(bool) PU::PrepareEscalation(const wchar_t *pwszFile)
+{
+ CMStringW wszFilePath;
+ // First try to create a file near Miranda32.exe
+ if (pwszFile == nullptr) {
+ wchar_t szPath[MAX_PATH];
+ GetModuleFileName(nullptr, szPath, _countof(szPath));
+ wchar_t *ext = wcsrchr(szPath, '.');
+ if (ext != nullptr)
+ *ext = '\0';
+ wszFilePath = szPath;
+ }
+ else wszFilePath = pwszFile;
+
+ wszFilePath.Append(L".test");
+
+ HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ // we are admins or UAC is disable, cool
+ CloseHandle(hFile);
+ DeleteFileW(wszFilePath);
+ return true;
+ }
+
+ // Check the current process's "run as administrator" status.
+ if (IsRunAsAdmin())
+ return true;
+
+ // if pipe already opened?
+ if (g_hPipe != nullptr)
+ return true;
+
+ // Elevate the process. Create a pipe for a stub first
+ wchar_t wzPipeName[MAX_PATH];
+ mir_snwprintf(wzPipeName, L"\\\\.\\pipe\\Miranda_Pu_%d", GetCurrentProcessId());
+ g_hPipe = CreateNamedPipe(wzPipeName, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, nullptr);
+ if (g_hPipe == INVALID_HANDLE_VALUE) {
+ g_hPipe = nullptr;
+ }
+ else {
+ wchar_t cmdLine[100], *p;
+ wchar_t szPath[MAX_PATH];
+ GetModuleFileName(nullptr, szPath, _countof(szPath));
+ if ((p = wcsrchr(szPath, '\\')) != nullptr)
+ wcscpy(p + 1, L"pu_stub.exe");
+ mir_snwprintf(cmdLine, L"%d", GetCurrentProcessId());
+
+ // Launch a stub
+ SHELLEXECUTEINFO sei = { sizeof(sei) };
+ sei.lpVerb = L"runas";
+ sei.lpFile = szPath;
+ sei.lpParameters = cmdLine;
+ sei.hwnd = nullptr;
+ sei.nShow = SW_NORMAL;
+ if (ShellExecuteEx(&sei)) {
+ if (g_hPipe != nullptr)
+ ConnectNamedPipe(g_hPipe, nullptr);
+ return true;
+ }
+
+ uint32_t dwError = GetLastError();
+ if (dwError == ERROR_CANCELLED) {
+ // The user refused to allow privileges elevation.
+ // Do nothing ...
+ }
+ }
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int TransactPipe(int opcode, const wchar_t *p1, const wchar_t *p2)
+{
+ uint8_t buf[1024];
+ uint32_t l1 = lstrlen(p1), l2 = lstrlen(p2);
+ if (l1 > MAX_PATH || l2 > MAX_PATH)
+ return ERROR_BAD_ARGUMENTS;
+
+ *(uint32_t *)buf = opcode;
+ wchar_t *dst = (wchar_t *)&buf[sizeof(uint32_t)];
+ lstrcpy(dst, p1);
+ dst += l1 + 1;
+ if (p2) {
+ lstrcpy(dst, p2);
+ dst += l2 + 1;
+ }
+ else *dst++ = 0;
+
+ DWORD dwBytes = 0, dwError;
+ if (!WriteFile(g_hPipe, buf, (uint32_t)((uint8_t *)dst - buf), &dwBytes, nullptr))
+ return GetLastError();
+
+ dwError = 0;
+ if (!ReadFile(g_hPipe, &dwError, sizeof(uint32_t), &dwBytes, nullptr))
+ return GetLastError();
+ if (dwBytes != sizeof(uint32_t))
+ return ERROR_BAD_ARGUMENTS;
+
+ return dwError;
+}
+
+MIR_APP_DLL(int) PU::SafeCopyFile(const wchar_t *pSrc, const wchar_t *pDst)
+{
+ if (g_hPipe == nullptr)
+ return CopyFileW(pSrc, pDst, FALSE);
+
+ return TransactPipe(1, pSrc, pDst);
+}
+
+MIR_APP_DLL(int) PU::SafeMoveFile(const wchar_t *pSrc, const wchar_t *pDst)
+{
+ if (g_hPipe == nullptr) {
+ if (!DeleteFileW(pDst)) {
+ uint32_t dwError = GetLastError();
+ if (dwError != ERROR_ACCESS_DENIED && dwError != ERROR_FILE_NOT_FOUND)
+ return dwError;
+ }
+
+ if (!MoveFileW(pSrc, pDst)) { // use copy on error
+ switch (uint32_t dwError = GetLastError()) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_NOT_FOUND:
+ return 0; // this file was included into many archives, so Miranda tries to move it again & again
+
+ case ERROR_ACCESS_DENIED:
+ case ERROR_SHARING_VIOLATION:
+ case ERROR_LOCK_VIOLATION:
+ // use copy routine if a move operation isn't available
+ // for example, when files are on different disks
+ if (!CopyFileW(pSrc, pDst, FALSE))
+ return GetLastError();
+
+ if (!DeleteFileW(pSrc))
+ return GetLastError();
+ break;
+
+ default:
+ return dwError;
+ }
+ }
+
+ return ERROR_SUCCESS;
+ }
+
+ return TransactPipe(2, pSrc, pDst);
+}
+
+MIR_APP_DLL(int) PU::SafeDeleteFile(const wchar_t *pwszFile)
+{
+ if (g_hPipe == nullptr)
+ return DeleteFileW(pwszFile);
+
+ return TransactPipe(3, pwszFile, nullptr);
+}
+
+MIR_APP_DLL(int) PU::SafeRecycleBin(const wchar_t *pwszFile)
+{
+ if (g_hPipe == nullptr) {
+ CMStringW tmpPath(pwszFile);
+ tmpPath.AppendChar(0);
+
+ SHFILEOPSTRUCT shfo = {};
+ shfo.wFunc = FO_DELETE;
+ shfo.pFrom = tmpPath;
+ shfo.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_ALLOWUNDO;
+ return SHFileOperation(&shfo);
+ }
+
+ return TransactPipe(7, pwszFile, nullptr);
+}
+
+MIR_APP_DLL(int) PU::SafeCreateDirectory(const wchar_t *pwszFolder)
+{
+ if (g_hPipe == nullptr)
+ return CreateDirectoryTreeW(pwszFolder);
+
+ return TransactPipe(4, pwszFolder, nullptr);
+}
+
+MIR_APP_DLL(int) PU::SafeDeleteDirectory(const wchar_t *pwszDirName)
+{
+ if (g_hPipe == nullptr)
+ return DeleteDirectoryTreeW(pwszDirName);
+
+ return TransactPipe(6, pwszDirName, nullptr);
+}
+
+MIR_APP_DLL(int) PU::SafeCreateFilePath(const wchar_t *pwszFolder)
+{
+ if (g_hPipe == nullptr) {
+ CreatePathToFileW(pwszFolder);
+ return 0;
+ }
+
+ return TransactPipe(5, pwszFolder, nullptr);
+}
diff --git a/src/mir_app/src/searchresults.cpp b/src/mir_app/src/searchresults.cpp index 2a298d6598..892ec2c7dd 100644 --- a/src/mir_app/src/searchresults.cpp +++ b/src/mir_app/src/searchresults.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/skin.h b/src/mir_app/src/skin.h index d0a76adfc0..e6225825a8 100644 --- a/src/mir_app/src/skin.h +++ b/src/mir_app/src/skin.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/skin2opts.cpp b/src/mir_app/src/skin2opts.cpp index 5d1cd2e072..a42db0b245 100644 --- a/src/mir_app/src/skin2opts.cpp +++ b/src/mir_app/src/skin2opts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/skinicons.cpp b/src/mir_app/src/skinicons.cpp index 927f5e798f..f6ce8e8b5d 100644 --- a/src/mir_app/src/skinicons.cpp +++ b/src/mir_app/src/skinicons.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/sounds.cpp b/src/mir_app/src/sounds.cpp index 8da80075df..8762adb26b 100644 --- a/src/mir_app/src/sounds.cpp +++ b/src/mir_app/src/sounds.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/srmm_base.cpp b/src/mir_app/src/srmm_base.cpp index 85a9308301..fd1d564a8a 100644 --- a/src/mir_app/src/srmm_base.cpp +++ b/src/mir_app/src/srmm_base.cpp @@ -1,923 +1,923 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "chat.h" -#include "resource.h" -#include "skin.h" -#include <m_history.h> - -CSrmmBaseDialog::CSrmmBaseDialog(CMPluginBase &pPlugin, int idDialog, SESSION_INFO *si) : - CDlgBase(pPlugin, idDialog), - timerFlash(this, 1), - timerType(this, 2), - - m_message(this, IDC_SRMM_MESSAGE), - m_nickList(this, IDC_SRMM_NICKLIST), - - m_btnOk(this, IDOK), - m_btnFilter(this, IDC_SRMM_FILTER), - m_btnHistory(this, IDC_SRMM_HISTORY), - m_btnNickList(this, IDC_SRMM_SHOWNICKLIST), - m_btnChannelMgr(this, IDC_SRMM_CHANMGR), - - m_btnColor(this, IDC_SRMM_COLOR), - m_btnBkColor(this, IDC_SRMM_BKGCOLOR), - m_btnBold(this, IDC_SRMM_BOLD), - - m_btnItalic(this, IDC_SRMM_ITALICS), - m_btnUnderline(this, IDC_SRMM_UNDERLINE), - - m_si(si), - m_hContact(0), - m_clrInputBG(GetSysColor(COLOR_WINDOW)) -{ - m_bFilterEnabled = db_get_b(0, CHAT_MODULE, "FilterEnabled", 0) != 0; - m_bNicklistEnabled = db_get_b(0, CHAT_MODULE, "ShowNicklist", 1) != 0; - m_iLogFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", 0x03E0); - - m_btnColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_Color); - m_btnBkColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BkColor); - m_btnBold.OnClick = m_btnItalic.OnClick = m_btnUnderline.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BIU); - - m_btnHistory.OnClick = Callback(this, &CSrmmBaseDialog::onClick_History); - m_btnChannelMgr.OnClick = Callback(this, &CSrmmBaseDialog::onClick_ChanMgr); - - m_nickList.OnDblClick = Callback(this, &CMsgDialog::onDblClick_List); - - if (si) { - m_hContact = si->hContact; - - if (si->pMI->bColor) { - m_iFG = 4; - m_bFGSet = true; - } - if (si->pMI->bBkgColor) { - m_iBG = 2; - m_bBGSet = true; - } - } -} - -void CSrmmBaseDialog::RunUserMenu(HWND hwndOwner, USERINFO *ui, const POINT &pt) -{ - HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_USERMENU)); - HMENU hSubMenu = GetSubMenu(hMenu, 0); - TranslateMenu(hSubMenu); - - USERINFO uinew; - memcpy(&uinew, ui, sizeof(USERINFO)); - - wchar_t szTemp[50]; - if (uinew.pszNick) - mir_snwprintf(szTemp, TranslateT("&Message %s"), uinew.pszNick); - else - mir_wstrncpy(szTemp, TranslateT("&Message"), _countof(szTemp) - 1); - - if (mir_wstrlen(szTemp) > 40) - mir_wstrncpy(szTemp + 40, L"...", 4); - ModifyMenu(hMenu, 0, MF_STRING | MF_BYPOSITION, IDM_SENDMESSAGE, szTemp); - - UINT uID = Chat_CreateMenu(hwndOwner, hSubMenu, pt, m_si, uinew.pszUID); - switch (uID) { - case 0: - break; - - case IDM_SENDMESSAGE: - Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0); - break; - - default: - Chat_DoEventHook(m_si, GC_USER_NICKLISTMENU, ui, nullptr, uID); - break; - } - DestroyMenu(hMenu); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK Srmm_ButtonSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_RBUTTONUP: - if (g_chatApi.bRightClickFilter) { - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - if (pDlg == nullptr) - break; - - switch (GetDlgCtrlID(hwnd)) { - case IDC_SRMM_FILTER: - pDlg->ShowFilterMenu(); - break; - - case IDC_SRMM_COLOR: - pDlg->ShowColorChooser(IDC_SRMM_COLOR); - break; - - case IDC_SRMM_BKGCOLOR: - pDlg->ShowColorChooser(IDC_SRMM_BKGCOLOR); - break; - } - } - break; - } - - return mir_callNextSubclass(hwnd, Srmm_ButtonSubclassProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - if (pDlg != nullptr) - return pDlg->WndProc_Message(msg, wParam, lParam); - - return mir_callNextSubclass(hwnd, stubMessageProc, msg, wParam, lParam); -} - -LRESULT CSrmmBaseDialog::WndProc_Message(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_SETCURSOR: - if (m_bInMenu) { - SetCursor(LoadCursor(nullptr, IDC_ARROW)); - return TRUE; - } - break; - - case WM_CHAR: - switch (wParam) { - case 0x02: // ctrl+B - if (m_btnBold.Enabled()) - return 1; - break; - case 0x09: // ctrl+I - if (m_btnItalic.Enabled()) - return 1; - break; - case 0x15: // ctrl+U - if (m_btnUnderline.Enabled()) - return 1; - break; - } - break; - - case WM_SYSKEYDOWN: - case WM_KEYDOWN: - if (wParam == VK_BACK) - if (m_message.GetRichTextLength() == 0) - return 1; - - MSG tmp = { m_hwnd, msg, wParam, lParam }; - if (Hotkey_Check(&tmp, g_pszHotkeySection) == 100) { - if (!(GetWindowLongPtr(m_message.GetHwnd(), GWL_STYLE) & ES_READONLY)) { - PostMessage(m_hwnd, WM_COMMAND, IDOK, 0); - return true; - } - } - } - - LRESULT res = mir_callNextSubclass(m_message.GetHwnd(), stubMessageProc, msg, wParam, lParam); - switch (msg) { - case WM_GETDLGCODE: - return res & ~DLGC_HASSETSEL; - - case WM_KEYUP: - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - RefreshButtonStatus(); - break; - - case WM_KEYDOWN: - if ((GetKeyState(VK_CONTROL) & 0x8000) && wParam == 'V' || (GetKeyState(VK_SHIFT) & 0x8000) && wParam == VK_INSERT) { - if (IsClipboardFormatAvailable(CF_HDROP)) { - m_message.SendMsg(WM_PASTE, 0, 0); - return 0; - } - } - - __fallthrough; - - case WM_SYSKEYDOWN: - if (!(GetKeyState(VK_RMENU) & 0x8000)) { - MSG message = { m_hwnd, msg, wParam, lParam }; - LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION); - if (iButtonFrom) { - Srmm_ProcessToolbarHotkey(m_hContact, iButtonFrom, m_hwnd); - return TRUE; - } - } - break; - } - - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// process mouse - hovering for the nickname list.fires events so the protocol can -// show the userinfo - tooltip. - -static void ProcessNickListHovering(HWND hwnd, int hoveredItem, SESSION_INFO *parentdat) -{ - static int currentHovered = -1; - static HWND hwndToolTip = nullptr; - static HWND oldParent = nullptr; - - if (hoveredItem == currentHovered) - return; - - currentHovered = hoveredItem; - - if (oldParent != hwnd && hwndToolTip) { - SendMessage(hwndToolTip, TTM_DELTOOL, 0, 0); - DestroyWindow(hwndToolTip); - hwndToolTip = nullptr; - } - - if (hoveredItem == -1) { - SendMessage(hwndToolTip, TTM_ACTIVATE, 0, 0); - return; - } - - bool bNewTip = false; - if (!hwndToolTip) { - bNewTip = true; - hwndToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, nullptr, - WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - hwnd, nullptr, g_plugin.getInst(), nullptr); - } - - RECT clientRect; - GetClientRect(hwnd, &clientRect); - - TOOLINFO ti = { sizeof(ti) }; - ti.uFlags = TTF_SUBCLASS; - ti.hinst = g_plugin.getInst(); - ti.hwnd = hwnd; - ti.uId = 1; - ti.rect = clientRect; - - CMStringW wszBuf; - - USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(parentdat->ptszID, parentdat->pszModule, currentHovered); - if (ui1) { - if (ProtoServiceExists(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) { - wchar_t *p = (wchar_t*)CallProtoService(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)parentdat->ptszID, (LPARAM)ui1->pszUID); - if (p != nullptr) { - wszBuf = p; - mir_free(p); - } - } - - if (wszBuf.IsEmpty()) - wszBuf.Format(L"%s: %s\r\n%s: %s\r\n%s: %s", - TranslateT("Nickname"), ui1->pszNick, - TranslateT("Unique ID"), ui1->pszUID, - TranslateT("Status"), g_chatApi.TM_WordToString(parentdat->pStatuses, ui1->Status)); - ti.lpszText = wszBuf.GetBuffer(); - } - - SendMessage(hwndToolTip, bNewTip ? TTM_ADDTOOL : TTM_UPDATETIPTEXT, 0, (LPARAM)&ti); - SendMessage(hwndToolTip, TTM_ACTIVATE, (ti.lpszText != nullptr), 0); - SendMessage(hwndToolTip, TTM_SETMAXTIPWIDTH, 0, 400); -} - -static void CALLBACK ChatTimerProc(HWND hwnd, UINT, UINT_PTR idEvent, DWORD) -{ - SESSION_INFO *si = (SESSION_INFO*)idEvent; - - POINT pt; - GetCursorPos(&pt); - ScreenToClient(hwnd, &pt); - - uint32_t nItemUnderMouse = (uint32_t)SendMessage(hwnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y)); - if (HIWORD(nItemUnderMouse) == 1) - nItemUnderMouse = (uint32_t)(-1); - else - nItemUnderMouse &= 0xFFFF; - if (((int)nItemUnderMouse != si->currentHovered) || (nItemUnderMouse == -1)) { - KillTimer(hwnd, idEvent); - return; - } - - USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(si->ptszID, si->pszModule, si->currentHovered); - if (ui1) { - CMStringW wszBuf; - if (ProtoServiceExists(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) { - wchar_t *p = (wchar_t*)CallProtoService(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)si->ptszID, (LPARAM)ui1->pszUID); - if (p) { - wszBuf = p; - mir_free(p); - } - } - if (wszBuf.IsEmpty()) - wszBuf.Format(L"<b>%s:</b>\t%s\n<b>%s:</b>\t%s\n<b>%s:</b>\t%s", - TranslateT("Nick"), ui1->pszNick, - TranslateT("Unique ID"), ui1->pszUID, - TranslateT("Status"), g_chatApi.TM_WordToString(si->pStatuses, ui1->Status)); - - CLCINFOTIP ti = { sizeof(ti) }; - Tipper_ShowTip(wszBuf, &ti); - si->bHasToolTip = true; - } - KillTimer(hwnd, idEvent); -} - -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubNicklistProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - if (pDlg != nullptr) - return pDlg->WndProc_Nicklist(msg, wParam, lParam); - - return mir_callNextSubclass(hwnd, stubNicklistProc, msg, wParam, lParam); -} - -LRESULT CSrmmBaseDialog::WndProc_Nicklist(UINT msg, WPARAM wParam, LPARAM lParam) -{ - RECT rc; - - switch (msg) { - case WM_MEASUREITEM: - { - MEASUREITEMSTRUCT *mis = (MEASUREITEMSTRUCT *)lParam; - if (mis->CtlType == ODT_MENU) - return Menu_MeasureItem(lParam); - } - return FALSE; - - case WM_DRAWITEM: - { - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; - if (dis->CtlType == ODT_MENU) - return Menu_DrawItem(lParam); - } - return FALSE; - - case WM_MOUSEMOVE: - RECT clientRect; - { - bool bTooltipExists = ServiceExists(MS_TIPPER_HIDETIP); - - POINT pt = { LOWORD(lParam), HIWORD(lParam) }; - GetClientRect(m_nickList.GetHwnd(), &clientRect); - if (PtInRect(&clientRect, pt)) { - // hit test item under mouse - uint32_t nItemUnderMouse = m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, lParam); - if (HIWORD(nItemUnderMouse) == 1) - nItemUnderMouse = (uint32_t)(-1); - else - nItemUnderMouse &= 0xFFFF; - - if (bTooltipExists) { - if ((int)nItemUnderMouse == m_si->currentHovered) - break; - m_si->currentHovered = (int)nItemUnderMouse; - - KillTimer(m_nickList.GetHwnd(), 1); - - if (m_si->bHasToolTip) { - Tipper_Hide(); - m_si->bHasToolTip = false; - } - - if (nItemUnderMouse != -1) - SetTimer(m_nickList.GetHwnd(), (UINT_PTR)m_si, 450, ChatTimerProc); - } - else ProcessNickListHovering(m_nickList.GetHwnd(), (int)nItemUnderMouse, m_si); - } - else { - if (bTooltipExists) { - KillTimer(m_nickList.GetHwnd(), 1); - if (m_si->bHasToolTip) { - Tipper_Hide(); - m_si->bHasToolTip = false; - } - } - else ProcessNickListHovering(m_nickList.GetHwnd(), -1, nullptr); - } - } - break; - - case WM_ERASEBKGND: - { - HDC dc = (HDC)wParam; - if (dc == nullptr) - break; - - int nUsers = m_si->getUserList().getCount(); - - int index = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); - if (index == LB_ERR || nUsers <= 0) - break; - - int height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0); - if (height == LB_ERR) - break; - - GetClientRect(m_nickList.GetHwnd(), &rc); - - int items = nUsers - index; - if (rc.bottom - rc.top > items * height) { - rc.top = items * height; - FillRect(dc, &rc, g_chatApi.hListBkgBrush); - } - } - return 1; - - case WM_CONTEXTMENU: - POINT pt; - { - int height = 0; - pt.x = GET_X_LPARAM(lParam); - pt.y = GET_Y_LPARAM(lParam); - if (pt.x == -1 && pt.y == -1) { - int index = m_nickList.GetCurSel(); - int top = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); - height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0); - pt.x = 4; - pt.y = (index - top)*height + 1; - } - else ScreenToClient(m_nickList.GetHwnd(), &pt); - - int item = LOWORD(m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y))); - USERINFO *ui = g_chatApi.SM_GetUserFromIndex(m_si->ptszID, m_si->pszModule, item); - if (ui != nullptr) { - if (pt.x == -1 && pt.y == -1) - pt.y += height - 4; - ClientToScreen(m_nickList.GetHwnd(), &pt); - - RunUserMenu(m_nickList.GetHwnd(), ui, pt); - return TRUE; - } - } - break; - } - - return mir_callNextSubclass(m_nickList.GetHwnd(), stubNicklistProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CSrmmBaseDialog::OnInitDialog() -{ - WindowList_Add(g_hWindowList, m_hwnd, m_hContact); - SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this); - - m_pLog = Srmm_GetLogWindow((CMsgDialog*)this); - if (m_pLog->GetType() != 0) { // custom log type - HWND hwndLog = GetDlgItem(m_hwnd, IDC_SRMM_LOG); - EnableWindow(hwndLog, FALSE); - ShowWindow(hwndLog, SW_HIDE); - } - m_pLog->Attach(); - - SetWindowLongPtr(m_message.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - mir_subclassWindow(m_message.GetHwnd(), stubMessageProc); - m_message.SetReadOnly(false); - ::DragAcceptFiles(m_message.GetHwnd(), TRUE); - - if (isChat()) { - SetWindowLongPtr(m_nickList.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - mir_subclassWindow(m_nickList.GetHwnd(), stubNicklistProc); - } - - // three buttons below are initiated inside this call, so button creation must precede subclassing - Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON); - - mir_subclassWindow(m_btnFilter.GetHwnd(), Srmm_ButtonSubclassProc); - mir_subclassWindow(m_btnColor.GetHwnd(), Srmm_ButtonSubclassProc); - mir_subclassWindow(m_btnBkColor.GetHwnd(), Srmm_ButtonSubclassProc); - - LoadSettings(); - return true; -} - -void CSrmmBaseDialog::OnDestroy() -{ - m_pLog->Detach(); - delete m_pLog; - - WindowList_Remove(g_hWindowList, m_hwnd); - - SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0); - mir_unsubclassWindow(m_message.GetHwnd(), stubMessageProc); - mir_unsubclassWindow(m_nickList.GetHwnd(), stubNicklistProc); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CSrmmBaseDialog::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_COMMAND: - if (!lParam && Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, m_hContact)) - return 0; - - if (wParam >= MIN_CBUTTONID && wParam <= MAX_CBUTTONID) { - Srmm_ClickToolbarIcon(m_hContact, wParam, m_hwnd, 0); - return 0; - } - break; - - case WM_ACTIVATE: - if (m_si && LOWORD(wParam) == WA_INACTIVE) { - m_si->wState &= ~GC_EVENT_HIGHLIGHT; - m_si->wState &= ~STATE_TALK; - } - break; - - case WM_CBD_RECREATE: - Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON); - break; - - case WM_NOTIFY: - LPNMHDR hdr = (LPNMHDR)lParam; - if (hdr->hwndFrom == m_pLog->GetHwnd()) - m_pLog->Notify(wParam, lParam); - break; - } - - return CDlgBase::DlgProc(msg, wParam, lParam); -} - -void CSrmmBaseDialog::AddLog() -{ - if (m_si->pLogEnd) - m_pLog->LogEvents(m_si->pLog, false); - else - m_pLog->Clear(); -} - -bool CSrmmBaseDialog::AllowTyping() const -{ - return isChat() ? m_si->iType != GCW_SERVER : true; -} - -void CSrmmBaseDialog::ClearLog() -{ - m_pLog->Clear(); -} - -void CSrmmBaseDialog::UpdateOptions() -{ - MODULEINFO *mi = m_si->pMI; - EnableWindow(m_btnBold.GetHwnd(), mi->bBold); - EnableWindow(m_btnItalic.GetHwnd(), mi->bItalics); - EnableWindow(m_btnUnderline.GetHwnd(), mi->bUnderline); - EnableWindow(m_btnColor.GetHwnd(), mi->bColor); - EnableWindow(m_btnBkColor.GetHwnd(), mi->bBkgColor); - if (m_si->iType == GCW_CHATROOM) - EnableWindow(m_btnChannelMgr.GetHwnd(), mi->bChanMgr); - - Resize(); - RedrawLog2(m_si); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void RedrawLog2(SESSION_INFO *si) -{ - si->LastTime = 0; - if (si->pLog) - si->pDlg->log()->LogEvents(si->pLogEnd, TRUE); -} - -static void __cdecl phase2(SESSION_INFO *si) -{ - Sleep(30); - if (si && si->pDlg) - RedrawLog2(si); -} - -void CSrmmBaseDialog::RedrawLog() -{ - m_si->LastTime = 0; - if (m_si->pLog) { - LOGINFO *pLog = m_si->pLog; - if (m_si->iEventCount > 60) { - int index = 0; - while (index < 59) { - if (pLog->next == nullptr) - break; - - pLog = pLog->next; - if (m_si->iType != GCW_CHATROOM || !m_bFilterEnabled || (m_iLogFilterFlags & pLog->iType) != 0) - index++; - } - m_pLog->LogEvents(pLog, true); - mir_forkThread<SESSION_INFO>(phase2, m_si); - } - else m_pLog->LogEvents(m_si->pLogEnd, true); - } - else ClearLog(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CSrmmBaseDialog::onClick_Color(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwEffects = 0; - cf.dwMask = CFM_COLOR; - - if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) { - if (!g_chatApi.bRightClickFilter) { - ShowColorChooser(pButton->GetCtrlId()); - return; - } - if (m_bFGSet) - cf.crTextColor = m_iFG; - } - else cf.crTextColor = m_clrInputFG; - - m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); -} - -void CSrmmBaseDialog::onClick_BkColor(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwEffects = 0; - cf.dwMask = CFM_BACKCOLOR; - - if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) { - if (!g_chatApi.bRightClickFilter) { - ShowColorChooser(pButton->GetCtrlId()); - return; - } - if (m_bBGSet) - cf.crBackColor = m_iBG; - } - else cf.crBackColor = m_clrInputBG; - - m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); -} - -void CSrmmBaseDialog::onClick_BIU(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE; - cf.dwEffects = 0; - - if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_BOLD)) - cf.dwEffects |= CFE_BOLD; - if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_ITALICS)) - cf.dwEffects |= CFE_ITALIC; - if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_UNDERLINE)) - cf.dwEffects |= CFE_UNDERLINE; - m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); -} - -void CSrmmBaseDialog::onClick_History(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - if (m_si != nullptr) - ShellExecute(m_hwnd, nullptr, g_chatApi.GetChatLogsFilename(m_si, 0), nullptr, nullptr, SW_SHOW); - else - CallService(MS_HISTORY_SHOWCONTACTHISTORY, m_hContact, 0); -} - -void CSrmmBaseDialog::onClick_ChanMgr(CCtrlButton *pButton) -{ - if (pButton->Enabled()) - Chat_DoEventHook(m_si, GC_USER_CHANMGR, nullptr, nullptr, 0); -} - -void CSrmmBaseDialog::onDblClick_List(CCtrlListBox *pList) -{ - TVHITTESTINFO hti; - hti.pt.x = (short)LOWORD(GetMessagePos()); - hti.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(pList->GetHwnd(), &hti.pt); - - int item = LOWORD(pList->SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(hti.pt.x, hti.pt.y))); - USERINFO *ui = g_chatApi.UM_FindUserFromIndex(m_si, item); - if (ui == nullptr) - return; - - bool bShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - if (Chat::bDoubleClick4Privat ? bShift : !bShift) { - int selStart = LOWORD(m_message.SendMsg(EM_GETSEL, 0, 0)); - CMStringW tszName(ui->pszNick); - if (selStart == 0) - tszName.AppendChar(':'); - tszName.AppendChar(' '); - - m_message.SendMsg(EM_REPLACESEL, FALSE, (LPARAM)tszName.GetString()); - PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); - SetFocus(m_message.GetHwnd()); - } - else Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -extern HANDLE hHookSrmmEvent; - -int CSrmmBaseDialog::NotifyEvent(int code) -{ - if (m_hContact == 0 && m_hwnd == nullptr) - return -1; - - MessageWindowEventData mwe = {}; - mwe.hContact = m_hContact; - mwe.hwndWindow = m_hwnd; - mwe.uType = code; - mwe.uFlags = MSG_WINDOW_UFLAG_MSG_BOTH; - mwe.hwndInput = m_message.GetHwnd(); - mwe.hwndLog = m_pLog->GetHwnd(); - return ::NotifyEventHooks(hHookSrmmEvent, 0, (LPARAM)&mwe); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CSrmmBaseDialog::ProcessFileDrop(HDROP hDrop, MCONTACT hContact) -{ - if (PasteFilesAsURL(hDrop)) - return true; - - return ::ProcessFileDrop(hDrop, hContact); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// If enabled pastes droped files as list of URL of file:/// type -// Can be enabled/disabled by Chat/ShiftDropFilePasteURL database parameter -// @param hDrop - Drop handle -// @return Returns true if processed here, returns false if should be processed elsewhere - -bool CSrmmBaseDialog::PasteFilesAsURL(HDROP hDrop) -{ - bool isShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - if (db_get_b(0, CHAT_MODULE, "ShiftDropFilePasteURL", 1) == 0 || !isShift) // hidden setting: Chat/ShiftDropFilePasteURL - return false; - - int fileCount = DragQueryFileW(hDrop, -1, nullptr, 0); - if (fileCount == 0) - return true; - - CMStringW pasteString(L" "); - for (int i = 0; i < fileCount; i++) { - wchar_t szFilename[MAX_PATH]; - if (DragQueryFileW(hDrop, i, szFilename, _countof(szFilename))) { - CMStringW fileString(L"file:///"); - fileString.Append(szFilename); - fileString.Replace(L"%", L"%25"); - fileString.Replace(L" ", L"%20"); - fileString.Append((i != fileCount - 1) ? L"\r\n" : L" "); - pasteString += fileString; - } - } - - m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pasteString.c_str()); - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CSrmmBaseDialog::ProcessHotkeys(int key, bool isShift, bool isCtrl, bool isAlt) -{ - // Esc (close tab) - if (key == VK_ESCAPE && !isShift && !isCtrl && !isAlt) { - CloseTab(); - return true; - } - - if (isCtrl && !isAlt) { - switch (key) { - case VK_SPACE: // ctrl-space (paste clean text) - m_btnBold.Push(false); m_btnBold.Click(); - m_btnItalic.Push(false); m_btnItalic.Click(); - m_btnUnderline.Push(false); m_btnUnderline.Click(); - - m_btnColor.Push(false); m_btnColor.Click(); - m_btnBkColor.Push(false); m_btnBkColor.Click(); - return true; - - case 0x42: // ctrl-b (bold) - m_btnBold.Push(!m_btnBold.IsPushed()); - m_btnBold.Click(); - return true; - - case 0x48: // ctrl-h (history) - m_btnHistory.Click(); - return true; - - case 0x49: // ctrl-i (italics) - m_btnItalic.Push(!m_btnItalic.IsPushed()); - m_btnItalic.Click(); - return true; - - case 0x4b: // ctrl-k (text color) - m_btnColor.Push(!m_btnColor.IsPushed()); - m_btnColor.Click(); - return true; - - case 0x4c: // ctrl-l (back color) - m_btnBkColor.Push(!m_btnBkColor.IsPushed()); - m_btnBkColor.Click(); - return true; - - case 0x55: // ctrl-u (underlining) - m_btnUnderline.Push(!m_btnUnderline.IsPushed()); - m_btnUnderline.Click(); - return true; - - case VK_F4: // ctrl-F4 - CloseTab(); - return true; - } - } - - return false; -} - -void CSrmmBaseDialog::RefreshButtonStatus() -{ - if (m_si == nullptr) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_BACKCOLOR | CFM_COLOR; - m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); - - if (m_si->pMI->bColor) { - bool bState = m_btnColor.IsPushed(); - if (!bState && cf.crTextColor != m_clrInputFG) - m_btnColor.Push(true); - else if (bState && cf.crTextColor == m_clrInputFG) - m_btnColor.Push(false); - } - - if (m_si->pMI->bBkgColor) { - bool bState = m_btnBkColor.IsPushed(); - if (!bState && cf.crBackColor != m_clrInputBG) - m_btnBkColor.Push(true); - else if (bState && cf.crBackColor == m_clrInputBG) - m_btnBkColor.Push(false); - } - - if (m_si->pMI->bBold) { - bool bState = m_btnBold.IsPushed(); - UINT u2 = cf.dwEffects & CFE_BOLD; - if (!bState && u2 != 0) - m_btnBold.Push(true); - else if (bState && u2 == 0) - m_btnBold.Push(false); - } - - if (m_si->pMI->bItalics) { - bool bState = m_btnItalic.IsPushed(); - UINT u2 = cf.dwEffects & CFE_ITALIC; - if (!bState && u2 != 0) - m_btnItalic.Push(true); - else if (bState && u2 == 0) - m_btnItalic.Push(false); - } - - if (m_si->pMI->bUnderline) { - bool bState = m_btnUnderline.IsPushed(); - UINT u2 = cf.dwEffects & CFE_UNDERLINE; - if (!bState && u2 != 0) - m_btnUnderline.Push(true); - else if (bState && u2 == 0) - m_btnUnderline.Push(false); - } -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#include "chat.h"
+#include "resource.h"
+#include "skin.h"
+#include <m_history.h>
+
+CSrmmBaseDialog::CSrmmBaseDialog(CMPluginBase &pPlugin, int idDialog, SESSION_INFO *si) :
+ CDlgBase(pPlugin, idDialog),
+ timerFlash(this, 1),
+ timerType(this, 2),
+
+ m_message(this, IDC_SRMM_MESSAGE),
+ m_nickList(this, IDC_SRMM_NICKLIST),
+
+ m_btnOk(this, IDOK),
+ m_btnFilter(this, IDC_SRMM_FILTER),
+ m_btnHistory(this, IDC_SRMM_HISTORY),
+ m_btnNickList(this, IDC_SRMM_SHOWNICKLIST),
+ m_btnChannelMgr(this, IDC_SRMM_CHANMGR),
+
+ m_btnColor(this, IDC_SRMM_COLOR),
+ m_btnBkColor(this, IDC_SRMM_BKGCOLOR),
+ m_btnBold(this, IDC_SRMM_BOLD),
+
+ m_btnItalic(this, IDC_SRMM_ITALICS),
+ m_btnUnderline(this, IDC_SRMM_UNDERLINE),
+
+ m_si(si),
+ m_hContact(0),
+ m_clrInputBG(GetSysColor(COLOR_WINDOW))
+{
+ m_bFilterEnabled = db_get_b(0, CHAT_MODULE, "FilterEnabled", 0) != 0;
+ m_bNicklistEnabled = db_get_b(0, CHAT_MODULE, "ShowNicklist", 1) != 0;
+ m_iLogFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", 0x03E0);
+
+ m_btnColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_Color);
+ m_btnBkColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BkColor);
+ m_btnBold.OnClick = m_btnItalic.OnClick = m_btnUnderline.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BIU);
+
+ m_btnHistory.OnClick = Callback(this, &CSrmmBaseDialog::onClick_History);
+ m_btnChannelMgr.OnClick = Callback(this, &CSrmmBaseDialog::onClick_ChanMgr);
+
+ m_nickList.OnDblClick = Callback(this, &CMsgDialog::onDblClick_List);
+
+ if (si) {
+ m_hContact = si->hContact;
+
+ if (si->pMI->bColor) {
+ m_iFG = 4;
+ m_bFGSet = true;
+ }
+ if (si->pMI->bBkgColor) {
+ m_iBG = 2;
+ m_bBGSet = true;
+ }
+ }
+}
+
+void CSrmmBaseDialog::RunUserMenu(HWND hwndOwner, USERINFO *ui, const POINT &pt)
+{
+ HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_USERMENU));
+ HMENU hSubMenu = GetSubMenu(hMenu, 0);
+ TranslateMenu(hSubMenu);
+
+ USERINFO uinew;
+ memcpy(&uinew, ui, sizeof(USERINFO));
+
+ wchar_t szTemp[50];
+ if (uinew.pszNick)
+ mir_snwprintf(szTemp, TranslateT("&Message %s"), uinew.pszNick);
+ else
+ mir_wstrncpy(szTemp, TranslateT("&Message"), _countof(szTemp) - 1);
+
+ if (mir_wstrlen(szTemp) > 40)
+ mir_wstrncpy(szTemp + 40, L"...", 4);
+ ModifyMenu(hMenu, 0, MF_STRING | MF_BYPOSITION, IDM_SENDMESSAGE, szTemp);
+
+ UINT uID = Chat_CreateMenu(hwndOwner, hSubMenu, pt, m_si, uinew.pszUID);
+ switch (uID) {
+ case 0:
+ break;
+
+ case IDM_SENDMESSAGE:
+ Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0);
+ break;
+
+ default:
+ Chat_DoEventHook(m_si, GC_USER_NICKLISTMENU, ui, nullptr, uID);
+ break;
+ }
+ DestroyMenu(hMenu);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK Srmm_ButtonSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_RBUTTONUP:
+ if (g_chatApi.bRightClickFilter) {
+ CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
+ if (pDlg == nullptr)
+ break;
+
+ switch (GetDlgCtrlID(hwnd)) {
+ case IDC_SRMM_FILTER:
+ pDlg->ShowFilterMenu();
+ break;
+
+ case IDC_SRMM_COLOR:
+ pDlg->ShowColorChooser(IDC_SRMM_COLOR);
+ break;
+
+ case IDC_SRMM_BKGCOLOR:
+ pDlg->ShowColorChooser(IDC_SRMM_BKGCOLOR);
+ break;
+ }
+ }
+ break;
+ }
+
+ return mir_callNextSubclass(hwnd, Srmm_ButtonSubclassProc, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
+ if (pDlg != nullptr)
+ return pDlg->WndProc_Message(msg, wParam, lParam);
+
+ return mir_callNextSubclass(hwnd, stubMessageProc, msg, wParam, lParam);
+}
+
+LRESULT CSrmmBaseDialog::WndProc_Message(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_SETCURSOR:
+ if (m_bInMenu) {
+ SetCursor(LoadCursor(nullptr, IDC_ARROW));
+ return TRUE;
+ }
+ break;
+
+ case WM_CHAR:
+ switch (wParam) {
+ case 0x02: // ctrl+B
+ if (m_btnBold.Enabled())
+ return 1;
+ break;
+ case 0x09: // ctrl+I
+ if (m_btnItalic.Enabled())
+ return 1;
+ break;
+ case 0x15: // ctrl+U
+ if (m_btnUnderline.Enabled())
+ return 1;
+ break;
+ }
+ break;
+
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ if (wParam == VK_BACK)
+ if (m_message.GetRichTextLength() == 0)
+ return 1;
+
+ MSG tmp = { m_hwnd, msg, wParam, lParam };
+ if (Hotkey_Check(&tmp, g_pszHotkeySection) == 100) {
+ if (!(GetWindowLongPtr(m_message.GetHwnd(), GWL_STYLE) & ES_READONLY)) {
+ PostMessage(m_hwnd, WM_COMMAND, IDOK, 0);
+ return true;
+ }
+ }
+ }
+
+ LRESULT res = mir_callNextSubclass(m_message.GetHwnd(), stubMessageProc, msg, wParam, lParam);
+ switch (msg) {
+ case WM_GETDLGCODE:
+ return res & ~DLGC_HASSETSEL;
+
+ case WM_KEYUP:
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ RefreshButtonStatus();
+ break;
+
+ case WM_KEYDOWN:
+ if ((GetKeyState(VK_CONTROL) & 0x8000) && wParam == 'V' || (GetKeyState(VK_SHIFT) & 0x8000) && wParam == VK_INSERT) {
+ if (IsClipboardFormatAvailable(CF_HDROP)) {
+ m_message.SendMsg(WM_PASTE, 0, 0);
+ return 0;
+ }
+ }
+
+ __fallthrough;
+
+ case WM_SYSKEYDOWN:
+ if (!(GetKeyState(VK_RMENU) & 0x8000)) {
+ MSG message = { m_hwnd, msg, wParam, lParam };
+ LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION);
+ if (iButtonFrom) {
+ Srmm_ProcessToolbarHotkey(m_hContact, iButtonFrom, m_hwnd);
+ return TRUE;
+ }
+ }
+ break;
+ }
+
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// process mouse - hovering for the nickname list.fires events so the protocol can
+// show the userinfo - tooltip.
+
+static void ProcessNickListHovering(HWND hwnd, int hoveredItem, SESSION_INFO *parentdat)
+{
+ static int currentHovered = -1;
+ static HWND hwndToolTip = nullptr;
+ static HWND oldParent = nullptr;
+
+ if (hoveredItem == currentHovered)
+ return;
+
+ currentHovered = hoveredItem;
+
+ if (oldParent != hwnd && hwndToolTip) {
+ SendMessage(hwndToolTip, TTM_DELTOOL, 0, 0);
+ DestroyWindow(hwndToolTip);
+ hwndToolTip = nullptr;
+ }
+
+ if (hoveredItem == -1) {
+ SendMessage(hwndToolTip, TTM_ACTIVATE, 0, 0);
+ return;
+ }
+
+ bool bNewTip = false;
+ if (!hwndToolTip) {
+ bNewTip = true;
+ hwndToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, nullptr,
+ WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ hwnd, nullptr, g_plugin.getInst(), nullptr);
+ }
+
+ RECT clientRect;
+ GetClientRect(hwnd, &clientRect);
+
+ TOOLINFO ti = { sizeof(ti) };
+ ti.uFlags = TTF_SUBCLASS;
+ ti.hinst = g_plugin.getInst();
+ ti.hwnd = hwnd;
+ ti.uId = 1;
+ ti.rect = clientRect;
+
+ CMStringW wszBuf;
+
+ USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(parentdat->ptszID, parentdat->pszModule, currentHovered);
+ if (ui1) {
+ if (ProtoServiceExists(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) {
+ wchar_t *p = (wchar_t*)CallProtoService(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)parentdat->ptszID, (LPARAM)ui1->pszUID);
+ if (p != nullptr) {
+ wszBuf = p;
+ mir_free(p);
+ }
+ }
+
+ if (wszBuf.IsEmpty())
+ wszBuf.Format(L"%s: %s\r\n%s: %s\r\n%s: %s",
+ TranslateT("Nickname"), ui1->pszNick,
+ TranslateT("Unique ID"), ui1->pszUID,
+ TranslateT("Status"), g_chatApi.TM_WordToString(parentdat->pStatuses, ui1->Status));
+ ti.lpszText = wszBuf.GetBuffer();
+ }
+
+ SendMessage(hwndToolTip, bNewTip ? TTM_ADDTOOL : TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
+ SendMessage(hwndToolTip, TTM_ACTIVATE, (ti.lpszText != nullptr), 0);
+ SendMessage(hwndToolTip, TTM_SETMAXTIPWIDTH, 0, 400);
+}
+
+static void CALLBACK ChatTimerProc(HWND hwnd, UINT, UINT_PTR idEvent, DWORD)
+{
+ SESSION_INFO *si = (SESSION_INFO*)idEvent;
+
+ POINT pt;
+ GetCursorPos(&pt);
+ ScreenToClient(hwnd, &pt);
+
+ uint32_t nItemUnderMouse = (uint32_t)SendMessage(hwnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y));
+ if (HIWORD(nItemUnderMouse) == 1)
+ nItemUnderMouse = (uint32_t)(-1);
+ else
+ nItemUnderMouse &= 0xFFFF;
+ if (((int)nItemUnderMouse != si->currentHovered) || (nItemUnderMouse == -1)) {
+ KillTimer(hwnd, idEvent);
+ return;
+ }
+
+ USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(si->ptszID, si->pszModule, si->currentHovered);
+ if (ui1) {
+ CMStringW wszBuf;
+ if (ProtoServiceExists(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) {
+ wchar_t *p = (wchar_t*)CallProtoService(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)si->ptszID, (LPARAM)ui1->pszUID);
+ if (p) {
+ wszBuf = p;
+ mir_free(p);
+ }
+ }
+ if (wszBuf.IsEmpty())
+ wszBuf.Format(L"<b>%s:</b>\t%s\n<b>%s:</b>\t%s\n<b>%s:</b>\t%s",
+ TranslateT("Nick"), ui1->pszNick,
+ TranslateT("Unique ID"), ui1->pszUID,
+ TranslateT("Status"), g_chatApi.TM_WordToString(si->pStatuses, ui1->Status));
+
+ CLCINFOTIP ti = { sizeof(ti) };
+ Tipper_ShowTip(wszBuf, &ti);
+ si->bHasToolTip = true;
+ }
+ KillTimer(hwnd, idEvent);
+}
+
+EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubNicklistProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
+ if (pDlg != nullptr)
+ return pDlg->WndProc_Nicklist(msg, wParam, lParam);
+
+ return mir_callNextSubclass(hwnd, stubNicklistProc, msg, wParam, lParam);
+}
+
+LRESULT CSrmmBaseDialog::WndProc_Nicklist(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc;
+
+ switch (msg) {
+ case WM_MEASUREITEM:
+ {
+ MEASUREITEMSTRUCT *mis = (MEASUREITEMSTRUCT *)lParam;
+ if (mis->CtlType == ODT_MENU)
+ return Menu_MeasureItem(lParam);
+ }
+ return FALSE;
+
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam;
+ if (dis->CtlType == ODT_MENU)
+ return Menu_DrawItem(lParam);
+ }
+ return FALSE;
+
+ case WM_MOUSEMOVE:
+ RECT clientRect;
+ {
+ bool bTooltipExists = ServiceExists(MS_TIPPER_HIDETIP);
+
+ POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+ GetClientRect(m_nickList.GetHwnd(), &clientRect);
+ if (PtInRect(&clientRect, pt)) {
+ // hit test item under mouse
+ uint32_t nItemUnderMouse = m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, lParam);
+ if (HIWORD(nItemUnderMouse) == 1)
+ nItemUnderMouse = (uint32_t)(-1);
+ else
+ nItemUnderMouse &= 0xFFFF;
+
+ if (bTooltipExists) {
+ if ((int)nItemUnderMouse == m_si->currentHovered)
+ break;
+ m_si->currentHovered = (int)nItemUnderMouse;
+
+ KillTimer(m_nickList.GetHwnd(), 1);
+
+ if (m_si->bHasToolTip) {
+ Tipper_Hide();
+ m_si->bHasToolTip = false;
+ }
+
+ if (nItemUnderMouse != -1)
+ SetTimer(m_nickList.GetHwnd(), (UINT_PTR)m_si, 450, ChatTimerProc);
+ }
+ else ProcessNickListHovering(m_nickList.GetHwnd(), (int)nItemUnderMouse, m_si);
+ }
+ else {
+ if (bTooltipExists) {
+ KillTimer(m_nickList.GetHwnd(), 1);
+ if (m_si->bHasToolTip) {
+ Tipper_Hide();
+ m_si->bHasToolTip = false;
+ }
+ }
+ else ProcessNickListHovering(m_nickList.GetHwnd(), -1, nullptr);
+ }
+ }
+ break;
+
+ case WM_ERASEBKGND:
+ {
+ HDC dc = (HDC)wParam;
+ if (dc == nullptr)
+ break;
+
+ int nUsers = m_si->getUserList().getCount();
+
+ int index = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0);
+ if (index == LB_ERR || nUsers <= 0)
+ break;
+
+ int height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0);
+ if (height == LB_ERR)
+ break;
+
+ GetClientRect(m_nickList.GetHwnd(), &rc);
+
+ int items = nUsers - index;
+ if (rc.bottom - rc.top > items * height) {
+ rc.top = items * height;
+ FillRect(dc, &rc, g_chatApi.hListBkgBrush);
+ }
+ }
+ return 1;
+
+ case WM_CONTEXTMENU:
+ POINT pt;
+ {
+ int height = 0;
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+ if (pt.x == -1 && pt.y == -1) {
+ int index = m_nickList.GetCurSel();
+ int top = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0);
+ height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0);
+ pt.x = 4;
+ pt.y = (index - top)*height + 1;
+ }
+ else ScreenToClient(m_nickList.GetHwnd(), &pt);
+
+ int item = LOWORD(m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y)));
+ USERINFO *ui = g_chatApi.SM_GetUserFromIndex(m_si->ptszID, m_si->pszModule, item);
+ if (ui != nullptr) {
+ if (pt.x == -1 && pt.y == -1)
+ pt.y += height - 4;
+ ClientToScreen(m_nickList.GetHwnd(), &pt);
+
+ RunUserMenu(m_nickList.GetHwnd(), ui, pt);
+ return TRUE;
+ }
+ }
+ break;
+ }
+
+ return mir_callNextSubclass(m_nickList.GetHwnd(), stubNicklistProc, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CSrmmBaseDialog::OnInitDialog()
+{
+ WindowList_Add(g_hWindowList, m_hwnd, m_hContact);
+ SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this);
+
+ m_pLog = Srmm_GetLogWindow((CMsgDialog*)this);
+ if (m_pLog->GetType() != 0) { // custom log type
+ HWND hwndLog = GetDlgItem(m_hwnd, IDC_SRMM_LOG);
+ EnableWindow(hwndLog, FALSE);
+ ShowWindow(hwndLog, SW_HIDE);
+ }
+ m_pLog->Attach();
+
+ SetWindowLongPtr(m_message.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_message.GetHwnd(), stubMessageProc);
+ m_message.SetReadOnly(false);
+ ::DragAcceptFiles(m_message.GetHwnd(), TRUE);
+
+ if (isChat()) {
+ SetWindowLongPtr(m_nickList.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_nickList.GetHwnd(), stubNicklistProc);
+ }
+
+ // three buttons below are initiated inside this call, so button creation must precede subclassing
+ Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON);
+
+ mir_subclassWindow(m_btnFilter.GetHwnd(), Srmm_ButtonSubclassProc);
+ mir_subclassWindow(m_btnColor.GetHwnd(), Srmm_ButtonSubclassProc);
+ mir_subclassWindow(m_btnBkColor.GetHwnd(), Srmm_ButtonSubclassProc);
+
+ LoadSettings();
+ return true;
+}
+
+void CSrmmBaseDialog::OnDestroy()
+{
+ m_pLog->Detach();
+ delete m_pLog;
+
+ WindowList_Remove(g_hWindowList, m_hwnd);
+
+ SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0);
+ mir_unsubclassWindow(m_message.GetHwnd(), stubMessageProc);
+ mir_unsubclassWindow(m_nickList.GetHwnd(), stubNicklistProc);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CSrmmBaseDialog::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_COMMAND:
+ if (!lParam && Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, m_hContact))
+ return 0;
+
+ if (wParam >= MIN_CBUTTONID && wParam <= MAX_CBUTTONID) {
+ Srmm_ClickToolbarIcon(m_hContact, wParam, m_hwnd, 0);
+ return 0;
+ }
+ break;
+
+ case WM_ACTIVATE:
+ if (m_si && LOWORD(wParam) == WA_INACTIVE) {
+ m_si->wState &= ~GC_EVENT_HIGHLIGHT;
+ m_si->wState &= ~STATE_TALK;
+ }
+ break;
+
+ case WM_CBD_RECREATE:
+ Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON);
+ break;
+
+ case WM_NOTIFY:
+ LPNMHDR hdr = (LPNMHDR)lParam;
+ if (hdr->hwndFrom == m_pLog->GetHwnd())
+ m_pLog->Notify(wParam, lParam);
+ break;
+ }
+
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+}
+
+void CSrmmBaseDialog::AddLog()
+{
+ if (m_si->pLogEnd)
+ m_pLog->LogEvents(m_si->pLog, false);
+ else
+ m_pLog->Clear();
+}
+
+bool CSrmmBaseDialog::AllowTyping() const
+{
+ return isChat() ? m_si->iType != GCW_SERVER : true;
+}
+
+void CSrmmBaseDialog::ClearLog()
+{
+ m_pLog->Clear();
+}
+
+void CSrmmBaseDialog::UpdateOptions()
+{
+ MODULEINFO *mi = m_si->pMI;
+ EnableWindow(m_btnBold.GetHwnd(), mi->bBold);
+ EnableWindow(m_btnItalic.GetHwnd(), mi->bItalics);
+ EnableWindow(m_btnUnderline.GetHwnd(), mi->bUnderline);
+ EnableWindow(m_btnColor.GetHwnd(), mi->bColor);
+ EnableWindow(m_btnBkColor.GetHwnd(), mi->bBkgColor);
+ if (m_si->iType == GCW_CHATROOM)
+ EnableWindow(m_btnChannelMgr.GetHwnd(), mi->bChanMgr);
+
+ Resize();
+ RedrawLog2(m_si);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void RedrawLog2(SESSION_INFO *si)
+{
+ si->LastTime = 0;
+ if (si->pLog)
+ si->pDlg->log()->LogEvents(si->pLogEnd, TRUE);
+}
+
+static void __cdecl phase2(SESSION_INFO *si)
+{
+ Sleep(30);
+ if (si && si->pDlg)
+ RedrawLog2(si);
+}
+
+void CSrmmBaseDialog::RedrawLog()
+{
+ m_si->LastTime = 0;
+ if (m_si->pLog) {
+ LOGINFO *pLog = m_si->pLog;
+ if (m_si->iEventCount > 60) {
+ int index = 0;
+ while (index < 59) {
+ if (pLog->next == nullptr)
+ break;
+
+ pLog = pLog->next;
+ if (m_si->iType != GCW_CHATROOM || !m_bFilterEnabled || (m_iLogFilterFlags & pLog->iType) != 0)
+ index++;
+ }
+ m_pLog->LogEvents(pLog, true);
+ mir_forkThread<SESSION_INFO>(phase2, m_si);
+ }
+ else m_pLog->LogEvents(m_si->pLogEnd, true);
+ }
+ else ClearLog();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSrmmBaseDialog::onClick_Color(CCtrlButton *pButton)
+{
+ if (!pButton->Enabled())
+ return;
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwEffects = 0;
+ cf.dwMask = CFM_COLOR;
+
+ if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) {
+ if (!g_chatApi.bRightClickFilter) {
+ ShowColorChooser(pButton->GetCtrlId());
+ return;
+ }
+ if (m_bFGSet)
+ cf.crTextColor = m_iFG;
+ }
+ else cf.crTextColor = m_clrInputFG;
+
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+}
+
+void CSrmmBaseDialog::onClick_BkColor(CCtrlButton *pButton)
+{
+ if (!pButton->Enabled())
+ return;
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwEffects = 0;
+ cf.dwMask = CFM_BACKCOLOR;
+
+ if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) {
+ if (!g_chatApi.bRightClickFilter) {
+ ShowColorChooser(pButton->GetCtrlId());
+ return;
+ }
+ if (m_bBGSet)
+ cf.crBackColor = m_iBG;
+ }
+ else cf.crBackColor = m_clrInputBG;
+
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+}
+
+void CSrmmBaseDialog::onClick_BIU(CCtrlButton *pButton)
+{
+ if (!pButton->Enabled())
+ return;
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
+ cf.dwEffects = 0;
+
+ if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_BOLD))
+ cf.dwEffects |= CFE_BOLD;
+ if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_ITALICS))
+ cf.dwEffects |= CFE_ITALIC;
+ if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_UNDERLINE))
+ cf.dwEffects |= CFE_UNDERLINE;
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+}
+
+void CSrmmBaseDialog::onClick_History(CCtrlButton *pButton)
+{
+ if (!pButton->Enabled())
+ return;
+
+ if (m_si != nullptr)
+ ShellExecute(m_hwnd, nullptr, g_chatApi.GetChatLogsFilename(m_si, 0), nullptr, nullptr, SW_SHOW);
+ else
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, m_hContact, 0);
+}
+
+void CSrmmBaseDialog::onClick_ChanMgr(CCtrlButton *pButton)
+{
+ if (pButton->Enabled())
+ Chat_DoEventHook(m_si, GC_USER_CHANMGR, nullptr, nullptr, 0);
+}
+
+void CSrmmBaseDialog::onDblClick_List(CCtrlListBox *pList)
+{
+ TVHITTESTINFO hti;
+ hti.pt.x = (short)LOWORD(GetMessagePos());
+ hti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(pList->GetHwnd(), &hti.pt);
+
+ int item = LOWORD(pList->SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(hti.pt.x, hti.pt.y)));
+ USERINFO *ui = g_chatApi.UM_FindUserFromIndex(m_si, item);
+ if (ui == nullptr)
+ return;
+
+ bool bShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
+ if (Chat::bDoubleClick4Privat ? bShift : !bShift) {
+ int selStart = LOWORD(m_message.SendMsg(EM_GETSEL, 0, 0));
+ CMStringW tszName(ui->pszNick);
+ if (selStart == 0)
+ tszName.AppendChar(':');
+ tszName.AppendChar(' ');
+
+ m_message.SendMsg(EM_REPLACESEL, FALSE, (LPARAM)tszName.GetString());
+ PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ SetFocus(m_message.GetHwnd());
+ }
+ else Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern HANDLE hHookSrmmEvent;
+
+int CSrmmBaseDialog::NotifyEvent(int code)
+{
+ if (m_hContact == 0 && m_hwnd == nullptr)
+ return -1;
+
+ MessageWindowEventData mwe = {};
+ mwe.hContact = m_hContact;
+ mwe.hwndWindow = m_hwnd;
+ mwe.uType = code;
+ mwe.uFlags = MSG_WINDOW_UFLAG_MSG_BOTH;
+ mwe.hwndInput = m_message.GetHwnd();
+ mwe.hwndLog = m_pLog->GetHwnd();
+ return ::NotifyEventHooks(hHookSrmmEvent, 0, (LPARAM)&mwe);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CSrmmBaseDialog::ProcessFileDrop(HDROP hDrop, MCONTACT hContact)
+{
+ if (PasteFilesAsURL(hDrop))
+ return true;
+
+ return ::ProcessFileDrop(hDrop, hContact);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// If enabled pastes droped files as list of URL of file:/// type
+// Can be enabled/disabled by Chat/ShiftDropFilePasteURL database parameter
+// @param hDrop - Drop handle
+// @return Returns true if processed here, returns false if should be processed elsewhere
+
+bool CSrmmBaseDialog::PasteFilesAsURL(HDROP hDrop)
+{
+ bool isShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
+ if (db_get_b(0, CHAT_MODULE, "ShiftDropFilePasteURL", 1) == 0 || !isShift) // hidden setting: Chat/ShiftDropFilePasteURL
+ return false;
+
+ int fileCount = DragQueryFileW(hDrop, -1, nullptr, 0);
+ if (fileCount == 0)
+ return true;
+
+ CMStringW pasteString(L" ");
+ for (int i = 0; i < fileCount; i++) {
+ wchar_t szFilename[MAX_PATH];
+ if (DragQueryFileW(hDrop, i, szFilename, _countof(szFilename))) {
+ CMStringW fileString(L"file:///");
+ fileString.Append(szFilename);
+ fileString.Replace(L"%", L"%25");
+ fileString.Replace(L" ", L"%20");
+ fileString.Append((i != fileCount - 1) ? L"\r\n" : L" ");
+ pasteString += fileString;
+ }
+ }
+
+ m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pasteString.c_str());
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CSrmmBaseDialog::ProcessHotkeys(int key, bool isShift, bool isCtrl, bool isAlt)
+{
+ // Esc (close tab)
+ if (key == VK_ESCAPE && !isShift && !isCtrl && !isAlt) {
+ CloseTab();
+ return true;
+ }
+
+ if (isCtrl && !isAlt) {
+ switch (key) {
+ case VK_SPACE: // ctrl-space (paste clean text)
+ m_btnBold.Push(false); m_btnBold.Click();
+ m_btnItalic.Push(false); m_btnItalic.Click();
+ m_btnUnderline.Push(false); m_btnUnderline.Click();
+
+ m_btnColor.Push(false); m_btnColor.Click();
+ m_btnBkColor.Push(false); m_btnBkColor.Click();
+ return true;
+
+ case 0x42: // ctrl-b (bold)
+ m_btnBold.Push(!m_btnBold.IsPushed());
+ m_btnBold.Click();
+ return true;
+
+ case 0x48: // ctrl-h (history)
+ m_btnHistory.Click();
+ return true;
+
+ case 0x49: // ctrl-i (italics)
+ m_btnItalic.Push(!m_btnItalic.IsPushed());
+ m_btnItalic.Click();
+ return true;
+
+ case 0x4b: // ctrl-k (text color)
+ m_btnColor.Push(!m_btnColor.IsPushed());
+ m_btnColor.Click();
+ return true;
+
+ case 0x4c: // ctrl-l (back color)
+ m_btnBkColor.Push(!m_btnBkColor.IsPushed());
+ m_btnBkColor.Click();
+ return true;
+
+ case 0x55: // ctrl-u (underlining)
+ m_btnUnderline.Push(!m_btnUnderline.IsPushed());
+ m_btnUnderline.Click();
+ return true;
+
+ case VK_F4: // ctrl-F4
+ CloseTab();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void CSrmmBaseDialog::RefreshButtonStatus()
+{
+ if (m_si == nullptr)
+ return;
+
+ CHARFORMAT2 cf;
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_BACKCOLOR | CFM_COLOR;
+ m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+
+ if (m_si->pMI->bColor) {
+ bool bState = m_btnColor.IsPushed();
+ if (!bState && cf.crTextColor != m_clrInputFG)
+ m_btnColor.Push(true);
+ else if (bState && cf.crTextColor == m_clrInputFG)
+ m_btnColor.Push(false);
+ }
+
+ if (m_si->pMI->bBkgColor) {
+ bool bState = m_btnBkColor.IsPushed();
+ if (!bState && cf.crBackColor != m_clrInputBG)
+ m_btnBkColor.Push(true);
+ else if (bState && cf.crBackColor == m_clrInputBG)
+ m_btnBkColor.Push(false);
+ }
+
+ if (m_si->pMI->bBold) {
+ bool bState = m_btnBold.IsPushed();
+ UINT u2 = cf.dwEffects & CFE_BOLD;
+ if (!bState && u2 != 0)
+ m_btnBold.Push(true);
+ else if (bState && u2 == 0)
+ m_btnBold.Push(false);
+ }
+
+ if (m_si->pMI->bItalics) {
+ bool bState = m_btnItalic.IsPushed();
+ UINT u2 = cf.dwEffects & CFE_ITALIC;
+ if (!bState && u2 != 0)
+ m_btnItalic.Push(true);
+ else if (bState && u2 == 0)
+ m_btnItalic.Push(false);
+ }
+
+ if (m_si->pMI->bUnderline) {
+ bool bState = m_btnUnderline.IsPushed();
+ UINT u2 = cf.dwEffects & CFE_UNDERLINE;
+ if (!bState && u2 != 0)
+ m_btnUnderline.Push(true);
+ else if (bState && u2 == 0)
+ m_btnUnderline.Push(false);
+ }
+}
diff --git a/src/mir_app/src/srmm_log.cpp b/src/mir_app/src/srmm_log.cpp index bc97e37023..d60636339d 100644 --- a/src/mir_app/src/srmm_log.cpp +++ b/src/mir_app/src/srmm_log.cpp @@ -1,196 +1,196 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// SRMM log container - -#include "stdafx.h" -#include "chat.h" - -struct LoggerClass -{ - LoggerClass(CMPlugin *p1, const char *p2, const wchar_t *p3, pfnSrmmLogCreator p4) : - pPlugin(p1), - szShortName(mir_strdup(p2)), - wszScreenName(mir_wstrdup(p3)), - pfnBuilder(p4) - {} - - CMPlugin *pPlugin; - ptrA szShortName; - ptrW wszScreenName; - pfnSrmmLogCreator pfnBuilder; -}; - -static OBJLIST<LoggerClass> g_arLogClasses(1, PtrKeySortT); - -static CMOption<uint8_t> g_bEnableCustomLogs("SRMM", "EnableCustomLogs", 0); - -///////////////////////////////////////////////////////////////////////////////////////// - -static bool sttEnableCustomLogs(CMsgDialog *pDlg) -{ - // always enable custom log viewers for private chats - if (!pDlg->isChat()) - return true; - - // if custom log viewers are disable, use build-in one - if (!g_bEnableCustomLogs) - return false; - - // check if custom viewers are forbidden for this particular account - auto *szProto = Proto_GetBaseAccountName(pDlg->m_hContact); - if (szProto) { - // hidden setting !!!!!!!! - CMStringA szProtoList(db_get_sm(0, "SRMM", "DisableCustomLogsForProto")); - - int iStart = 0; - while (true) { - auto forbiddenProto = szProtoList.Tokenize(",; ", iStart); - if (forbiddenProto.IsEmpty()) - break; - - if (forbiddenProto == szProto) - return false; - } - } - - // ok-ok, use that custom viewer - return true; -} - -CSrmmLogWindow* Srmm_GetLogWindow(CMsgDialog *pDlg) -{ - if (sttEnableCustomLogs(pDlg)) { - CMStringA szViewerName(db_get_sm(pDlg->m_hContact, SRMM_MODULE, "Logger")); - if (szViewerName.IsEmpty()) - szViewerName = db_get_sm(0, "SRMM", "Logger", "built-in"); - - for (auto &it : g_arLogClasses) - if (szViewerName == it->szShortName) - return it->pfnBuilder(*pDlg); - } - - for (auto &it : g_arLogClasses) - if (!mir_strcmp(it->szShortName, "built-in")) - return it->pfnBuilder(*pDlg); - - return nullptr; // shall never happen -} - -///////////////////////////////////////////////////////////////////////////////////////// -// options dialog - -static class CSrmmLogOptionsDlg *pDialog = nullptr; - -class CSrmmLogOptionsDlg : public CDlgBase -{ - CCtrlListBox m_list; - CCtrlCheck chkCustomLogs; - -public: - CSrmmLogOptionsDlg() : - CDlgBase(g_plugin, IDD_OPT_SRMMLOG), - m_list(this, IDC_LIST), - chkCustomLogs(this, IDC_ENABLE_CUSTOM) - { - CreateLink(chkCustomLogs, g_bEnableCustomLogs); - - m_list.OnSelChange = Callback(this, &CSrmmLogOptionsDlg::onChange_List); - } - - bool OnInitDialog() override - { - pDialog = this; - ptrA szCurr(db_get_sa(0, "SRMM", "Logger", "built-in")); - - for (auto &it : g_arLogClasses) { - int idx = m_list.AddString(TranslateW_LP(it->wszScreenName, it->pPlugin), LPARAM(it)); - if (!mir_strcmp(szCurr, it->szShortName)) - m_list.SetCurSel(idx); - } - - return true; - } - - bool OnApply() override - { - int idx = m_list.GetCurSel(); - if (idx == -1) - return false; - - auto *pLogger = (LoggerClass *)m_list.GetItemData(idx); - db_set_s(0, "SRMM", "Logger", pLogger->szShortName); - return true; - } - - void OnDestroy() override - { - pDialog = nullptr; - } - - void Rebuild() - { - m_list.ResetContent(); - OnInitDialog(); - } - - void onChange_List(CCtrlListBox *) - { - NotifyChange(); - } -}; - -void SrmmLogOptionsInit(WPARAM wParam) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 910000000; - odp.szGroup.a = LPGEN("Message sessions"); - odp.szTitle.a = LPGEN("Log viewer"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CSrmmLogOptionsDlg(); - g_plugin.addOptions(wParam, &odp); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HANDLE) RegisterSrmmLog(CMPlugin *pPlugin, const char *pszShortName, const wchar_t *pwszScreenName, pfnSrmmLogCreator fnBuilder) -{ - if (!pszShortName || !pwszScreenName || !fnBuilder) - return nullptr; - - auto *p = new LoggerClass(pPlugin, pszShortName, pwszScreenName, fnBuilder); - g_arLogClasses.insert(p); - - if (pDialog) - pDialog->Rebuild(); - return p; -} - -MIR_APP_DLL(void) UnregisterSrmmLog(HANDLE pLogger) -{ - g_arLogClasses.remove((LoggerClass *)pLogger); - - if (pDialog) - pDialog->Rebuild(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SRMM log container
+
+#include "stdafx.h"
+#include "chat.h"
+
+struct LoggerClass
+{
+ LoggerClass(CMPlugin *p1, const char *p2, const wchar_t *p3, pfnSrmmLogCreator p4) :
+ pPlugin(p1),
+ szShortName(mir_strdup(p2)),
+ wszScreenName(mir_wstrdup(p3)),
+ pfnBuilder(p4)
+ {}
+
+ CMPlugin *pPlugin;
+ ptrA szShortName;
+ ptrW wszScreenName;
+ pfnSrmmLogCreator pfnBuilder;
+};
+
+static OBJLIST<LoggerClass> g_arLogClasses(1, PtrKeySortT);
+
+static CMOption<uint8_t> g_bEnableCustomLogs("SRMM", "EnableCustomLogs", 0);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static bool sttEnableCustomLogs(CMsgDialog *pDlg)
+{
+ // always enable custom log viewers for private chats
+ if (!pDlg->isChat())
+ return true;
+
+ // if custom log viewers are disable, use build-in one
+ if (!g_bEnableCustomLogs)
+ return false;
+
+ // check if custom viewers are forbidden for this particular account
+ auto *szProto = Proto_GetBaseAccountName(pDlg->m_hContact);
+ if (szProto) {
+ // hidden setting !!!!!!!!
+ CMStringA szProtoList(db_get_sm(0, "SRMM", "DisableCustomLogsForProto"));
+
+ int iStart = 0;
+ while (true) {
+ auto forbiddenProto = szProtoList.Tokenize(",; ", iStart);
+ if (forbiddenProto.IsEmpty())
+ break;
+
+ if (forbiddenProto == szProto)
+ return false;
+ }
+ }
+
+ // ok-ok, use that custom viewer
+ return true;
+}
+
+CSrmmLogWindow* Srmm_GetLogWindow(CMsgDialog *pDlg)
+{
+ if (sttEnableCustomLogs(pDlg)) {
+ CMStringA szViewerName(db_get_sm(pDlg->m_hContact, SRMM_MODULE, "Logger"));
+ if (szViewerName.IsEmpty())
+ szViewerName = db_get_sm(0, "SRMM", "Logger", "built-in");
+
+ for (auto &it : g_arLogClasses)
+ if (szViewerName == it->szShortName)
+ return it->pfnBuilder(*pDlg);
+ }
+
+ for (auto &it : g_arLogClasses)
+ if (!mir_strcmp(it->szShortName, "built-in"))
+ return it->pfnBuilder(*pDlg);
+
+ return nullptr; // shall never happen
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// options dialog
+
+static class CSrmmLogOptionsDlg *pDialog = nullptr;
+
+class CSrmmLogOptionsDlg : public CDlgBase
+{
+ CCtrlListBox m_list;
+ CCtrlCheck chkCustomLogs;
+
+public:
+ CSrmmLogOptionsDlg() :
+ CDlgBase(g_plugin, IDD_OPT_SRMMLOG),
+ m_list(this, IDC_LIST),
+ chkCustomLogs(this, IDC_ENABLE_CUSTOM)
+ {
+ CreateLink(chkCustomLogs, g_bEnableCustomLogs);
+
+ m_list.OnSelChange = Callback(this, &CSrmmLogOptionsDlg::onChange_List);
+ }
+
+ bool OnInitDialog() override
+ {
+ pDialog = this;
+ ptrA szCurr(db_get_sa(0, "SRMM", "Logger", "built-in"));
+
+ for (auto &it : g_arLogClasses) {
+ int idx = m_list.AddString(TranslateW_LP(it->wszScreenName, it->pPlugin), LPARAM(it));
+ if (!mir_strcmp(szCurr, it->szShortName))
+ m_list.SetCurSel(idx);
+ }
+
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ int idx = m_list.GetCurSel();
+ if (idx == -1)
+ return false;
+
+ auto *pLogger = (LoggerClass *)m_list.GetItemData(idx);
+ db_set_s(0, "SRMM", "Logger", pLogger->szShortName);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ pDialog = nullptr;
+ }
+
+ void Rebuild()
+ {
+ m_list.ResetContent();
+ OnInitDialog();
+ }
+
+ void onChange_List(CCtrlListBox *)
+ {
+ NotifyChange();
+ }
+};
+
+void SrmmLogOptionsInit(WPARAM wParam)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 910000000;
+ odp.szGroup.a = LPGEN("Message sessions");
+ odp.szTitle.a = LPGEN("Log viewer");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new CSrmmLogOptionsDlg();
+ g_plugin.addOptions(wParam, &odp);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(HANDLE) RegisterSrmmLog(CMPlugin *pPlugin, const char *pszShortName, const wchar_t *pwszScreenName, pfnSrmmLogCreator fnBuilder)
+{
+ if (!pszShortName || !pwszScreenName || !fnBuilder)
+ return nullptr;
+
+ auto *p = new LoggerClass(pPlugin, pszShortName, pwszScreenName, fnBuilder);
+ g_arLogClasses.insert(p);
+
+ if (pDialog)
+ pDialog->Rebuild();
+ return p;
+}
+
+MIR_APP_DLL(void) UnregisterSrmmLog(HANDLE pLogger)
+{
+ g_arLogClasses.remove((LoggerClass *)pLogger);
+
+ if (pDialog)
+ pDialog->Rebuild();
+}
diff --git a/src/mir_app/src/srmm_log_hpp.cpp b/src/mir_app/src/srmm_log_hpp.cpp index 09fe9b3f4b..584ed539f9 100644 --- a/src/mir_app/src/srmm_log_hpp.cpp +++ b/src/mir_app/src/srmm_log_hpp.cpp @@ -1,227 +1,227 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// CHppLogWindow class - -#include "stdafx.h" -#include "chat.h" - -#define EVENTTYPE_STATUSCHANGE 25368 -#define EVENTTYPE_ERRMSG 25366 - -class CHppLogWindow : public CSrmmLogWindow -{ - HWND m_hwnd = nullptr; - -public: - CHppLogWindow(CMsgDialog &pDlg) : - CSrmmLogWindow(pDlg) - { - } - - void Attach() override - { - IEVIEWWINDOW ieWindow = {}; - ieWindow.iType = IEW_CREATE; - ieWindow.dwMode = IEWM_TABSRMM; - ieWindow.parent = m_pDlg.GetHwnd(); - ieWindow.cx = 10; - ieWindow.cy = 10; - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); - m_hwnd = ieWindow.hwnd; - } - - void Detach() override - { - IEVIEWWINDOW ieWindow = {}; - ieWindow.iType = IEW_DESTROY; - ieWindow.hwnd = m_hwnd; - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); - } - - ////////////////////////////////////////////////////////////////////////////////////// - - bool AtBottom() override - { - return false; - } - - void Clear() override - { - IEVIEWEVENT event = {}; - event.iType = IEE_CLEAR_LOG; - event.hwnd = m_hwnd; - CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); - } - - HWND GetHwnd() override - { - return m_hwnd; - } - - int GetType() override - { - return 2; - } - - wchar_t* GetSelection() override - { - IEVIEWEVENT event = {}; - event.hwnd = m_hwnd; - event.iType = IEE_GET_SELECTION; - event.hContact = m_pDlg.m_hContact; - event.dwFlags = 0; - return (wchar_t *)CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); - } - - void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) override - { - if (!bAppend) - Clear(); - - IEVIEWEVENT event = {}; - event.hwnd = m_hwnd; - event.iType = IEE_LOG_DB_EVENTS; - event.hDbEventFirst = hDbEventFirst; - event.hContact = m_pDlg.m_hContact; - event.count = count; - CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); - } - - void CHppLogWindow::LogEvents(LOGINFO *pLog, bool) - { - IEVIEWEVENTDATA ied = {}; - ied.dwFlags = IEEDF_UNICODE_NICK | IEEDF_UNICODE_TEXT; - - IEVIEWEVENT event = {}; - event.hwnd = m_hwnd; - event.hContact = m_pDlg.m_hContact; - event.codepage = CP_ACP; - event.iType = IEE_LOG_MEM_EVENTS; - event.eventData = &ied; - event.count = 1; - - while (pLog) { - if (pLog->ptszText) { - ied.szNick.w = pLog->ptszNick; - ied.szText.w = pLog->ptszText; - ied.time = pLog->time; - ied.bIsMe = pLog->bIsMe; - - switch (pLog->iType) { - case GC_EVENT_MESSAGE: - ied.iType = IEED_GC_EVENT_MESSAGE; - ied.dwData = IEEDD_GC_SHOW_NICK; - break; - case GC_EVENT_ACTION: - ied.iType = IEED_GC_EVENT_ACTION; - break; - case GC_EVENT_JOIN: - ied.iType = IEED_GC_EVENT_JOIN; - break; - case GC_EVENT_PART: - ied.iType = IEED_GC_EVENT_PART; - break; - case GC_EVENT_QUIT: - ied.iType = IEED_GC_EVENT_QUIT; - break; - case GC_EVENT_NICK: - ied.iType = IEED_GC_EVENT_NICK; - break; - case GC_EVENT_KICK: - ied.iType = IEED_GC_EVENT_KICK; - break; - case GC_EVENT_NOTICE: - ied.iType = IEED_GC_EVENT_NOTICE; - break; - case GC_EVENT_TOPIC: - ied.iType = IEED_GC_EVENT_TOPIC; - break; - case GC_EVENT_INFORMATION: - ied.iType = IEED_GC_EVENT_INFORMATION; - break; - case GC_EVENT_ADDSTATUS: - ied.iType = IEED_GC_EVENT_ADDSTATUS; - break; - case GC_EVENT_REMOVESTATUS: - ied.iType = IEED_GC_EVENT_REMOVESTATUS; - break; - } - ied.dwData |= g_Settings->bShowTime ? IEEDD_GC_SHOW_TIME : 0; - ied.dwData |= IEEDD_GC_SHOW_ICON; - ied.dwFlags = IEEDF_UNICODE_TEXT | IEEDF_UNICODE_NICK; - CallService(MS_HPP_EG_EVENT, 0, (LPARAM) & event); - } - - pLog = pLog->prev; - } - } - - void Resize() override - { - RECT rcRichEdit; - GetWindowRect(GetDlgItem(m_pDlg.GetHwnd(), IDC_SRMM_LOG), &rcRichEdit); - - POINT pt = { rcRichEdit.left, rcRichEdit.top }; - ScreenToClient(GetParent(m_hwnd), &pt); - - IEVIEWWINDOW ieWindow = { sizeof(ieWindow) }; - ieWindow.iType = IEW_SETPOS; - ieWindow.parent = m_hwnd; - ieWindow.hwnd = m_hwnd; - ieWindow.x = pt.x; - ieWindow.y = pt.y; - ieWindow.cx = rcRichEdit.right - rcRichEdit.left; - ieWindow.cy = rcRichEdit.bottom - rcRichEdit.top; - if (ieWindow.cx != 0 && ieWindow.cy != 0) - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); - } - - void ScrollToBottom() override - { - IEVIEWWINDOW iew = { sizeof(iew) }; - iew.iType = IEW_SCROLLBOTTOM; - iew.hwnd = m_hwnd; - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&iew); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -static HANDLE hLogger; - -static CSrmmLogWindow *logBuilder(CMsgDialog &pDlg) -{ - return new CHppLogWindow(pDlg); -} - -MIR_APP_DLL(void) RegisterHppLogger() -{ - hLogger = RegisterSrmmLog(&g_plugin, "hpp", L"History++", &logBuilder); -} - -MIR_APP_DLL(void) UnregisterHppLogger() -{ - UnregisterSrmmLog(hLogger); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CHppLogWindow class
+
+#include "stdafx.h"
+#include "chat.h"
+
+#define EVENTTYPE_STATUSCHANGE 25368
+#define EVENTTYPE_ERRMSG 25366
+
+class CHppLogWindow : public CSrmmLogWindow
+{
+ HWND m_hwnd = nullptr;
+
+public:
+ CHppLogWindow(CMsgDialog &pDlg) :
+ CSrmmLogWindow(pDlg)
+ {
+ }
+
+ void Attach() override
+ {
+ IEVIEWWINDOW ieWindow = {};
+ ieWindow.iType = IEW_CREATE;
+ ieWindow.dwMode = IEWM_TABSRMM;
+ ieWindow.parent = m_pDlg.GetHwnd();
+ ieWindow.cx = 10;
+ ieWindow.cy = 10;
+ CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow);
+ m_hwnd = ieWindow.hwnd;
+ }
+
+ void Detach() override
+ {
+ IEVIEWWINDOW ieWindow = {};
+ ieWindow.iType = IEW_DESTROY;
+ ieWindow.hwnd = m_hwnd;
+ CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////
+
+ bool AtBottom() override
+ {
+ return false;
+ }
+
+ void Clear() override
+ {
+ IEVIEWEVENT event = {};
+ event.iType = IEE_CLEAR_LOG;
+ event.hwnd = m_hwnd;
+ CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event);
+ }
+
+ HWND GetHwnd() override
+ {
+ return m_hwnd;
+ }
+
+ int GetType() override
+ {
+ return 2;
+ }
+
+ wchar_t* GetSelection() override
+ {
+ IEVIEWEVENT event = {};
+ event.hwnd = m_hwnd;
+ event.iType = IEE_GET_SELECTION;
+ event.hContact = m_pDlg.m_hContact;
+ event.dwFlags = 0;
+ return (wchar_t *)CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event);
+ }
+
+ void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) override
+ {
+ if (!bAppend)
+ Clear();
+
+ IEVIEWEVENT event = {};
+ event.hwnd = m_hwnd;
+ event.iType = IEE_LOG_DB_EVENTS;
+ event.hDbEventFirst = hDbEventFirst;
+ event.hContact = m_pDlg.m_hContact;
+ event.count = count;
+ CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event);
+ }
+
+ void CHppLogWindow::LogEvents(LOGINFO *pLog, bool)
+ {
+ IEVIEWEVENTDATA ied = {};
+ ied.dwFlags = IEEDF_UNICODE_NICK | IEEDF_UNICODE_TEXT;
+
+ IEVIEWEVENT event = {};
+ event.hwnd = m_hwnd;
+ event.hContact = m_pDlg.m_hContact;
+ event.codepage = CP_ACP;
+ event.iType = IEE_LOG_MEM_EVENTS;
+ event.eventData = &ied;
+ event.count = 1;
+
+ while (pLog) {
+ if (pLog->ptszText) {
+ ied.szNick.w = pLog->ptszNick;
+ ied.szText.w = pLog->ptszText;
+ ied.time = pLog->time;
+ ied.bIsMe = pLog->bIsMe;
+
+ switch (pLog->iType) {
+ case GC_EVENT_MESSAGE:
+ ied.iType = IEED_GC_EVENT_MESSAGE;
+ ied.dwData = IEEDD_GC_SHOW_NICK;
+ break;
+ case GC_EVENT_ACTION:
+ ied.iType = IEED_GC_EVENT_ACTION;
+ break;
+ case GC_EVENT_JOIN:
+ ied.iType = IEED_GC_EVENT_JOIN;
+ break;
+ case GC_EVENT_PART:
+ ied.iType = IEED_GC_EVENT_PART;
+ break;
+ case GC_EVENT_QUIT:
+ ied.iType = IEED_GC_EVENT_QUIT;
+ break;
+ case GC_EVENT_NICK:
+ ied.iType = IEED_GC_EVENT_NICK;
+ break;
+ case GC_EVENT_KICK:
+ ied.iType = IEED_GC_EVENT_KICK;
+ break;
+ case GC_EVENT_NOTICE:
+ ied.iType = IEED_GC_EVENT_NOTICE;
+ break;
+ case GC_EVENT_TOPIC:
+ ied.iType = IEED_GC_EVENT_TOPIC;
+ break;
+ case GC_EVENT_INFORMATION:
+ ied.iType = IEED_GC_EVENT_INFORMATION;
+ break;
+ case GC_EVENT_ADDSTATUS:
+ ied.iType = IEED_GC_EVENT_ADDSTATUS;
+ break;
+ case GC_EVENT_REMOVESTATUS:
+ ied.iType = IEED_GC_EVENT_REMOVESTATUS;
+ break;
+ }
+ ied.dwData |= g_Settings->bShowTime ? IEEDD_GC_SHOW_TIME : 0;
+ ied.dwData |= IEEDD_GC_SHOW_ICON;
+ ied.dwFlags = IEEDF_UNICODE_TEXT | IEEDF_UNICODE_NICK;
+ CallService(MS_HPP_EG_EVENT, 0, (LPARAM) & event);
+ }
+
+ pLog = pLog->prev;
+ }
+ }
+
+ void Resize() override
+ {
+ RECT rcRichEdit;
+ GetWindowRect(GetDlgItem(m_pDlg.GetHwnd(), IDC_SRMM_LOG), &rcRichEdit);
+
+ POINT pt = { rcRichEdit.left, rcRichEdit.top };
+ ScreenToClient(GetParent(m_hwnd), &pt);
+
+ IEVIEWWINDOW ieWindow = { sizeof(ieWindow) };
+ ieWindow.iType = IEW_SETPOS;
+ ieWindow.parent = m_hwnd;
+ ieWindow.hwnd = m_hwnd;
+ ieWindow.x = pt.x;
+ ieWindow.y = pt.y;
+ ieWindow.cx = rcRichEdit.right - rcRichEdit.left;
+ ieWindow.cy = rcRichEdit.bottom - rcRichEdit.top;
+ if (ieWindow.cx != 0 && ieWindow.cy != 0)
+ CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow);
+ }
+
+ void ScrollToBottom() override
+ {
+ IEVIEWWINDOW iew = { sizeof(iew) };
+ iew.iType = IEW_SCROLLBOTTOM;
+ iew.hwnd = m_hwnd;
+ CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&iew);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static HANDLE hLogger;
+
+static CSrmmLogWindow *logBuilder(CMsgDialog &pDlg)
+{
+ return new CHppLogWindow(pDlg);
+}
+
+MIR_APP_DLL(void) RegisterHppLogger()
+{
+ hLogger = RegisterSrmmLog(&g_plugin, "hpp", L"History++", &logBuilder);
+}
+
+MIR_APP_DLL(void) UnregisterHppLogger()
+{
+ UnregisterSrmmLog(hLogger);
+}
diff --git a/src/mir_app/src/srmm_log_rtf.cpp b/src/mir_app/src/srmm_log_rtf.cpp index 01d8b7c5c8..c6bf86af64 100644 --- a/src/mir_app/src/srmm_log_rtf.cpp +++ b/src/mir_app/src/srmm_log_rtf.cpp @@ -1,408 +1,408 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// SRMM log container - -#include "stdafx.h" -#include "chat.h" - -#define EVENTTYPE_STATUSCHANGE 25368 -#define EVENTTYPE_ERRMSG 25366 - -CRtfLogWindow::CRtfLogWindow(CMsgDialog &pDlg) : - CSrmmLogWindow(pDlg), - m_rtf(*(CCtrlRichEdit*)pDlg.FindControl(IDC_SRMM_LOG)) -{ -} - -CRtfLogWindow::~CRtfLogWindow() -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubLogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CRtfLogWindow *pLog = (CRtfLogWindow *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (pLog != nullptr) - return pLog->WndProc(msg, wParam, lParam); - - return mir_callNextSubclass(hwnd, stubLogProc, msg, wParam, lParam); -} - -void CRtfLogWindow::Attach() -{ - SetWindowLongPtr(m_rtf.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - m_rtf.SetReadOnly(true); - - mir_subclassWindow(m_rtf.GetHwnd(), stubLogProc); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CRtfLogWindow::Detach() -{ - mir_unsubclassWindow(m_rtf.GetHwnd(), stubLogProc); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CRtfLogWindow::AtBottom() -{ - if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL)) - return false; - - SCROLLINFO si = {}; - si.cbSize = sizeof(si); - si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; - GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si); - return (si.nPos + (int)si.nPage + 5) >= si.nMax; -} - -void CRtfLogWindow::Clear() -{ - m_rtf.SetText(L""); -} - -HWND CRtfLogWindow::GetHwnd() -{ - return m_rtf.GetHwnd(); -} - -int CRtfLogWindow::GetType() -{ - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static DWORD CALLBACK StreamOutCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) -{ - CMStringW *str = (CMStringW *)dwCookie; - str->Append((wchar_t*)pbBuff, cb / 2); - *pcb = cb; - return 0; -} - -wchar_t* CRtfLogWindow::GetSelection() -{ - CHARRANGE sel; - SendMessage(m_rtf.GetHwnd(), EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin == sel.cpMax) - return nullptr; - - CMStringW result; - - EDITSTREAM stream; - memset(&stream, 0, sizeof(stream)); - stream.pfnCallback = StreamOutCallback; - stream.dwCookie = (DWORD_PTR)&result; - SendMessage(m_rtf.GetHwnd(), EM_STREAMOUT, SF_TEXT | SF_UNICODE | SFF_SELECTION, (LPARAM)&stream); - return result.Detach(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CRtfLogWindow::Notify(WPARAM, LPARAM lParam) -{ - LPNMHDR hdr = (LPNMHDR)lParam; - if (hdr->code != EN_LINK) - return FALSE; - - ENLINK *pLink = (ENLINK *)lParam; - switch (pLink->msg) { - case WM_SETCURSOR: - SetCursor(g_hCurHyperlinkHand); - SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE); - return TRUE; - - case WM_RBUTTONDOWN: - case WM_LBUTTONUP: - case WM_LBUTTONDBLCLK: - CHARRANGE sel; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin != sel.cpMax) - break; - - CMStringW wszText(' ', pLink->chrg.cpMax - pLink->chrg.cpMin + 1); - - TEXTRANGE tr; - tr.chrg = pLink->chrg; - tr.lpstrText = wszText.GetBuffer(); - m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr); - if (wcschr(tr.lpstrText, '@') != nullptr && wcschr(tr.lpstrText, ':') == nullptr && wcschr(tr.lpstrText, '/') == nullptr) - wszText.Insert(0, L"mailto:"); - - if (pLink->msg == WM_RBUTTONDOWN) { - HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT)); - HMENU hSubMenu = GetSubMenu(hMenu, 6); - TranslateMenu(hSubMenu); - - POINT pt = { GET_X_LPARAM(pLink->lParam), GET_Y_LPARAM(pLink->lParam) }; - ClientToScreen(((NMHDR *)lParam)->hwndFrom, &pt); - - switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlg.m_hwnd, nullptr)) { - case IDM_OPENLINK: - Utils_OpenUrlW(wszText); - break; - - case IDM_COPYLINK: - Utils_ClipboardCopy(wszText); - break; - } - - DestroyMenu(hMenu); - SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE); - return TRUE; - } - - Utils_OpenUrlW(wszText); - SetFocus(m_pDlg.m_message.GetHwnd()); - } - - return FALSE; -} - -void CRtfLogWindow::Resize() -{ - bool bottomScroll = !m_pDlg.isChat(); - if (AtBottom()) - bottomScroll = true; - - // ::MoveWindow(m_rtf.GetHwnd(), x, y, cx, cy, true); - - if (bottomScroll) - ScrollToBottom(); -} - -void CRtfLogWindow::ScrollToBottom() -{ - if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL)) - return; - - SCROLLINFO si = {}; - si.cbSize = sizeof(si); - si.fMask = SIF_PAGE | SIF_RANGE; - GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si); - - si.fMask = SIF_POS; - si.nPos = si.nMax - si.nPage; - SetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si, TRUE); - m_rtf.SendMsg(WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static wchar_t szTrimString[] = L":;,.!?\'\"><()[]- \r\n"; - -INT_PTR CRtfLogWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - CHARRANGE sel; - - switch (msg) { - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_INACTIVE) { - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin != sel.cpMax) { - sel.cpMin = sel.cpMax; - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - } - } - break; - - case WM_SETCURSOR: - if (m_pDlg.m_bInMenu) { - SetCursor(LoadCursor(nullptr, IDC_ARROW)); - return TRUE; - } - break; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (!(GetKeyState(VK_RMENU) & 0x8000)) { - MSG message = { m_pDlg.m_hwnd, msg, wParam, lParam }; - LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION); - if (iButtonFrom) { - Srmm_ProcessToolbarHotkey(m_pDlg.m_hContact, iButtonFrom, m_pDlg.m_hwnd); - return TRUE; - } - } - break; - - case WM_CHAR: - if (wParam >= ' ') { - SetFocus(m_pDlg.m_message.GetHwnd()); - m_pDlg.m_message.SendMsg(WM_CHAR, wParam, lParam); - } - else if (wParam == '\t') - SetFocus(m_pDlg.m_message.GetHwnd()); - break; - - case WM_CONTEXTMENU: - POINT pt, ptl; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - if (lParam == 0xFFFFFFFF) { - m_rtf.SendMsg(EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax); - ClientToScreen(m_rtf.GetHwnd(), &pt); - } - else { - pt.x = GET_X_LPARAM(lParam); - pt.y = GET_Y_LPARAM(lParam); - } - ptl = pt; - ScreenToClient(m_rtf.GetHwnd(), &ptl); - { - wchar_t *pszWord = (wchar_t *)_alloca(8192); - pszWord[0] = '\0'; - - // get a word under cursor - if (sel.cpMin == sel.cpMax) { - int iCharIndex = m_rtf.SendMsg(EM_CHARFROMPOS, 0, (LPARAM)&ptl); - if (iCharIndex < 0) - break; - - sel.cpMin = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_LEFT, iCharIndex); - sel.cpMax = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_RIGHT, iCharIndex); - } - - if (sel.cpMax > sel.cpMin) { - TEXTRANGE tr = { 0 }; - tr.chrg = sel; - tr.lpstrText = pszWord; - int iRes = m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr); - if (iRes > 0) { - wchar_t *p = wcschr(pszWord, '\r'); - if (p) - *p = 0; - - size_t iLen = mir_wstrlen(pszWord) - 1; - while (wcschr(szTrimString, pszWord[iLen])) { - pszWord[iLen] = '\0'; - iLen--; - } - } - } - - CHARRANGE all = { 0, -1 }; - HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_LOGMENU)); - HMENU hSubMenu = GetSubMenu(hMenu, 0); - TranslateMenu(hSubMenu); - m_pDlg.m_bInMenu = true; - - int flags = MF_BYPOSITION | (GetRichTextLength(m_rtf.GetHwnd()) == 0 ? MF_GRAYED : MF_ENABLED); - EnableMenuItem(hSubMenu, 0, flags); - EnableMenuItem(hSubMenu, 2, flags); - - if (pszWord && pszWord[0]) { - CMStringW wszText(FORMAT, TranslateT("Look up '%s':"), pszWord); - if (wszText.GetLength() > 30) { - wszText.Truncate(30); - wszText.AppendChar('\''); - } - ModifyMenu(hSubMenu, 4, MF_STRING | MF_BYPOSITION, 4, wszText); - } - else ModifyMenu(hSubMenu, 4, MF_STRING | MF_GRAYED | MF_BYPOSITION, 4, TranslateT("No word to look up")); - - UINT uID = Chat_CreateMenu(m_rtf.GetHwnd(), hSubMenu, pt, m_pDlg.m_si, nullptr); - m_pDlg.m_bInMenu = false; - DestroyMenu(hMenu); - - switch (uID) { - case 0: - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - case IDM_COPYALL: - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&all); - m_rtf.SendMsg(WM_COPY, 0, 0); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - case IDM_CLEAR: - m_rtf.SetText(L""); - if (auto *si = m_pDlg.m_si) { - g_chatApi.LM_RemoveAll(&si->pLog, &si->pLogEnd); - si->iEventCount = 0; - si->LastTime = 0; - } - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - case IDM_SEARCH_GOOGLE: - case IDM_SEARCH_BING: - case IDM_SEARCH_YANDEX: - case IDM_SEARCH_YAHOO: - case IDM_SEARCH_WIKIPEDIA: - case IDM_SEARCH_FOODNETWORK: - case IDM_SEARCH_GOOGLE_MAPS: - case IDM_SEARCH_GOOGLE_TRANSLATE: - { - CMStringW szURL; - switch (uID) { - case IDM_SEARCH_WIKIPEDIA: - szURL.Format(L"http://en.wikipedia.org/wiki/%s", pszWord); - break; - case IDM_SEARCH_YAHOO: - szURL.Format(L"http://search.yahoo.com/search?p=%s&ei=UTF-8", pszWord); - break; - case IDM_SEARCH_FOODNETWORK: - szURL.Format(L"http://search.foodnetwork.com/search/delegate.do?fnSearchString=%s", pszWord); - break; - case IDM_SEARCH_BING: - szURL.Format(L"http://www.bing.com/search?q=%s&form=OSDSRC", pszWord); - break; - case IDM_SEARCH_GOOGLE_MAPS: - szURL.Format(L"http://maps.google.com/maps?q=%s&ie=utf-8&oe=utf-8", pszWord); - break; - case IDM_SEARCH_GOOGLE_TRANSLATE: - szURL.Format(L"http://translate.google.com/?q=%s&ie=utf-8&oe=utf-8", pszWord); - break; - case IDM_SEARCH_YANDEX: - szURL.Format(L"http://yandex.ru/yandsearch?text=%s", pszWord); - break; - case IDM_SEARCH_GOOGLE: - szURL.Format(L"http://www.google.com/search?q=%s&ie=utf-8&oe=utf-8", pszWord); - break; - } - Utils_OpenUrlW(szURL); - } - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - default: - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - Chat_DoEventHook(m_pDlg.m_si, GC_USER_LOGMENU, nullptr, nullptr, uID); - break; - } - } - return 0; - } - - LRESULT res = mir_callNextSubclass(m_rtf.GetHwnd(), stubLogProc, msg, wParam, lParam); - if (msg == WM_GETDLGCODE) - return res & ~DLGC_HASSETSEL; - return res; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SRMM log container
+
+#include "stdafx.h"
+#include "chat.h"
+
+#define EVENTTYPE_STATUSCHANGE 25368
+#define EVENTTYPE_ERRMSG 25366
+
+CRtfLogWindow::CRtfLogWindow(CMsgDialog &pDlg) :
+ CSrmmLogWindow(pDlg),
+ m_rtf(*(CCtrlRichEdit*)pDlg.FindControl(IDC_SRMM_LOG))
+{
+}
+
+CRtfLogWindow::~CRtfLogWindow()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubLogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CRtfLogWindow *pLog = (CRtfLogWindow *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (pLog != nullptr)
+ return pLog->WndProc(msg, wParam, lParam);
+
+ return mir_callNextSubclass(hwnd, stubLogProc, msg, wParam, lParam);
+}
+
+void CRtfLogWindow::Attach()
+{
+ SetWindowLongPtr(m_rtf.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+ m_rtf.SetReadOnly(true);
+
+ mir_subclassWindow(m_rtf.GetHwnd(), stubLogProc);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CRtfLogWindow::Detach()
+{
+ mir_unsubclassWindow(m_rtf.GetHwnd(), stubLogProc);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CRtfLogWindow::AtBottom()
+{
+ if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL))
+ return false;
+
+ SCROLLINFO si = {};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
+ GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si);
+ return (si.nPos + (int)si.nPage + 5) >= si.nMax;
+}
+
+void CRtfLogWindow::Clear()
+{
+ m_rtf.SetText(L"");
+}
+
+HWND CRtfLogWindow::GetHwnd()
+{
+ return m_rtf.GetHwnd();
+}
+
+int CRtfLogWindow::GetType()
+{
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static DWORD CALLBACK StreamOutCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
+{
+ CMStringW *str = (CMStringW *)dwCookie;
+ str->Append((wchar_t*)pbBuff, cb / 2);
+ *pcb = cb;
+ return 0;
+}
+
+wchar_t* CRtfLogWindow::GetSelection()
+{
+ CHARRANGE sel;
+ SendMessage(m_rtf.GetHwnd(), EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (sel.cpMin == sel.cpMax)
+ return nullptr;
+
+ CMStringW result;
+
+ EDITSTREAM stream;
+ memset(&stream, 0, sizeof(stream));
+ stream.pfnCallback = StreamOutCallback;
+ stream.dwCookie = (DWORD_PTR)&result;
+ SendMessage(m_rtf.GetHwnd(), EM_STREAMOUT, SF_TEXT | SF_UNICODE | SFF_SELECTION, (LPARAM)&stream);
+ return result.Detach();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CRtfLogWindow::Notify(WPARAM, LPARAM lParam)
+{
+ LPNMHDR hdr = (LPNMHDR)lParam;
+ if (hdr->code != EN_LINK)
+ return FALSE;
+
+ ENLINK *pLink = (ENLINK *)lParam;
+ switch (pLink->msg) {
+ case WM_SETCURSOR:
+ SetCursor(g_hCurHyperlinkHand);
+ SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_LBUTTONDBLCLK:
+ CHARRANGE sel;
+ m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (sel.cpMin != sel.cpMax)
+ break;
+
+ CMStringW wszText(' ', pLink->chrg.cpMax - pLink->chrg.cpMin + 1);
+
+ TEXTRANGE tr;
+ tr.chrg = pLink->chrg;
+ tr.lpstrText = wszText.GetBuffer();
+ m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ if (wcschr(tr.lpstrText, '@') != nullptr && wcschr(tr.lpstrText, ':') == nullptr && wcschr(tr.lpstrText, '/') == nullptr)
+ wszText.Insert(0, L"mailto:");
+
+ if (pLink->msg == WM_RBUTTONDOWN) {
+ HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT));
+ HMENU hSubMenu = GetSubMenu(hMenu, 6);
+ TranslateMenu(hSubMenu);
+
+ POINT pt = { GET_X_LPARAM(pLink->lParam), GET_Y_LPARAM(pLink->lParam) };
+ ClientToScreen(((NMHDR *)lParam)->hwndFrom, &pt);
+
+ switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlg.m_hwnd, nullptr)) {
+ case IDM_OPENLINK:
+ Utils_OpenUrlW(wszText);
+ break;
+
+ case IDM_COPYLINK:
+ Utils_ClipboardCopy(wszText);
+ break;
+ }
+
+ DestroyMenu(hMenu);
+ SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+
+ Utils_OpenUrlW(wszText);
+ SetFocus(m_pDlg.m_message.GetHwnd());
+ }
+
+ return FALSE;
+}
+
+void CRtfLogWindow::Resize()
+{
+ bool bottomScroll = !m_pDlg.isChat();
+ if (AtBottom())
+ bottomScroll = true;
+
+ // ::MoveWindow(m_rtf.GetHwnd(), x, y, cx, cy, true);
+
+ if (bottomScroll)
+ ScrollToBottom();
+}
+
+void CRtfLogWindow::ScrollToBottom()
+{
+ if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL))
+ return;
+
+ SCROLLINFO si = {};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_PAGE | SIF_RANGE;
+ GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si);
+
+ si.fMask = SIF_POS;
+ si.nPos = si.nMax - si.nPage;
+ SetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si, TRUE);
+ m_rtf.SendMsg(WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static wchar_t szTrimString[] = L":;,.!?\'\"><()[]- \r\n";
+
+INT_PTR CRtfLogWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CHARRANGE sel;
+
+ switch (msg) {
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) == WA_INACTIVE) {
+ m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (sel.cpMin != sel.cpMax) {
+ sel.cpMin = sel.cpMax;
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel);
+ }
+ }
+ break;
+
+ case WM_SETCURSOR:
+ if (m_pDlg.m_bInMenu) {
+ SetCursor(LoadCursor(nullptr, IDC_ARROW));
+ return TRUE;
+ }
+ break;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ if (!(GetKeyState(VK_RMENU) & 0x8000)) {
+ MSG message = { m_pDlg.m_hwnd, msg, wParam, lParam };
+ LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION);
+ if (iButtonFrom) {
+ Srmm_ProcessToolbarHotkey(m_pDlg.m_hContact, iButtonFrom, m_pDlg.m_hwnd);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_CHAR:
+ if (wParam >= ' ') {
+ SetFocus(m_pDlg.m_message.GetHwnd());
+ m_pDlg.m_message.SendMsg(WM_CHAR, wParam, lParam);
+ }
+ else if (wParam == '\t')
+ SetFocus(m_pDlg.m_message.GetHwnd());
+ break;
+
+ case WM_CONTEXTMENU:
+ POINT pt, ptl;
+ m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (lParam == 0xFFFFFFFF) {
+ m_rtf.SendMsg(EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax);
+ ClientToScreen(m_rtf.GetHwnd(), &pt);
+ }
+ else {
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+ }
+ ptl = pt;
+ ScreenToClient(m_rtf.GetHwnd(), &ptl);
+ {
+ wchar_t *pszWord = (wchar_t *)_alloca(8192);
+ pszWord[0] = '\0';
+
+ // get a word under cursor
+ if (sel.cpMin == sel.cpMax) {
+ int iCharIndex = m_rtf.SendMsg(EM_CHARFROMPOS, 0, (LPARAM)&ptl);
+ if (iCharIndex < 0)
+ break;
+
+ sel.cpMin = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_LEFT, iCharIndex);
+ sel.cpMax = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_RIGHT, iCharIndex);
+ }
+
+ if (sel.cpMax > sel.cpMin) {
+ TEXTRANGE tr = { 0 };
+ tr.chrg = sel;
+ tr.lpstrText = pszWord;
+ int iRes = m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ if (iRes > 0) {
+ wchar_t *p = wcschr(pszWord, '\r');
+ if (p)
+ *p = 0;
+
+ size_t iLen = mir_wstrlen(pszWord) - 1;
+ while (wcschr(szTrimString, pszWord[iLen])) {
+ pszWord[iLen] = '\0';
+ iLen--;
+ }
+ }
+ }
+
+ CHARRANGE all = { 0, -1 };
+ HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_LOGMENU));
+ HMENU hSubMenu = GetSubMenu(hMenu, 0);
+ TranslateMenu(hSubMenu);
+ m_pDlg.m_bInMenu = true;
+
+ int flags = MF_BYPOSITION | (GetRichTextLength(m_rtf.GetHwnd()) == 0 ? MF_GRAYED : MF_ENABLED);
+ EnableMenuItem(hSubMenu, 0, flags);
+ EnableMenuItem(hSubMenu, 2, flags);
+
+ if (pszWord && pszWord[0]) {
+ CMStringW wszText(FORMAT, TranslateT("Look up '%s':"), pszWord);
+ if (wszText.GetLength() > 30) {
+ wszText.Truncate(30);
+ wszText.AppendChar('\'');
+ }
+ ModifyMenu(hSubMenu, 4, MF_STRING | MF_BYPOSITION, 4, wszText);
+ }
+ else ModifyMenu(hSubMenu, 4, MF_STRING | MF_GRAYED | MF_BYPOSITION, 4, TranslateT("No word to look up"));
+
+ UINT uID = Chat_CreateMenu(m_rtf.GetHwnd(), hSubMenu, pt, m_pDlg.m_si, nullptr);
+ m_pDlg.m_bInMenu = false;
+ DestroyMenu(hMenu);
+
+ switch (uID) {
+ case 0:
+ PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ break;
+
+ case IDM_COPYALL:
+ m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel);
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&all);
+ m_rtf.SendMsg(WM_COPY, 0, 0);
+ m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel);
+ PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ break;
+
+ case IDM_CLEAR:
+ m_rtf.SetText(L"");
+ if (auto *si = m_pDlg.m_si) {
+ g_chatApi.LM_RemoveAll(&si->pLog, &si->pLogEnd);
+ si->iEventCount = 0;
+ si->LastTime = 0;
+ }
+ PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ break;
+
+ case IDM_SEARCH_GOOGLE:
+ case IDM_SEARCH_BING:
+ case IDM_SEARCH_YANDEX:
+ case IDM_SEARCH_YAHOO:
+ case IDM_SEARCH_WIKIPEDIA:
+ case IDM_SEARCH_FOODNETWORK:
+ case IDM_SEARCH_GOOGLE_MAPS:
+ case IDM_SEARCH_GOOGLE_TRANSLATE:
+ {
+ CMStringW szURL;
+ switch (uID) {
+ case IDM_SEARCH_WIKIPEDIA:
+ szURL.Format(L"http://en.wikipedia.org/wiki/%s", pszWord);
+ break;
+ case IDM_SEARCH_YAHOO:
+ szURL.Format(L"http://search.yahoo.com/search?p=%s&ei=UTF-8", pszWord);
+ break;
+ case IDM_SEARCH_FOODNETWORK:
+ szURL.Format(L"http://search.foodnetwork.com/search/delegate.do?fnSearchString=%s", pszWord);
+ break;
+ case IDM_SEARCH_BING:
+ szURL.Format(L"http://www.bing.com/search?q=%s&form=OSDSRC", pszWord);
+ break;
+ case IDM_SEARCH_GOOGLE_MAPS:
+ szURL.Format(L"http://maps.google.com/maps?q=%s&ie=utf-8&oe=utf-8", pszWord);
+ break;
+ case IDM_SEARCH_GOOGLE_TRANSLATE:
+ szURL.Format(L"http://translate.google.com/?q=%s&ie=utf-8&oe=utf-8", pszWord);
+ break;
+ case IDM_SEARCH_YANDEX:
+ szURL.Format(L"http://yandex.ru/yandsearch?text=%s", pszWord);
+ break;
+ case IDM_SEARCH_GOOGLE:
+ szURL.Format(L"http://www.google.com/search?q=%s&ie=utf-8&oe=utf-8", pszWord);
+ break;
+ }
+ Utils_OpenUrlW(szURL);
+ }
+ PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ break;
+
+ default:
+ PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ Chat_DoEventHook(m_pDlg.m_si, GC_USER_LOGMENU, nullptr, nullptr, uID);
+ break;
+ }
+ }
+ return 0;
+ }
+
+ LRESULT res = mir_callNextSubclass(m_rtf.GetHwnd(), stubLogProc, msg, wParam, lParam);
+ if (msg == WM_GETDLGCODE)
+ return res & ~DLGC_HASSETSEL;
+ return res;
+}
diff --git a/src/mir_app/src/srmm_statusicon.cpp b/src/mir_app/src/srmm_statusicon.cpp index 1324a30eb7..12a613610e 100644 --- a/src/mir_app/src/srmm_statusicon.cpp +++ b/src/mir_app/src/srmm_statusicon.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/srmm_toolbar.cpp b/src/mir_app/src/srmm_toolbar.cpp index f833ea16f9..fb593ea1a4 100644 --- a/src/mir_app/src/srmm_toolbar.cpp +++ b/src/mir_app/src/srmm_toolbar.cpp @@ -1,886 +1,886 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "chat.h" -#include "skin.h" - -#define BB_MODULE_NAME "SRMM_Toolbar" - -#define DPISCALEY_S(argY) ((int)((double)(argY) * g_DPIscaleY)) -#define DPISCALEX_S(argX) ((int)((double)(argX) * g_DPIscaleX)) - -static double g_DPIscaleX, g_DPIscaleY; -static class CSrmmToolbarOptions *g_pDialog = nullptr; - -static CMOption<uint8_t> g_iButtonGap(BB_MODULE_NAME, "ButtonsBarGap", 1); - -static int SortButtons(const CustomButtonData *p1, const CustomButtonData *p2) -{ - if (p1->m_bRSided != p2->m_bRSided) - return (p2->m_bRSided) ? -1 : 1; - if (p1->m_dwPosition != p2->m_dwPosition) - return p1->m_dwPosition - p2->m_dwPosition; - int res = mir_strcmp(p1->m_pszModuleName, p2->m_pszModuleName); - if (res != 0) - return res; - return p1->m_dwButtonID - p2->m_dwButtonID; -} - -static LIST<CustomButtonData> arButtonsList(1, SortButtons); - -int LastCID = MIN_CBUTTONID; -int dwSepCount = 0; - -static mir_cs csToolBar; -static HANDLE hHookToolBarLoadedEvt, hHookButtonPressedEvt; - -static int sstSortButtons(const void *p1, const void *p2) -{ - return SortButtons(*(CustomButtonData**)p1, *(CustomButtonData**)p2); -} - -static void CB_RegisterSeparators() -{ - BBButton bbd = {}; - bbd.pszModuleName = "Tabsrmm_sep"; - for (int i = 0; dwSepCount > i; i++) { - bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON; - bbd.dwButtonID = i + 1; - bbd.dwDefPos = 410 + i; - Srmm_AddButton(&bbd, &g_plugin); - } -} - -MIR_APP_DLL(CustomButtonData*) Srmm_GetNthButton(int i) -{ - return arButtonsList[i]; -} - -MIR_APP_DLL(int) Srmm_GetButtonCount(void) -{ - return arButtonsList.getCount(); -} - -MIR_APP_DLL(int) Srmm_GetButtonGap() -{ - return g_iButtonGap; -} - -MIR_APP_DLL(int) Srmm_GetButtonState(HWND hwndDlg, BBButton *bbdi) -{ - if (hwndDlg == nullptr || bbdi == nullptr) - return 1; - - uint32_t tempCID = 0; - bbdi->bbbFlags = 0; - for (auto &cbd : arButtonsList) - if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) { - tempCID = cbd->m_dwButtonCID; - break; - } - - if (!tempCID) - return 1; - - HWND hwndBtn = GetDlgItem(hwndDlg, tempCID); - bbdi->bbbFlags = (IsDlgButtonChecked(hwndDlg, tempCID) ? BBSF_PUSHED : BBSF_RELEASED) | (IsWindowVisible(hwndBtn) ? 0 : BBSF_HIDDEN) | (IsWindowEnabled(hwndBtn) ? 0 : BBSF_DISABLED); - return 0; -} - -MIR_APP_DLL(int) Srmm_SetButtonState(MCONTACT hContact, BBButton *bbdi) -{ - if (hContact == 0 || bbdi == nullptr) - return 1; - - uint32_t tempCID = 0; - for (auto &cbd : arButtonsList) - if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) { - tempCID = cbd->m_dwButtonCID; - break; - } - - if (!tempCID) - return 1; - - HWND hwndDlg = WindowList_Find(g_hWindowList, hContact); - if (hwndDlg == nullptr) - return 1; - - HWND hwndBtn = GetDlgItem(hwndDlg, tempCID); - if (hwndBtn == nullptr) - return 1; - - SetWindowTextA(hwndBtn, bbdi->pszModuleName); - if (bbdi->hIcon) - SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(bbdi->hIcon)); - if (bbdi->pwszTooltip) - SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)bbdi->pwszTooltip, BATF_UNICODE); - if (bbdi->bbbFlags) { - ShowWindow(hwndBtn, (bbdi->bbbFlags & BBSF_HIDDEN) ? SW_HIDE : SW_SHOW); - EnableWindow(hwndBtn, !(bbdi->bbbFlags & BBSF_DISABLED)); - Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_PUSHED) != 0); - Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_RELEASED) == 0); - } - return 0; -} - -MIR_APP_DLL(int) Srmm_ModifyButton(BBButton *bbdi) -{ - if (!bbdi) - return 1; - - CustomButtonData *cbd = nullptr; - { - mir_cslock lck(csToolBar); - - for (auto &p : arButtonsList) - if (!mir_strcmp(p->m_pszModuleName, bbdi->pszModuleName) && (p->m_dwButtonID == bbdi->dwButtonID)) { - cbd = p; - break; - } - - if (cbd != nullptr) { - if (bbdi->pwszTooltip) - cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip); - if (bbdi->hIcon) - cbd->m_hIcon = bbdi->hIcon; - if (bbdi->bbbFlags) { - cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; - cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; - cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; - cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; - cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; - cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; - } - } - } - - if (cbd != nullptr) - WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, (LPARAM)cbd); - return 0; -} - -MIR_APP_DLL(void) Srmm_ClickToolbarIcon(MCONTACT hContact, int idFrom, HWND hwndDlg, BOOL code) -{ - bool bFromArrow = false; - HWND hwndFrom = nullptr; - - CustomButtonClickData cbcd = {}; - - for (auto &cbd : arButtonsList) { - if (cbd->m_dwButtonCID == idFrom) { - cbcd.pszModule = cbd->m_pszModuleName; - cbcd.dwButtonId = cbd->m_dwButtonID; - hwndFrom = GetDlgItem(hwndDlg, idFrom); - } - else if (cbd->m_dwArrowCID == idFrom) { - bFromArrow = true; - cbcd.pszModule = cbd->m_pszModuleName; - cbcd.dwButtonId = cbd->m_dwButtonID; - hwndFrom = GetDlgItem(hwndDlg, idFrom-1); - } - } - - if (hwndFrom == nullptr) - return; - - RECT rc; - GetWindowRect(hwndFrom, &rc); - cbcd.pt.x = rc.left; - cbcd.pt.y = rc.bottom; - - cbcd.hwndFrom = GetParent(hwndFrom); - cbcd.hContact = hContact; - cbcd.flags = (code ? BBCF_RIGHTBUTTON : 0) | (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0) | (bFromArrow ? BBCF_ARROWCLICKED : 0); - - NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd); -} - -void Srmm_ProcessToolbarHotkey(MCONTACT hContact, INT_PTR iButtonFrom, HWND hwndDlg) -{ - HWND hwndFrom = nullptr; - - CustomButtonClickData cbcd = {}; - - for (auto &cbd : arButtonsList) { - if (cbd->m_hotkey == nullptr || cbd->m_bDisabled) - continue; - - if (cbd->m_hotkey->lParam == iButtonFrom) { - cbcd.pszModule = cbd->m_pszModuleName; - cbcd.dwButtonId = cbd->m_dwButtonID; - hwndFrom = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - break; - } - } - - if (hwndFrom == nullptr) - return; - - RECT rc; - GetWindowRect(hwndFrom, &rc); - cbcd.pt.x = rc.left; - cbcd.pt.y = rc.bottom; - - cbcd.hwndFrom = GetParent(hwndFrom); - cbcd.hContact = hContact; - cbcd.flags = (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0); - - NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd); -} - -MIR_APP_DLL(void) Srmm_ResetToolbar() -{ - for (auto &cbd : arButtonsList) { - cbd->m_dwPosition = cbd->m_dwOrigPosition; - cbd->m_bRSided = cbd->m_dwOrigFlags.bit1; - cbd->m_bIMButton = cbd->m_dwOrigFlags.bit2; - cbd->m_bChatButton = cbd->m_dwOrigFlags.bit3; - cbd->m_bCanBeHidden = cbd->m_dwOrigFlags.bit4; - } -} - -void Srmm_CreateToolbarIcons(HWND hwndDlg, int flags) -{ - HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hwndDlg, GWLP_HINSTANCE); - - CDlgBase *pDlg = CDlgBase::Find(hwndDlg); - - for (auto &cbd : arButtonsList) { - if (cbd->m_bSeparator) - continue; - - HWND hwndButton = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - if ((flags & BBBF_ISIMBUTTON) && cbd->m_bIMButton || (flags & BBBF_ISCHATBUTTON) && cbd->m_bChatButton) { - if (hwndButton == nullptr) { - hwndButton = CreateWindowEx(0, L"MButtonClass", L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, cbd->m_iButtonWidth, DPISCALEX_S(22), hwndDlg, (HMENU)cbd->m_dwButtonCID, hInstance, nullptr); - if (hwndButton == nullptr) // smth went wrong - continue; - - // if there's a pre-created button control in a class, initialize it - if (pDlg != nullptr) { - CCtrlBase *pControl = (*pDlg)[cbd->m_dwButtonCID]; - if (pControl) - pControl->OnInit(); - } - } - SendMessage(hwndButton, BUTTONSETASFLATBTN, TRUE, 0); - if (cbd->m_pwszText) - SetWindowTextW(hwndButton, cbd->m_pwszText); - if (cbd->m_pwszTooltip) - SendMessage(hwndButton, BUTTONADDTOOLTIP, LPARAM(cbd->m_pwszTooltip), BATF_UNICODE); - if (cbd->m_hIcon) - SendMessage(hwndButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon)); - - if (cbd->m_dwArrowCID) - SendMessage(hwndButton, BUTTONSETARROW, cbd->m_dwArrowCID, 0); - if (cbd->m_bPushButton) - SendMessage(hwndButton, BUTTONSETASPUSHBTN, TRUE, 0); - - if (cbd->m_bDisabled) - EnableWindow(hwndButton, FALSE); - if (cbd->m_bHidden) - ShowWindow(hwndButton, SW_HIDE); - } - else if (hwndButton) - DestroyWindow(hwndButton); - } -} - -MIR_APP_DLL(void) Srmm_UpdateToolbarIcons(HWND hwndDlg) -{ - for (auto &cbd : arButtonsList) { - if (cbd->m_bSeparator || cbd->m_hIcon == nullptr) - continue; - - HWND hwndBtn = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - if (hwndBtn) - SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon)); - } -} - -MIR_APP_DLL(void) Srmm_RedrawToolbarIcons(HWND hwndDlg) -{ - for (auto &cbd : arButtonsList) { - HWND hwnd = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - if (hwnd) - InvalidateRect(hwnd, nullptr, TRUE); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void CB_ReInitCustomButtons() -{ - for (auto &cbd : arButtonsList.rev_iter()) - if (cbd->m_opFlags & (BBSF_NTBSWAPED | BBSF_NTBDESTRUCT)) { - cbd->m_opFlags ^= BBSF_NTBSWAPED; - - if (cbd->m_opFlags & BBSF_NTBDESTRUCT) - arButtonsList.removeItem(&cbd); - } - - qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); - - WindowList_Broadcast(g_hWindowList, WM_CBD_RECREATE, 0, 0); - WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0); - WindowList_Broadcast(g_hWindowList, WM_CBD_LOADICONS, 0, 0); -} - -static void CB_WriteButtonSettings(MCONTACT hContact, CustomButtonData *cbd) -{ - char SettingName[1024]; - char SettingParameter[1024]; - - //modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden - - mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID); - mir_snprintf(SettingParameter, "%d_%u_%u_%u_%u_%u", cbd->m_dwPosition, cbd->m_bIMButton, cbd->m_bChatButton, 0, cbd->m_bRSided, cbd->m_bCanBeHidden); - if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT)) - db_set_s(hContact, BB_MODULE_NAME, SettingName, SettingParameter); - else - db_unset(hContact, BB_MODULE_NAME, SettingName); -} - -#define MIDDLE_SEPARATOR L">-------M-------<" - -class CSrmmToolbarOptions : public CDlgBase -{ - CCtrlTreeView m_toolBar; - CCtrlCheck m_btnIM, m_btnChat, m_btnHidden; - CCtrlButton m_btnReset, m_btnSeparator; - CCtrlSpin m_gap; - CTimer timer; - - HIMAGELIST m_hImgl; - - void SaveTree() - { - bool RSide = false; - int count = 10; - uint32_t loc_sepcout = 0; - wchar_t strbuf[128]; - - TVITEMEX tvi; - tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE; - tvi.hItem = m_toolBar.GetRoot(); - tvi.pszText = strbuf; - tvi.cchTextMax = _countof(strbuf); - { - mir_cslock lck(csToolBar); - - while (tvi.hItem != nullptr) { - m_toolBar.GetItem(&tvi); - - if (mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR) == 0) { - RSide = true; - count = m_toolBar.GetCount() * 10 - count; - tvi.hItem = m_toolBar.GetNextSibling(tvi.hItem); - continue; - } - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - if (cbd && arButtonsList.indexOf(cbd) != -1) { - if (cbd->m_opFlags) { - cbd->m_bIMButton = (cbd->m_opFlags & BBSF_IMBUTTON) != 0; - cbd->m_bChatButton = (cbd->m_opFlags & BBSF_CHATBUTTON) != 0; - cbd->m_bCanBeHidden = (cbd->m_opFlags & BBSF_CANBEHIDDEN) != 0; - } - - if (RSide && !cbd->m_bRSided) { - cbd->m_bRSided = true; - cbd->m_opFlags |= BBSF_NTBSWAPED; - } - else if (!RSide && cbd->m_bRSided) { - cbd->m_bRSided = false; - cbd->m_opFlags |= BBSF_NTBSWAPED; - } - - if (!cbd->m_bCantBeHidden && !m_toolBar.GetCheckState(tvi.hItem)) { - cbd->m_bIMButton = false; - cbd->m_bChatButton = false; - - if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep")) - cbd->m_opFlags = BBSF_NTBDESTRUCT; - } - else { - if (!cbd->m_bIMButton && !cbd->m_bChatButton) - cbd->m_bIMButton = true; - if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep")) { - cbd->m_bHidden = false; - cbd->m_opFlags &= ~BBSF_NTBDESTRUCT; - ++loc_sepcout; - } - } - - cbd->m_dwPosition = (uint32_t)count; - CB_WriteButtonSettings(0, cbd); - - if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT)) - (RSide) ? (count -= 10) : (count += 10); - } - - HTREEITEM hItem = m_toolBar.GetNextSibling(tvi.hItem); - if (cbd->m_opFlags & BBSF_NTBDESTRUCT) - m_toolBar.DeleteItem(tvi.hItem); - tvi.hItem = hItem; - } - - qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); - } - db_set_dw(0, BB_MODULE_NAME, "SeparatorsCount", loc_sepcout); - dwSepCount = loc_sepcout; - } - - void BuildMenuObjectsTree() - { - m_toolBar.DeleteAllItems(); - - m_hImgl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 2, 2); - ImageList_AddIcon(m_hImgl, Skin_LoadIcon(SKINICON_OTHER_SMALLDOT)); - ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL)); - m_toolBar.SetImageList(m_hImgl, TVSIL_NORMAL); - - if (arButtonsList.getCount() == 0) - return; - - bool bPrevSide = false; - - TVINSERTSTRUCT tvis; - tvis.hParent = nullptr; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE; - - mir_cslock lck(csToolBar); - for (auto &cbd : arButtonsList) { - if (bPrevSide != cbd->m_bRSided) { - bPrevSide = true; - - TVINSERTSTRUCT tvis2 = {}; - tvis.hInsertAfter = TVI_LAST; - tvis2.itemex.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_STATE | TVIF_STATEEX; - tvis2.itemex.pszText = MIDDLE_SEPARATOR; - tvis2.itemex.stateMask = TVIS_BOLD; - tvis2.itemex.state = TVIS_BOLD; - tvis2.itemex.iImage = tvis.item.iSelectedImage = -1; - tvis2.itemex.uStateEx = TVIS_EX_DISABLED; - tvis.hInsertAfter = m_toolBar.InsertItem(&tvis2); - m_toolBar.SetItemState(tvis.hInsertAfter, 0x3000, TVIS_STATEIMAGEMASK); - } - - tvis.item.lParam = (LPARAM)cbd; - - if (cbd->m_bSeparator) { - tvis.item.pszText = TranslateT("<Separator>"); - tvis.item.iImage = tvis.item.iSelectedImage = 0; - } - else { - tvis.item.pszText = TranslateW(cbd->m_pwszTooltip); - tvis.item.iImage = tvis.item.iSelectedImage = ImageList_AddIcon(m_hImgl, IcoLib_GetIconByHandle(cbd->m_hIcon)); - } - cbd->m_opFlags = 0; - HTREEITEM hti = m_toolBar.InsertItem(&tvis); - - m_toolBar.SetCheckState(hti, (cbd->m_bIMButton || cbd->m_bChatButton)); - if (cbd->m_bCantBeHidden) - m_toolBar.SetItemState(hti, 0x3000, TVIS_STATEIMAGEMASK); - } - } - -public: - CSrmmToolbarOptions() : - CDlgBase(g_plugin, IDD_OPT_TOOLBAR), - m_gap(this, IDC_SPIN1, 10), - m_btnIM(this, IDC_IMCHECK), - m_btnChat(this, IDC_CHATCHECK), - m_toolBar(this, IDC_TOOLBARTREE), - m_btnReset(this, IDC_BBRESET), - m_btnHidden(this, IDC_CANBEHIDDEN), - m_btnSeparator(this, IDC_SEPARATOR), - m_hImgl(nullptr), - timer(this, 1) - { - timer.OnEvent = Callback(this, &CSrmmToolbarOptions::OnTimer); - - m_toolBar.SetFlags(MTREE_DND); // enable drag-n-drop - m_toolBar.OnSelChanged = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanged); - m_toolBar.OnSelChanging = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanging); - m_toolBar.OnItemChanged = Callback(this, &CSrmmToolbarOptions::OnTreeItemChanged); - - m_btnReset.OnClick = Callback(this, &CSrmmToolbarOptions::btnResetClicked); - m_btnSeparator.OnClick = Callback(this, &CSrmmToolbarOptions::btnSeparatorClicked); - } - - bool OnInitDialog() override - { - g_pDialog = this; - BuildMenuObjectsTree(); - - m_btnIM.Disable(); - m_btnChat.Disable(); - m_btnHidden.Disable(); - - m_gap.SetPosition(g_iButtonGap); - return true; - } - - void OnDestroy() override - { - g_pDialog = nullptr; - ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL)); - ImageList_Destroy(m_toolBar.GetImageList(TVSIL_STATE)); - } - - bool OnApply() override - { - OnTreeSelChanging(nullptr); // save latest changes - SaveTree(); // save the whole tree then - CB_ReInitCustomButtons(); - Chat_UpdateOptions(); // also restore chat windows - - uint16_t newGap = m_gap.GetPosition(); - if (newGap != g_iButtonGap) { - g_iButtonGap = newGap; - WindowList_BroadcastAsync(g_hWindowList, WM_SIZE, 0, 0); - } - - BuildMenuObjectsTree(); - - m_btnIM.Disable(); - m_btnChat.Disable(); - m_btnHidden.Disable(); - return true; - } - - virtual void OnReset() override - { - CB_ReInitCustomButtons(); - dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0); - } - - void btnResetClicked(void*) - { - db_delete_module(0, BB_MODULE_NAME); - - Srmm_ResetToolbar(); - qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); - - BuildMenuObjectsTree(); - NotifyChange(); - } - - void btnSeparatorClicked(void*) - { - HTREEITEM hItem = m_toolBar.GetSelection(); - if (!hItem) - hItem = TVI_FIRST; - - BBButton bbd = {}; - bbd.pszModuleName = "Tabsrmm_sep"; - bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON; - bbd.dwButtonID = ++dwSepCount; - - CustomButtonData *cbd = (CustomButtonData*)Srmm_AddButton(&bbd, &g_plugin); - - TVINSERTSTRUCT tvis; - tvis.hParent = nullptr; - tvis.hInsertAfter = hItem; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvis.item.pszText = TranslateT("<Separator>"); - tvis.item.iImage = tvis.item.iSelectedImage = -1; - tvis.item.lParam = (LPARAM)cbd; - hItem = m_toolBar.InsertItem(&tvis); - - m_toolBar.SetCheckState(hItem, (cbd->m_bIMButton || cbd->m_bChatButton)); - NotifyChange(); - } - - void OnTreeSelChanging(void*) - { - HTREEITEM hItem = m_toolBar.GetSelection(); - if (hItem == nullptr) - return; - - wchar_t strbuf[128]; - TVITEMEX tvi; - tvi.hItem = hItem; - tvi.pszText = strbuf; - tvi.cchTextMax = _countof(strbuf); - tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; - m_toolBar.GetItem(&tvi); - - if (tvi.lParam == 0 || !m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR)) - return; - - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - cbd->m_bIMButton = m_btnIM.GetState() != 0; - cbd->m_bChatButton = m_btnChat.GetState() != 0; - cbd->m_bCanBeHidden = !cbd->m_bCantBeHidden && m_btnHidden.GetState() != 0; - cbd->m_opFlags = (cbd->m_bIMButton ? BBSF_IMBUTTON : 0) + (cbd->m_bChatButton ? BBSF_CHATBUTTON : 0) + (cbd->m_bCanBeHidden ? BBSF_CANBEHIDDEN : 0); - - if (!cbd->m_bChatButton && !cbd->m_bIMButton) - m_toolBar.SetCheckState(tvi.hItem, 0); - } - - void OnTreeSelChanged(void*) - { - HTREEITEM hItem = m_toolBar.GetSelection(); - if (hItem == nullptr) - return; - - wchar_t strbuf[128]; - TVITEMEX tvi; - tvi.pszText = strbuf; - tvi.cchTextMax = _countof(strbuf); - tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hItem; - m_toolBar.GetItem(&tvi); - - if (!m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR)) { - m_btnIM.Disable(); - m_btnChat.Disable(); - m_btnHidden.Disable(); - return; - } - - if (tvi.lParam == 0) - return; - - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - m_btnIM.Enable(); m_btnIM.SetState(cbd->m_bIMButton); - m_btnChat.Enable(); m_btnChat.SetState(cbd->m_bChatButton); - m_btnHidden.Enable(); m_btnHidden.SetState(cbd->m_bCanBeHidden); - } - - void OnTreeItemChanged(CCtrlTreeView::TEventInfo *evt) - { - bool iNewState = !m_toolBar.GetCheckState(evt->hItem); - m_btnIM.Enable(iNewState); - m_btnChat.Enable(iNewState); - m_btnHidden.Enable(iNewState); - if (iNewState) - m_btnIM.SetState(true); - } - - void OnTimer(CTimer *pTimer) - { - pTimer->Stop(); - BuildMenuObjectsTree(); - } - - static void RereadButtons() - { - if (g_pDialog) - g_pDialog->timer.Start(100); - } -}; - -void SrmmLogOptionsInit(WPARAM wParam); - -static int SrmmOptionsInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 910000000; - odp.szGroup.a = LPGEN("Message sessions"); - odp.szTitle.a = LPGEN("Toolbar"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CSrmmToolbarOptions(); - g_plugin.addOptions(wParam, &odp); - - ChatOptionsInit(wParam); - SrmmLogOptionsInit(wParam); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HANDLE) Srmm_AddButton(const BBButton *bbdi, HPLUGIN _hLang) -{ - if (bbdi == nullptr) - return nullptr; - - CustomButtonData *cbd = new CustomButtonData(); - cbd->m_pszModuleName = mir_strdup(bbdi->pszModuleName); - cbd->m_pwszText = mir_wstrdup(bbdi->pwszText); - cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip); - - cbd->m_dwButtonID = bbdi->dwButtonID; - cbd->m_hIcon = bbdi->hIcon; - cbd->m_dwPosition = cbd->m_dwOrigPosition = bbdi->dwDefPos; - cbd->m_dwButtonCID = (bbdi->bbbFlags & BBBF_CREATEBYID) ? bbdi->dwButtonID : LastCID; - cbd->m_dwArrowCID = (bbdi->bbbFlags & BBBF_ISARROWBUTTON) ? cbd->m_dwButtonCID + 1 : 0; - cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; - cbd->m_bSeparator = (bbdi->bbbFlags & BBBF_ISSEPARATOR) != 0; - - cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; - cbd->m_bPushButton = (bbdi->bbbFlags & BBBF_ISPUSHBUTTON) != 0; - cbd->m_pPlugin = _hLang; - - cbd->m_dwOrigFlags.bit1 = cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; - cbd->m_dwOrigFlags.bit2 = cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; - cbd->m_dwOrigFlags.bit3 = cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; - cbd->m_dwOrigFlags.bit4 = cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; - - if (cbd->m_bSeparator) - cbd->m_iButtonWidth = DPISCALEX_S(10); - else if (bbdi->bbbFlags & BBBF_ISARROWBUTTON) - cbd->m_iButtonWidth = DPISCALEX_S(34); - else - cbd->m_iButtonWidth = DPISCALEX_S(22); - - if (bbdi->pszHotkey) { - for (auto &p : hotkeys) { - if (!mir_strcmp(p->getName(), bbdi->pszHotkey)) { - cbd->m_hotkey = p; - break; - } - } - } - - // download database settings - char SettingName[1024]; - mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID); - - DBVARIANT dbv = { 0 }; - if (!db_get_s(0, BB_MODULE_NAME, SettingName, &dbv)) { - // modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden - char *token = strtok(dbv.pszVal, "_"); - cbd->m_dwPosition = (uint32_t)atoi(token); - token = strtok(nullptr, "_"); - cbd->m_bIMButton = atoi(token) != 0; - token = strtok(nullptr, "_"); - cbd->m_bChatButton = atoi(token) != 0; - token = strtok(nullptr, "_"); - token = strtok(nullptr, "_"); - cbd->m_bRSided = atoi(token) != 0; - token = strtok(nullptr, "_"); - cbd->m_bCanBeHidden = atoi(token) != 0; - - db_free(&dbv); - } - - arButtonsList.insert(cbd); - - if (cbd->m_dwButtonCID != cbd->m_dwButtonID) - LastCID++; - if (cbd->m_dwArrowCID == LastCID) - LastCID++; - - WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0); - CSrmmToolbarOptions::RereadButtons(); - return cbd; -} - -MIR_APP_DLL(int) Srmm_RemoveButton(BBButton *bbdi) -{ - if (!bbdi) - return 1; - - CustomButtonData *pFound = nullptr; - { - mir_cslock lck(csToolBar); - - for (auto &cbd : arButtonsList.rev_iter()) - if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && cbd->m_dwButtonID == bbdi->dwButtonID) - pFound = arButtonsList.removeItem(&cbd); - } - - if (pFound) { - CSrmmToolbarOptions::RereadButtons(); - WindowList_Broadcast(g_hWindowList, WM_CBD_REMOVED, pFound->m_dwButtonCID, (LPARAM)pFound); - delete pFound; - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void KillModuleToolbarIcons(CMPluginBase *pPlugin) -{ - int oldCount = arButtonsList.getCount(); - - auto T = arButtonsList.rev_iter(); - for (auto &cbd : T) - if (cbd->m_pPlugin == pPlugin) - delete arButtonsList.removeItem(&cbd); - - if (oldCount != arButtonsList.getCount()) - CSrmmToolbarOptions::RereadButtons(); -} - -static INT_PTR BroadcastMessage(WPARAM, LPARAM lParam) -{ - Srmm_Broadcast((UINT)lParam, 0, 0); - return 0; -} - -static void CALLBACK SrmmLoadToolbar() -{ - NotifyEventHooks(hHookToolBarLoadedEvt, 0, 0); - DestroyHookableEvent(hHookToolBarLoadedEvt); - - HookEvent(ME_OPT_INITIALISE, SrmmOptionsInit); -} - -static int ConvertToolbarData(const char *szSetting, void*) -{ - DBVARIANT dbv; - if (!db_get(0, "Tab" BB_MODULE_NAME, szSetting, &dbv)) { - db_set(0, BB_MODULE_NAME, szSetting, &dbv); - db_free(&dbv); - } - return 0; -} - -void LoadSrmmToolbarModule() -{ - CreateServiceFunction("SRMsg/BroadcastMessage", BroadcastMessage); - - Miranda_WaitOnHandle(SrmmLoadToolbar); - - hHookButtonPressedEvt = CreateHookableEvent(ME_MSG_BUTTONPRESSED); - hHookToolBarLoadedEvt = CreateHookableEvent(ME_MSG_TOOLBARLOADED); - - HDC hScrnDC = GetDC(nullptr); - g_DPIscaleX = GetDeviceCaps(hScrnDC, LOGPIXELSX) / 96.0; - g_DPIscaleY = GetDeviceCaps(hScrnDC, LOGPIXELSY) / 96.0; - ReleaseDC(nullptr, hScrnDC); - - // old data? convert them - if (db_get_dw(0, "Tab" BB_MODULE_NAME, "SeparatorsCount", -1) != -1) { - db_enum_settings(0, ConvertToolbarData, "Tab" BB_MODULE_NAME, nullptr); - db_delete_module(0, "Tab" BB_MODULE_NAME); - } - - dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0); - CB_RegisterSeparators(); -} - -void UnloadSrmmToolbarModule() -{ - DestroyHookableEvent(hHookButtonPressedEvt); - - for (auto &it : arButtonsList) - delete it; - arButtonsList.destroy(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "chat.h"
+#include "skin.h"
+
+#define BB_MODULE_NAME "SRMM_Toolbar"
+
+#define DPISCALEY_S(argY) ((int)((double)(argY) * g_DPIscaleY))
+#define DPISCALEX_S(argX) ((int)((double)(argX) * g_DPIscaleX))
+
+static double g_DPIscaleX, g_DPIscaleY;
+static class CSrmmToolbarOptions *g_pDialog = nullptr;
+
+static CMOption<uint8_t> g_iButtonGap(BB_MODULE_NAME, "ButtonsBarGap", 1);
+
+static int SortButtons(const CustomButtonData *p1, const CustomButtonData *p2)
+{
+ if (p1->m_bRSided != p2->m_bRSided)
+ return (p2->m_bRSided) ? -1 : 1;
+ if (p1->m_dwPosition != p2->m_dwPosition)
+ return p1->m_dwPosition - p2->m_dwPosition;
+ int res = mir_strcmp(p1->m_pszModuleName, p2->m_pszModuleName);
+ if (res != 0)
+ return res;
+ return p1->m_dwButtonID - p2->m_dwButtonID;
+}
+
+static LIST<CustomButtonData> arButtonsList(1, SortButtons);
+
+int LastCID = MIN_CBUTTONID;
+int dwSepCount = 0;
+
+static mir_cs csToolBar;
+static HANDLE hHookToolBarLoadedEvt, hHookButtonPressedEvt;
+
+static int sstSortButtons(const void *p1, const void *p2)
+{
+ return SortButtons(*(CustomButtonData**)p1, *(CustomButtonData**)p2);
+}
+
+static void CB_RegisterSeparators()
+{
+ BBButton bbd = {};
+ bbd.pszModuleName = "Tabsrmm_sep";
+ for (int i = 0; dwSepCount > i; i++) {
+ bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON;
+ bbd.dwButtonID = i + 1;
+ bbd.dwDefPos = 410 + i;
+ Srmm_AddButton(&bbd, &g_plugin);
+ }
+}
+
+MIR_APP_DLL(CustomButtonData*) Srmm_GetNthButton(int i)
+{
+ return arButtonsList[i];
+}
+
+MIR_APP_DLL(int) Srmm_GetButtonCount(void)
+{
+ return arButtonsList.getCount();
+}
+
+MIR_APP_DLL(int) Srmm_GetButtonGap()
+{
+ return g_iButtonGap;
+}
+
+MIR_APP_DLL(int) Srmm_GetButtonState(HWND hwndDlg, BBButton *bbdi)
+{
+ if (hwndDlg == nullptr || bbdi == nullptr)
+ return 1;
+
+ uint32_t tempCID = 0;
+ bbdi->bbbFlags = 0;
+ for (auto &cbd : arButtonsList)
+ if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) {
+ tempCID = cbd->m_dwButtonCID;
+ break;
+ }
+
+ if (!tempCID)
+ return 1;
+
+ HWND hwndBtn = GetDlgItem(hwndDlg, tempCID);
+ bbdi->bbbFlags = (IsDlgButtonChecked(hwndDlg, tempCID) ? BBSF_PUSHED : BBSF_RELEASED) | (IsWindowVisible(hwndBtn) ? 0 : BBSF_HIDDEN) | (IsWindowEnabled(hwndBtn) ? 0 : BBSF_DISABLED);
+ return 0;
+}
+
+MIR_APP_DLL(int) Srmm_SetButtonState(MCONTACT hContact, BBButton *bbdi)
+{
+ if (hContact == 0 || bbdi == nullptr)
+ return 1;
+
+ uint32_t tempCID = 0;
+ for (auto &cbd : arButtonsList)
+ if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) {
+ tempCID = cbd->m_dwButtonCID;
+ break;
+ }
+
+ if (!tempCID)
+ return 1;
+
+ HWND hwndDlg = WindowList_Find(g_hWindowList, hContact);
+ if (hwndDlg == nullptr)
+ return 1;
+
+ HWND hwndBtn = GetDlgItem(hwndDlg, tempCID);
+ if (hwndBtn == nullptr)
+ return 1;
+
+ SetWindowTextA(hwndBtn, bbdi->pszModuleName);
+ if (bbdi->hIcon)
+ SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(bbdi->hIcon));
+ if (bbdi->pwszTooltip)
+ SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)bbdi->pwszTooltip, BATF_UNICODE);
+ if (bbdi->bbbFlags) {
+ ShowWindow(hwndBtn, (bbdi->bbbFlags & BBSF_HIDDEN) ? SW_HIDE : SW_SHOW);
+ EnableWindow(hwndBtn, !(bbdi->bbbFlags & BBSF_DISABLED));
+ Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_PUSHED) != 0);
+ Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_RELEASED) == 0);
+ }
+ return 0;
+}
+
+MIR_APP_DLL(int) Srmm_ModifyButton(BBButton *bbdi)
+{
+ if (!bbdi)
+ return 1;
+
+ CustomButtonData *cbd = nullptr;
+ {
+ mir_cslock lck(csToolBar);
+
+ for (auto &p : arButtonsList)
+ if (!mir_strcmp(p->m_pszModuleName, bbdi->pszModuleName) && (p->m_dwButtonID == bbdi->dwButtonID)) {
+ cbd = p;
+ break;
+ }
+
+ if (cbd != nullptr) {
+ if (bbdi->pwszTooltip)
+ cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip);
+ if (bbdi->hIcon)
+ cbd->m_hIcon = bbdi->hIcon;
+ if (bbdi->bbbFlags) {
+ cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0;
+ cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0;
+ cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0;
+ cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0;
+ cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0;
+ cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0;
+ }
+ }
+ }
+
+ if (cbd != nullptr)
+ WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, (LPARAM)cbd);
+ return 0;
+}
+
+MIR_APP_DLL(void) Srmm_ClickToolbarIcon(MCONTACT hContact, int idFrom, HWND hwndDlg, BOOL code)
+{
+ bool bFromArrow = false;
+ HWND hwndFrom = nullptr;
+
+ CustomButtonClickData cbcd = {};
+
+ for (auto &cbd : arButtonsList) {
+ if (cbd->m_dwButtonCID == idFrom) {
+ cbcd.pszModule = cbd->m_pszModuleName;
+ cbcd.dwButtonId = cbd->m_dwButtonID;
+ hwndFrom = GetDlgItem(hwndDlg, idFrom);
+ }
+ else if (cbd->m_dwArrowCID == idFrom) {
+ bFromArrow = true;
+ cbcd.pszModule = cbd->m_pszModuleName;
+ cbcd.dwButtonId = cbd->m_dwButtonID;
+ hwndFrom = GetDlgItem(hwndDlg, idFrom-1);
+ }
+ }
+
+ if (hwndFrom == nullptr)
+ return;
+
+ RECT rc;
+ GetWindowRect(hwndFrom, &rc);
+ cbcd.pt.x = rc.left;
+ cbcd.pt.y = rc.bottom;
+
+ cbcd.hwndFrom = GetParent(hwndFrom);
+ cbcd.hContact = hContact;
+ cbcd.flags = (code ? BBCF_RIGHTBUTTON : 0) | (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0) | (bFromArrow ? BBCF_ARROWCLICKED : 0);
+
+ NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd);
+}
+
+void Srmm_ProcessToolbarHotkey(MCONTACT hContact, INT_PTR iButtonFrom, HWND hwndDlg)
+{
+ HWND hwndFrom = nullptr;
+
+ CustomButtonClickData cbcd = {};
+
+ for (auto &cbd : arButtonsList) {
+ if (cbd->m_hotkey == nullptr || cbd->m_bDisabled)
+ continue;
+
+ if (cbd->m_hotkey->lParam == iButtonFrom) {
+ cbcd.pszModule = cbd->m_pszModuleName;
+ cbcd.dwButtonId = cbd->m_dwButtonID;
+ hwndFrom = GetDlgItem(hwndDlg, cbd->m_dwButtonCID);
+ break;
+ }
+ }
+
+ if (hwndFrom == nullptr)
+ return;
+
+ RECT rc;
+ GetWindowRect(hwndFrom, &rc);
+ cbcd.pt.x = rc.left;
+ cbcd.pt.y = rc.bottom;
+
+ cbcd.hwndFrom = GetParent(hwndFrom);
+ cbcd.hContact = hContact;
+ cbcd.flags = (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0);
+
+ NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd);
+}
+
+MIR_APP_DLL(void) Srmm_ResetToolbar()
+{
+ for (auto &cbd : arButtonsList) {
+ cbd->m_dwPosition = cbd->m_dwOrigPosition;
+ cbd->m_bRSided = cbd->m_dwOrigFlags.bit1;
+ cbd->m_bIMButton = cbd->m_dwOrigFlags.bit2;
+ cbd->m_bChatButton = cbd->m_dwOrigFlags.bit3;
+ cbd->m_bCanBeHidden = cbd->m_dwOrigFlags.bit4;
+ }
+}
+
+void Srmm_CreateToolbarIcons(HWND hwndDlg, int flags)
+{
+ HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hwndDlg, GWLP_HINSTANCE);
+
+ CDlgBase *pDlg = CDlgBase::Find(hwndDlg);
+
+ for (auto &cbd : arButtonsList) {
+ if (cbd->m_bSeparator)
+ continue;
+
+ HWND hwndButton = GetDlgItem(hwndDlg, cbd->m_dwButtonCID);
+ if ((flags & BBBF_ISIMBUTTON) && cbd->m_bIMButton || (flags & BBBF_ISCHATBUTTON) && cbd->m_bChatButton) {
+ if (hwndButton == nullptr) {
+ hwndButton = CreateWindowEx(0, L"MButtonClass", L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, cbd->m_iButtonWidth, DPISCALEX_S(22), hwndDlg, (HMENU)cbd->m_dwButtonCID, hInstance, nullptr);
+ if (hwndButton == nullptr) // smth went wrong
+ continue;
+
+ // if there's a pre-created button control in a class, initialize it
+ if (pDlg != nullptr) {
+ CCtrlBase *pControl = (*pDlg)[cbd->m_dwButtonCID];
+ if (pControl)
+ pControl->OnInit();
+ }
+ }
+ SendMessage(hwndButton, BUTTONSETASFLATBTN, TRUE, 0);
+ if (cbd->m_pwszText)
+ SetWindowTextW(hwndButton, cbd->m_pwszText);
+ if (cbd->m_pwszTooltip)
+ SendMessage(hwndButton, BUTTONADDTOOLTIP, LPARAM(cbd->m_pwszTooltip), BATF_UNICODE);
+ if (cbd->m_hIcon)
+ SendMessage(hwndButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon));
+
+ if (cbd->m_dwArrowCID)
+ SendMessage(hwndButton, BUTTONSETARROW, cbd->m_dwArrowCID, 0);
+ if (cbd->m_bPushButton)
+ SendMessage(hwndButton, BUTTONSETASPUSHBTN, TRUE, 0);
+
+ if (cbd->m_bDisabled)
+ EnableWindow(hwndButton, FALSE);
+ if (cbd->m_bHidden)
+ ShowWindow(hwndButton, SW_HIDE);
+ }
+ else if (hwndButton)
+ DestroyWindow(hwndButton);
+ }
+}
+
+MIR_APP_DLL(void) Srmm_UpdateToolbarIcons(HWND hwndDlg)
+{
+ for (auto &cbd : arButtonsList) {
+ if (cbd->m_bSeparator || cbd->m_hIcon == nullptr)
+ continue;
+
+ HWND hwndBtn = GetDlgItem(hwndDlg, cbd->m_dwButtonCID);
+ if (hwndBtn)
+ SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon));
+ }
+}
+
+MIR_APP_DLL(void) Srmm_RedrawToolbarIcons(HWND hwndDlg)
+{
+ for (auto &cbd : arButtonsList) {
+ HWND hwnd = GetDlgItem(hwndDlg, cbd->m_dwButtonCID);
+ if (hwnd)
+ InvalidateRect(hwnd, nullptr, TRUE);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void CB_ReInitCustomButtons()
+{
+ for (auto &cbd : arButtonsList.rev_iter())
+ if (cbd->m_opFlags & (BBSF_NTBSWAPED | BBSF_NTBDESTRUCT)) {
+ cbd->m_opFlags ^= BBSF_NTBSWAPED;
+
+ if (cbd->m_opFlags & BBSF_NTBDESTRUCT)
+ arButtonsList.removeItem(&cbd);
+ }
+
+ qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons);
+
+ WindowList_Broadcast(g_hWindowList, WM_CBD_RECREATE, 0, 0);
+ WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0);
+ WindowList_Broadcast(g_hWindowList, WM_CBD_LOADICONS, 0, 0);
+}
+
+static void CB_WriteButtonSettings(MCONTACT hContact, CustomButtonData *cbd)
+{
+ char SettingName[1024];
+ char SettingParameter[1024];
+
+ //modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden
+
+ mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID);
+ mir_snprintf(SettingParameter, "%d_%u_%u_%u_%u_%u", cbd->m_dwPosition, cbd->m_bIMButton, cbd->m_bChatButton, 0, cbd->m_bRSided, cbd->m_bCanBeHidden);
+ if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT))
+ db_set_s(hContact, BB_MODULE_NAME, SettingName, SettingParameter);
+ else
+ db_unset(hContact, BB_MODULE_NAME, SettingName);
+}
+
+#define MIDDLE_SEPARATOR L">-------M-------<"
+
+class CSrmmToolbarOptions : public CDlgBase
+{
+ CCtrlTreeView m_toolBar;
+ CCtrlCheck m_btnIM, m_btnChat, m_btnHidden;
+ CCtrlButton m_btnReset, m_btnSeparator;
+ CCtrlSpin m_gap;
+ CTimer timer;
+
+ HIMAGELIST m_hImgl;
+
+ void SaveTree()
+ {
+ bool RSide = false;
+ int count = 10;
+ uint32_t loc_sepcout = 0;
+ wchar_t strbuf[128];
+
+ TVITEMEX tvi;
+ tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE;
+ tvi.hItem = m_toolBar.GetRoot();
+ tvi.pszText = strbuf;
+ tvi.cchTextMax = _countof(strbuf);
+ {
+ mir_cslock lck(csToolBar);
+
+ while (tvi.hItem != nullptr) {
+ m_toolBar.GetItem(&tvi);
+
+ if (mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR) == 0) {
+ RSide = true;
+ count = m_toolBar.GetCount() * 10 - count;
+ tvi.hItem = m_toolBar.GetNextSibling(tvi.hItem);
+ continue;
+ }
+ CustomButtonData *cbd = (CustomButtonData*)tvi.lParam;
+ if (cbd && arButtonsList.indexOf(cbd) != -1) {
+ if (cbd->m_opFlags) {
+ cbd->m_bIMButton = (cbd->m_opFlags & BBSF_IMBUTTON) != 0;
+ cbd->m_bChatButton = (cbd->m_opFlags & BBSF_CHATBUTTON) != 0;
+ cbd->m_bCanBeHidden = (cbd->m_opFlags & BBSF_CANBEHIDDEN) != 0;
+ }
+
+ if (RSide && !cbd->m_bRSided) {
+ cbd->m_bRSided = true;
+ cbd->m_opFlags |= BBSF_NTBSWAPED;
+ }
+ else if (!RSide && cbd->m_bRSided) {
+ cbd->m_bRSided = false;
+ cbd->m_opFlags |= BBSF_NTBSWAPED;
+ }
+
+ if (!cbd->m_bCantBeHidden && !m_toolBar.GetCheckState(tvi.hItem)) {
+ cbd->m_bIMButton = false;
+ cbd->m_bChatButton = false;
+
+ if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep"))
+ cbd->m_opFlags = BBSF_NTBDESTRUCT;
+ }
+ else {
+ if (!cbd->m_bIMButton && !cbd->m_bChatButton)
+ cbd->m_bIMButton = true;
+ if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep")) {
+ cbd->m_bHidden = false;
+ cbd->m_opFlags &= ~BBSF_NTBDESTRUCT;
+ ++loc_sepcout;
+ }
+ }
+
+ cbd->m_dwPosition = (uint32_t)count;
+ CB_WriteButtonSettings(0, cbd);
+
+ if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT))
+ (RSide) ? (count -= 10) : (count += 10);
+ }
+
+ HTREEITEM hItem = m_toolBar.GetNextSibling(tvi.hItem);
+ if (cbd->m_opFlags & BBSF_NTBDESTRUCT)
+ m_toolBar.DeleteItem(tvi.hItem);
+ tvi.hItem = hItem;
+ }
+
+ qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons);
+ }
+ db_set_dw(0, BB_MODULE_NAME, "SeparatorsCount", loc_sepcout);
+ dwSepCount = loc_sepcout;
+ }
+
+ void BuildMenuObjectsTree()
+ {
+ m_toolBar.DeleteAllItems();
+
+ m_hImgl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 2, 2);
+ ImageList_AddIcon(m_hImgl, Skin_LoadIcon(SKINICON_OTHER_SMALLDOT));
+ ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL));
+ m_toolBar.SetImageList(m_hImgl, TVSIL_NORMAL);
+
+ if (arButtonsList.getCount() == 0)
+ return;
+
+ bool bPrevSide = false;
+
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = nullptr;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE;
+
+ mir_cslock lck(csToolBar);
+ for (auto &cbd : arButtonsList) {
+ if (bPrevSide != cbd->m_bRSided) {
+ bPrevSide = true;
+
+ TVINSERTSTRUCT tvis2 = {};
+ tvis.hInsertAfter = TVI_LAST;
+ tvis2.itemex.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_STATE | TVIF_STATEEX;
+ tvis2.itemex.pszText = MIDDLE_SEPARATOR;
+ tvis2.itemex.stateMask = TVIS_BOLD;
+ tvis2.itemex.state = TVIS_BOLD;
+ tvis2.itemex.iImage = tvis.item.iSelectedImage = -1;
+ tvis2.itemex.uStateEx = TVIS_EX_DISABLED;
+ tvis.hInsertAfter = m_toolBar.InsertItem(&tvis2);
+ m_toolBar.SetItemState(tvis.hInsertAfter, 0x3000, TVIS_STATEIMAGEMASK);
+ }
+
+ tvis.item.lParam = (LPARAM)cbd;
+
+ if (cbd->m_bSeparator) {
+ tvis.item.pszText = TranslateT("<Separator>");
+ tvis.item.iImage = tvis.item.iSelectedImage = 0;
+ }
+ else {
+ tvis.item.pszText = TranslateW(cbd->m_pwszTooltip);
+ tvis.item.iImage = tvis.item.iSelectedImage = ImageList_AddIcon(m_hImgl, IcoLib_GetIconByHandle(cbd->m_hIcon));
+ }
+ cbd->m_opFlags = 0;
+ HTREEITEM hti = m_toolBar.InsertItem(&tvis);
+
+ m_toolBar.SetCheckState(hti, (cbd->m_bIMButton || cbd->m_bChatButton));
+ if (cbd->m_bCantBeHidden)
+ m_toolBar.SetItemState(hti, 0x3000, TVIS_STATEIMAGEMASK);
+ }
+ }
+
+public:
+ CSrmmToolbarOptions() :
+ CDlgBase(g_plugin, IDD_OPT_TOOLBAR),
+ m_gap(this, IDC_SPIN1, 10),
+ m_btnIM(this, IDC_IMCHECK),
+ m_btnChat(this, IDC_CHATCHECK),
+ m_toolBar(this, IDC_TOOLBARTREE),
+ m_btnReset(this, IDC_BBRESET),
+ m_btnHidden(this, IDC_CANBEHIDDEN),
+ m_btnSeparator(this, IDC_SEPARATOR),
+ m_hImgl(nullptr),
+ timer(this, 1)
+ {
+ timer.OnEvent = Callback(this, &CSrmmToolbarOptions::OnTimer);
+
+ m_toolBar.SetFlags(MTREE_DND); // enable drag-n-drop
+ m_toolBar.OnSelChanged = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanged);
+ m_toolBar.OnSelChanging = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanging);
+ m_toolBar.OnItemChanged = Callback(this, &CSrmmToolbarOptions::OnTreeItemChanged);
+
+ m_btnReset.OnClick = Callback(this, &CSrmmToolbarOptions::btnResetClicked);
+ m_btnSeparator.OnClick = Callback(this, &CSrmmToolbarOptions::btnSeparatorClicked);
+ }
+
+ bool OnInitDialog() override
+ {
+ g_pDialog = this;
+ BuildMenuObjectsTree();
+
+ m_btnIM.Disable();
+ m_btnChat.Disable();
+ m_btnHidden.Disable();
+
+ m_gap.SetPosition(g_iButtonGap);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ g_pDialog = nullptr;
+ ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL));
+ ImageList_Destroy(m_toolBar.GetImageList(TVSIL_STATE));
+ }
+
+ bool OnApply() override
+ {
+ OnTreeSelChanging(nullptr); // save latest changes
+ SaveTree(); // save the whole tree then
+ CB_ReInitCustomButtons();
+ Chat_UpdateOptions(); // also restore chat windows
+
+ uint16_t newGap = m_gap.GetPosition();
+ if (newGap != g_iButtonGap) {
+ g_iButtonGap = newGap;
+ WindowList_BroadcastAsync(g_hWindowList, WM_SIZE, 0, 0);
+ }
+
+ BuildMenuObjectsTree();
+
+ m_btnIM.Disable();
+ m_btnChat.Disable();
+ m_btnHidden.Disable();
+ return true;
+ }
+
+ virtual void OnReset() override
+ {
+ CB_ReInitCustomButtons();
+ dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0);
+ }
+
+ void btnResetClicked(void*)
+ {
+ db_delete_module(0, BB_MODULE_NAME);
+
+ Srmm_ResetToolbar();
+ qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons);
+
+ BuildMenuObjectsTree();
+ NotifyChange();
+ }
+
+ void btnSeparatorClicked(void*)
+ {
+ HTREEITEM hItem = m_toolBar.GetSelection();
+ if (!hItem)
+ hItem = TVI_FIRST;
+
+ BBButton bbd = {};
+ bbd.pszModuleName = "Tabsrmm_sep";
+ bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON;
+ bbd.dwButtonID = ++dwSepCount;
+
+ CustomButtonData *cbd = (CustomButtonData*)Srmm_AddButton(&bbd, &g_plugin);
+
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = nullptr;
+ tvis.hInsertAfter = hItem;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.item.pszText = TranslateT("<Separator>");
+ tvis.item.iImage = tvis.item.iSelectedImage = -1;
+ tvis.item.lParam = (LPARAM)cbd;
+ hItem = m_toolBar.InsertItem(&tvis);
+
+ m_toolBar.SetCheckState(hItem, (cbd->m_bIMButton || cbd->m_bChatButton));
+ NotifyChange();
+ }
+
+ void OnTreeSelChanging(void*)
+ {
+ HTREEITEM hItem = m_toolBar.GetSelection();
+ if (hItem == nullptr)
+ return;
+
+ wchar_t strbuf[128];
+ TVITEMEX tvi;
+ tvi.hItem = hItem;
+ tvi.pszText = strbuf;
+ tvi.cchTextMax = _countof(strbuf);
+ tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM;
+ m_toolBar.GetItem(&tvi);
+
+ if (tvi.lParam == 0 || !m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR))
+ return;
+
+ CustomButtonData *cbd = (CustomButtonData*)tvi.lParam;
+ cbd->m_bIMButton = m_btnIM.GetState() != 0;
+ cbd->m_bChatButton = m_btnChat.GetState() != 0;
+ cbd->m_bCanBeHidden = !cbd->m_bCantBeHidden && m_btnHidden.GetState() != 0;
+ cbd->m_opFlags = (cbd->m_bIMButton ? BBSF_IMBUTTON : 0) + (cbd->m_bChatButton ? BBSF_CHATBUTTON : 0) + (cbd->m_bCanBeHidden ? BBSF_CANBEHIDDEN : 0);
+
+ if (!cbd->m_bChatButton && !cbd->m_bIMButton)
+ m_toolBar.SetCheckState(tvi.hItem, 0);
+ }
+
+ void OnTreeSelChanged(void*)
+ {
+ HTREEITEM hItem = m_toolBar.GetSelection();
+ if (hItem == nullptr)
+ return;
+
+ wchar_t strbuf[128];
+ TVITEMEX tvi;
+ tvi.pszText = strbuf;
+ tvi.cchTextMax = _countof(strbuf);
+ tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hItem;
+ m_toolBar.GetItem(&tvi);
+
+ if (!m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR)) {
+ m_btnIM.Disable();
+ m_btnChat.Disable();
+ m_btnHidden.Disable();
+ return;
+ }
+
+ if (tvi.lParam == 0)
+ return;
+
+ CustomButtonData *cbd = (CustomButtonData*)tvi.lParam;
+ m_btnIM.Enable(); m_btnIM.SetState(cbd->m_bIMButton);
+ m_btnChat.Enable(); m_btnChat.SetState(cbd->m_bChatButton);
+ m_btnHidden.Enable(); m_btnHidden.SetState(cbd->m_bCanBeHidden);
+ }
+
+ void OnTreeItemChanged(CCtrlTreeView::TEventInfo *evt)
+ {
+ bool iNewState = !m_toolBar.GetCheckState(evt->hItem);
+ m_btnIM.Enable(iNewState);
+ m_btnChat.Enable(iNewState);
+ m_btnHidden.Enable(iNewState);
+ if (iNewState)
+ m_btnIM.SetState(true);
+ }
+
+ void OnTimer(CTimer *pTimer)
+ {
+ pTimer->Stop();
+ BuildMenuObjectsTree();
+ }
+
+ static void RereadButtons()
+ {
+ if (g_pDialog)
+ g_pDialog->timer.Start(100);
+ }
+};
+
+void SrmmLogOptionsInit(WPARAM wParam);
+
+static int SrmmOptionsInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 910000000;
+ odp.szGroup.a = LPGEN("Message sessions");
+ odp.szTitle.a = LPGEN("Toolbar");
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pDialog = new CSrmmToolbarOptions();
+ g_plugin.addOptions(wParam, &odp);
+
+ ChatOptionsInit(wParam);
+ SrmmLogOptionsInit(wParam);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(HANDLE) Srmm_AddButton(const BBButton *bbdi, HPLUGIN _hLang)
+{
+ if (bbdi == nullptr)
+ return nullptr;
+
+ CustomButtonData *cbd = new CustomButtonData();
+ cbd->m_pszModuleName = mir_strdup(bbdi->pszModuleName);
+ cbd->m_pwszText = mir_wstrdup(bbdi->pwszText);
+ cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip);
+
+ cbd->m_dwButtonID = bbdi->dwButtonID;
+ cbd->m_hIcon = bbdi->hIcon;
+ cbd->m_dwPosition = cbd->m_dwOrigPosition = bbdi->dwDefPos;
+ cbd->m_dwButtonCID = (bbdi->bbbFlags & BBBF_CREATEBYID) ? bbdi->dwButtonID : LastCID;
+ cbd->m_dwArrowCID = (bbdi->bbbFlags & BBBF_ISARROWBUTTON) ? cbd->m_dwButtonCID + 1 : 0;
+ cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0;
+ cbd->m_bSeparator = (bbdi->bbbFlags & BBBF_ISSEPARATOR) != 0;
+
+ cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0;
+ cbd->m_bPushButton = (bbdi->bbbFlags & BBBF_ISPUSHBUTTON) != 0;
+ cbd->m_pPlugin = _hLang;
+
+ cbd->m_dwOrigFlags.bit1 = cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0;
+ cbd->m_dwOrigFlags.bit2 = cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0;
+ cbd->m_dwOrigFlags.bit3 = cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0;
+ cbd->m_dwOrigFlags.bit4 = cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0;
+
+ if (cbd->m_bSeparator)
+ cbd->m_iButtonWidth = DPISCALEX_S(10);
+ else if (bbdi->bbbFlags & BBBF_ISARROWBUTTON)
+ cbd->m_iButtonWidth = DPISCALEX_S(34);
+ else
+ cbd->m_iButtonWidth = DPISCALEX_S(22);
+
+ if (bbdi->pszHotkey) {
+ for (auto &p : hotkeys) {
+ if (!mir_strcmp(p->getName(), bbdi->pszHotkey)) {
+ cbd->m_hotkey = p;
+ break;
+ }
+ }
+ }
+
+ // download database settings
+ char SettingName[1024];
+ mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID);
+
+ DBVARIANT dbv = { 0 };
+ if (!db_get_s(0, BB_MODULE_NAME, SettingName, &dbv)) {
+ // modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden
+ char *token = strtok(dbv.pszVal, "_");
+ cbd->m_dwPosition = (uint32_t)atoi(token);
+ token = strtok(nullptr, "_");
+ cbd->m_bIMButton = atoi(token) != 0;
+ token = strtok(nullptr, "_");
+ cbd->m_bChatButton = atoi(token) != 0;
+ token = strtok(nullptr, "_");
+ token = strtok(nullptr, "_");
+ cbd->m_bRSided = atoi(token) != 0;
+ token = strtok(nullptr, "_");
+ cbd->m_bCanBeHidden = atoi(token) != 0;
+
+ db_free(&dbv);
+ }
+
+ arButtonsList.insert(cbd);
+
+ if (cbd->m_dwButtonCID != cbd->m_dwButtonID)
+ LastCID++;
+ if (cbd->m_dwArrowCID == LastCID)
+ LastCID++;
+
+ WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0);
+ CSrmmToolbarOptions::RereadButtons();
+ return cbd;
+}
+
+MIR_APP_DLL(int) Srmm_RemoveButton(BBButton *bbdi)
+{
+ if (!bbdi)
+ return 1;
+
+ CustomButtonData *pFound = nullptr;
+ {
+ mir_cslock lck(csToolBar);
+
+ for (auto &cbd : arButtonsList.rev_iter())
+ if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && cbd->m_dwButtonID == bbdi->dwButtonID)
+ pFound = arButtonsList.removeItem(&cbd);
+ }
+
+ if (pFound) {
+ CSrmmToolbarOptions::RereadButtons();
+ WindowList_Broadcast(g_hWindowList, WM_CBD_REMOVED, pFound->m_dwButtonCID, (LPARAM)pFound);
+ delete pFound;
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void KillModuleToolbarIcons(CMPluginBase *pPlugin)
+{
+ int oldCount = arButtonsList.getCount();
+
+ auto T = arButtonsList.rev_iter();
+ for (auto &cbd : T)
+ if (cbd->m_pPlugin == pPlugin)
+ delete arButtonsList.removeItem(&cbd);
+
+ if (oldCount != arButtonsList.getCount())
+ CSrmmToolbarOptions::RereadButtons();
+}
+
+static INT_PTR BroadcastMessage(WPARAM, LPARAM lParam)
+{
+ Srmm_Broadcast((UINT)lParam, 0, 0);
+ return 0;
+}
+
+static void CALLBACK SrmmLoadToolbar()
+{
+ NotifyEventHooks(hHookToolBarLoadedEvt, 0, 0);
+ DestroyHookableEvent(hHookToolBarLoadedEvt);
+
+ HookEvent(ME_OPT_INITIALISE, SrmmOptionsInit);
+}
+
+static int ConvertToolbarData(const char *szSetting, void*)
+{
+ DBVARIANT dbv;
+ if (!db_get(0, "Tab" BB_MODULE_NAME, szSetting, &dbv)) {
+ db_set(0, BB_MODULE_NAME, szSetting, &dbv);
+ db_free(&dbv);
+ }
+ return 0;
+}
+
+void LoadSrmmToolbarModule()
+{
+ CreateServiceFunction("SRMsg/BroadcastMessage", BroadcastMessage);
+
+ Miranda_WaitOnHandle(SrmmLoadToolbar);
+
+ hHookButtonPressedEvt = CreateHookableEvent(ME_MSG_BUTTONPRESSED);
+ hHookToolBarLoadedEvt = CreateHookableEvent(ME_MSG_TOOLBARLOADED);
+
+ HDC hScrnDC = GetDC(nullptr);
+ g_DPIscaleX = GetDeviceCaps(hScrnDC, LOGPIXELSX) / 96.0;
+ g_DPIscaleY = GetDeviceCaps(hScrnDC, LOGPIXELSY) / 96.0;
+ ReleaseDC(nullptr, hScrnDC);
+
+ // old data? convert them
+ if (db_get_dw(0, "Tab" BB_MODULE_NAME, "SeparatorsCount", -1) != -1) {
+ db_enum_settings(0, ConvertToolbarData, "Tab" BB_MODULE_NAME, nullptr);
+ db_delete_module(0, "Tab" BB_MODULE_NAME);
+ }
+
+ dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0);
+ CB_RegisterSeparators();
+}
+
+void UnloadSrmmToolbarModule()
+{
+ DestroyHookableEvent(hHookButtonPressedEvt);
+
+ for (auto &it : arButtonsList)
+ delete it;
+ arButtonsList.destroy();
+}
diff --git a/src/mir_app/src/srmm_util.cpp b/src/mir_app/src/srmm_util.cpp index 4f66b9ffd2..2ca887a54a 100644 --- a/src/mir_app/src/srmm_util.cpp +++ b/src/mir_app/src/srmm_util.cpp @@ -1,147 +1,147 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "chat.h" - -const char *g_pszHotkeySection; - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(DWORD) CALLBACK Srmm_LogStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb) -{ - LOGSTREAMDATA *lstrdat = (LOGSTREAMDATA*)dwCookie; - if (lstrdat) { - // create the RTF - if (lstrdat->buffer == nullptr) { - lstrdat->bufferOffset = 0; - lstrdat->buffer = g_chatApi.Log_CreateRTF(lstrdat); - lstrdat->bufferLen = (int)mir_strlen(lstrdat->buffer); - } - - // give the RTF to the RE control - *pcb = min(cb, LONG(lstrdat->bufferLen - lstrdat->bufferOffset)); - memcpy(pbBuff, lstrdat->buffer + lstrdat->bufferOffset, *pcb); - lstrdat->bufferOffset += *pcb; - - // free stuff if the streaming operation is complete - if (lstrdat->bufferOffset == lstrdat->bufferLen) { - mir_free(lstrdat->buffer); - lstrdat->buffer = nullptr; - } - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Srmm_GetWindowData(MCONTACT hContact, MessageWindowData &mwd) -{ - if (hContact == 0) - return 1; - - HWND hwnd = WindowList_Find(g_hWindowList, hContact); - if (hwnd == nullptr) - return 1; - - mwd.hwndWindow = hwnd; - mwd.pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - mwd.uState = MSG_WINDOW_STATE_EXISTS; - if (IsWindowVisible(hwnd)) - mwd.uState |= MSG_WINDOW_STATE_VISIBLE; - if (GetForegroundWindow() == hwnd) - mwd.uState |= MSG_WINDOW_STATE_FOCUS; - if (IsIconic(hwnd)) - mwd.uState |= MSG_WINDOW_STATE_ICONIC; - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Srmm_Broadcast(UINT msg, WPARAM wParam, LPARAM lParam) -{ - WindowList_Broadcast(g_hWindowList, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Srmm_CreateHotkey(const char *pszSection, const char *pszDescription) -{ - g_pszHotkeySection = pszSection; - - uint16_t wHotKey = HOTKEYCODE(0, VK_RETURN); - if (db_get_b(0, SRMM_MODULE, "SendOnCtrlEnter")) - wHotKey = HOTKEYCODE(HOTKEYF_CONTROL, VK_RETURN); - - if (db_get_b(0, "Tab_SRMsg", "sendonshiftenter")) - wHotKey = HOTKEYCODE(HOTKEYF_SHIFT, VK_RETURN); - - HOTKEYDESC hd = { "tabsrmm_send", pszDescription, pszSection, 0, wHotKey, 0, 100 }; - Hotkey_Register(&hd, g_pChatPlugin); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HWND) Srmm_FindWindow(MCONTACT hContact) -{ - return WindowList_Find(g_hWindowList, hContact); -} - -MIR_APP_DLL(CMsgDialog*) Srmm_FindDialog(MCONTACT hContact) -{ - HWND hwnd = Srmm_FindWindow(hContact); - return (hwnd) ? (CMsgDialog *)GetWindowLongPtr(hwnd, GWLP_USERDATA) : nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// serializes all thread-unsafe operation to the first thread - -struct SSTParam -{ - HWND hwnd; - const wchar_t *wszText; - HICON hIcon; -}; - -static INT_PTR CALLBACK sttSetStatusText(void *_p) -{ - SSTParam *param = (SSTParam*)_p; - - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(param->hwnd, GWLP_USERDATA); - if (pDlg != nullptr) - pDlg->SetStatusText(param->wszText, param->hIcon); - return 0; -} - -MIR_APP_DLL(void) Srmm_SetStatusText(MCONTACT hContact, const wchar_t *wszText, HICON hIcon) -{ - HWND hwnd = Srmm_FindWindow(hContact); - if (hwnd == nullptr) - hwnd = Srmm_FindWindow(db_mc_getMeta(hContact)); - if (hwnd == nullptr) - return; - - SSTParam param = { hwnd, wszText, hIcon }; - CallFunctionSync(sttSetStatusText, ¶m); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "chat.h"
+
+const char *g_pszHotkeySection;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(DWORD) CALLBACK Srmm_LogStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb)
+{
+ LOGSTREAMDATA *lstrdat = (LOGSTREAMDATA*)dwCookie;
+ if (lstrdat) {
+ // create the RTF
+ if (lstrdat->buffer == nullptr) {
+ lstrdat->bufferOffset = 0;
+ lstrdat->buffer = g_chatApi.Log_CreateRTF(lstrdat);
+ lstrdat->bufferLen = (int)mir_strlen(lstrdat->buffer);
+ }
+
+ // give the RTF to the RE control
+ *pcb = min(cb, LONG(lstrdat->bufferLen - lstrdat->bufferOffset));
+ memcpy(pbBuff, lstrdat->buffer + lstrdat->bufferOffset, *pcb);
+ lstrdat->bufferOffset += *pcb;
+
+ // free stuff if the streaming operation is complete
+ if (lstrdat->bufferOffset == lstrdat->bufferLen) {
+ mir_free(lstrdat->buffer);
+ lstrdat->buffer = nullptr;
+ }
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Srmm_GetWindowData(MCONTACT hContact, MessageWindowData &mwd)
+{
+ if (hContact == 0)
+ return 1;
+
+ HWND hwnd = WindowList_Find(g_hWindowList, hContact);
+ if (hwnd == nullptr)
+ return 1;
+
+ mwd.hwndWindow = hwnd;
+ mwd.pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ mwd.uState = MSG_WINDOW_STATE_EXISTS;
+ if (IsWindowVisible(hwnd))
+ mwd.uState |= MSG_WINDOW_STATE_VISIBLE;
+ if (GetForegroundWindow() == hwnd)
+ mwd.uState |= MSG_WINDOW_STATE_FOCUS;
+ if (IsIconic(hwnd))
+ mwd.uState |= MSG_WINDOW_STATE_ICONIC;
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(void) Srmm_Broadcast(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WindowList_Broadcast(g_hWindowList, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(void) Srmm_CreateHotkey(const char *pszSection, const char *pszDescription)
+{
+ g_pszHotkeySection = pszSection;
+
+ uint16_t wHotKey = HOTKEYCODE(0, VK_RETURN);
+ if (db_get_b(0, SRMM_MODULE, "SendOnCtrlEnter"))
+ wHotKey = HOTKEYCODE(HOTKEYF_CONTROL, VK_RETURN);
+
+ if (db_get_b(0, "Tab_SRMsg", "sendonshiftenter"))
+ wHotKey = HOTKEYCODE(HOTKEYF_SHIFT, VK_RETURN);
+
+ HOTKEYDESC hd = { "tabsrmm_send", pszDescription, pszSection, 0, wHotKey, 0, 100 };
+ Hotkey_Register(&hd, g_pChatPlugin);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(HWND) Srmm_FindWindow(MCONTACT hContact)
+{
+ return WindowList_Find(g_hWindowList, hContact);
+}
+
+MIR_APP_DLL(CMsgDialog*) Srmm_FindDialog(MCONTACT hContact)
+{
+ HWND hwnd = Srmm_FindWindow(hContact);
+ return (hwnd) ? (CMsgDialog *)GetWindowLongPtr(hwnd, GWLP_USERDATA) : nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// serializes all thread-unsafe operation to the first thread
+
+struct SSTParam
+{
+ HWND hwnd;
+ const wchar_t *wszText;
+ HICON hIcon;
+};
+
+static INT_PTR CALLBACK sttSetStatusText(void *_p)
+{
+ SSTParam *param = (SSTParam*)_p;
+
+ CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(param->hwnd, GWLP_USERDATA);
+ if (pDlg != nullptr)
+ pDlg->SetStatusText(param->wszText, param->hIcon);
+ return 0;
+}
+
+MIR_APP_DLL(void) Srmm_SetStatusText(MCONTACT hContact, const wchar_t *wszText, HICON hIcon)
+{
+ HWND hwnd = Srmm_FindWindow(hContact);
+ if (hwnd == nullptr)
+ hwnd = Srmm_FindWindow(db_mc_getMeta(hContact));
+ if (hwnd == nullptr)
+ return;
+
+ SSTParam param = { hwnd, wszText, hIcon };
+ CallFunctionSync(sttSetStatusText, ¶m);
+}
diff --git a/src/mir_app/src/stdafx.cxx b/src/mir_app/src/stdafx.cxx index 52ec2d6817..e23069a5b8 100644 --- a/src/mir_app/src/stdafx.cxx +++ b/src/mir_app/src/stdafx.cxx @@ -1,6 +1,6 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/mir_app/src/stdafx.h b/src/mir_app/src/stdafx.h index e40748c95a..cbb17fac19 100644 --- a/src/mir_app/src/stdafx.h +++ b/src/mir_app/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/usedIcons.cpp b/src/mir_app/src/usedIcons.cpp index dec0672025..fc3274aa60 100644 --- a/src/mir_app/src/usedIcons.cpp +++ b/src/mir_app/src/usedIcons.cpp @@ -1,7 +1,7 @@ /*
Copyright (C) 2009 Ricardo Pescuma Domenecci
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/src/mir_app/src/usedIcons.h b/src/mir_app/src/usedIcons.h index 62c5539bd7..699f30ea7f 100644 --- a/src/mir_app/src/usedIcons.h +++ b/src/mir_app/src/usedIcons.h @@ -1,7 +1,7 @@ /*
Copyright (C) 2009 Ricardo Pescuma Domenecci
-Copyright (C) 2012-22 Miranda NG team
+Copyright (C) 2012-23 Miranda NG team
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/src/mir_app/src/userInfo.cpp b/src/mir_app/src/userInfo.cpp index deffc648a3..196ea0ae0a 100644 --- a/src/mir_app/src/userInfo.cpp +++ b/src/mir_app/src/userInfo.cpp @@ -1,45 +1,45 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// User info page base dialog - -CUserInfoPageDlg::CUserInfoPageDlg(class CMPluginBase &pPlug, int idDialog) : - CDlgBase(pPlug, idDialog) -{ - m_forceResizable = true; -} - -bool CUserInfoPageDlg::IsEmpty() const -{ - return false; -} - -bool CUserInfoPageDlg::OnRefresh() -{ - return false; -} - +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// User info page base dialog
+
+CUserInfoPageDlg::CUserInfoPageDlg(class CMPluginBase &pPlug, int idDialog) :
+ CDlgBase(pPlug, idDialog)
+{
+ m_forceResizable = true;
+}
+
+bool CUserInfoPageDlg::IsEmpty() const
+{
+ return false;
+}
+
+bool CUserInfoPageDlg::OnRefresh()
+{
+ return false;
+}
+
diff --git a/src/mir_app/src/utils.cpp b/src/mir_app/src/utils.cpp index 36260b3905..42e916d7d7 100644 --- a/src/mir_app/src/utils.cpp +++ b/src/mir_app/src/utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_app/src/visibility.cpp b/src/mir_app/src/visibility.cpp index 1aaa1421a5..8c116a2497 100644 --- a/src/mir_app/src/visibility.cpp +++ b/src/mir_app/src/visibility.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_core/res/version.rc b/src/mir_core/res/version.rc index 626717e99b..6d60274913 100644 --- a/src/mir_core/res/version.rc +++ b/src/mir_core/res/version.rc @@ -1,57 +1,57 @@ -#ifdef APSTUDIO_INVOKED -#error this file is not editable by Microsoft Visual C++ -#endif //APSTUDIO_INVOKED - -#include <windows.h> -#include <winres.h> - -#include "../include/m_version.h" - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1250) -#endif //_WIN32 - -VS_VERSION_INFO VERSIONINFO - FILEVERSION MIRANDA_VERSION_FILEVERSION - PRODUCTVERSION MIRANDA_VERSION_FILEVERSION - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "000004b0" - BEGIN - VALUE "Comments", "Licensed under the terms of the GNU General Public License\0" - VALUE "CompanyName", "Miranda NG team\0" - VALUE "FileDescription", "Miranda NG\0" - VALUE "FileVersion", MIRANDA_VERSION_DISPLAY - VALUE "InternalName", "mir_core\0" - VALUE "LegalCopyright", "Copyright © 2000-11 Miranda IM, 2012-22 Miranda NG team. This software is released under the terms of the GNU General Public License.\0" - VALUE "LegalTrademarks", "\0" - VALUE "OriginalFilename", "mir_core.mir\0" - VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Miranda NG\0" - VALUE "ProductVersion", MIRANDA_VERSION_DISPLAY - VALUE "SpecialBuild", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0, 1200 - END -END - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// +#ifdef APSTUDIO_INVOKED
+#error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+#include <windows.h>
+#include <winres.h>
+
+#include "../include/m_version.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1250)
+#endif //_WIN32
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION MIRANDA_VERSION_FILEVERSION
+ PRODUCTVERSION MIRANDA_VERSION_FILEVERSION
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "Comments", "Licensed under the terms of the GNU General Public License\0"
+ VALUE "CompanyName", "Miranda NG team\0"
+ VALUE "FileDescription", "Miranda NG\0"
+ VALUE "FileVersion", MIRANDA_VERSION_DISPLAY
+ VALUE "InternalName", "mir_core\0"
+ VALUE "LegalCopyright", "Copyright © 2000-11 Miranda IM, 2012-23 Miranda NG team. This software is released under the terms of the GNU General Public License.\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "mir_core.mir\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "Miranda NG\0"
+ VALUE "ProductVersion", MIRANDA_VERSION_DISPLAY
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
diff --git a/src/mir_core/src/Linux/CCtrlBase.cpp b/src/mir_core/src/Linux/CCtrlBase.cpp index cba8567e52..ec84f1b77c 100644 --- a/src/mir_core/src/Linux/CCtrlBase.cpp +++ b/src/mir_core/src/Linux/CCtrlBase.cpp @@ -1,205 +1,205 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static mir_cs csCtrl; - -static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} - -static LIST<CCtrlBase> arControls(10, CompareControls); - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlBase - -CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) : - m_parentWnd(wnd), - m_idCtrl(idCtrl) -{ - if (wnd) - wnd->AddControl(this); -} - -CCtrlBase::~CCtrlBase() -{ -} - -void CCtrlBase::OnInit() -{ - m_hwnd = nullptr; -} - -void CCtrlBase::OnDestroy() -{ - void *bullshit[2]; // vfptr + hwnd - bullshit[1] = m_hwnd; - CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); - if (pCtrl) { - pCtrl->Unsubclass(); - arControls.remove(pCtrl); - } - - evas_object_del(m_hwnd); - m_hwnd = nullptr; -} - -bool CCtrlBase::OnApply() -{ - m_bChanged = false; - return true; -} - -void CCtrlBase::OnReset() -{ -} - -void CCtrlBase::Show(bool bShow) -{ - // ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE); -} - -void CCtrlBase::Enable(bool bIsEnable) -{ - // ::EnableWindow(m_hwnd, bIsEnable); -} - -bool CCtrlBase::Enabled() const -{ - return (m_hwnd != nullptr); -} - -void CCtrlBase::NotifyChange() -{ - if (!m_parentWnd) - return; - - if (m_parentWnd->IsInitialized()) { - m_bChanged = true; - if (!m_bSilent) - m_parentWnd->NotifyChange(); - } - - OnChange(this); -} - -LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const -{ - // return ::SendMessage(m_hwnd, Msg, wParam, lParam); - return 0; -} - -void CCtrlBase::SetText(const wchar_t *text) -{ - // ::SetWindowText(m_hwnd, text); -} - -void CCtrlBase::SetTextA(const char *text) -{ - // ::SetWindowTextA(m_hwnd, text); -} - -void CCtrlBase::SetDraw(bool bEnable) -{ - // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CCtrlBase::SetInt(int value) -{ - wchar_t buf[32] = { 0 }; - mir_snwprintf(buf, L"%d", value); - SetText(buf); -} - -wchar_t* CCtrlBase::GetText() const -{ - return mir_wstrdup(L""); -} - -char* CCtrlBase::GetTextA() const -{ - return mir_strdup(""); -} - -char* CCtrlBase::GetTextU() const -{ - return mir_utf8encodeW(ptrW(GetText())); -} - -wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const -{ - // GetWindowTextW(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextA(char *buf, size_t size) const -{ - // GetWindowTextA(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextU(char *buf, size_t size) const -{ - ptrW wszText(GetText()); - strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE); - return buf; -} - -int CCtrlBase::GetInt() const -{ - // int length = GetWindowTextLengthW(m_hwnd) + 1; - // wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t)); - // GetWindowTextW(m_hwnd, result, length); - // return _wtoi(result); - return 0; -} - -void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - pos.iCurr = -1; - - // if (pos.pt.x == 0 && pos.pt.y == 0) - // GetCursorPos(&pos.pt); -} - -LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM) -{ - return FALSE; -} - -void CCtrlBase::Subclass() -{ - // mir_subclassWindow(m_hwnd, GlobalSubclassWndProc); - - mir_cslock lck(csCtrl); - arControls.insert(this); -} - -void CCtrlBase::Unsubclass() -{ - // mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static mir_cs csCtrl;
+
+static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2)
+{
+ return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd();
+}
+
+static LIST<CCtrlBase> arControls(10, CompareControls);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlBase
+
+CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) :
+ m_parentWnd(wnd),
+ m_idCtrl(idCtrl)
+{
+ if (wnd)
+ wnd->AddControl(this);
+}
+
+CCtrlBase::~CCtrlBase()
+{
+}
+
+void CCtrlBase::OnInit()
+{
+ m_hwnd = nullptr;
+}
+
+void CCtrlBase::OnDestroy()
+{
+ void *bullshit[2]; // vfptr + hwnd
+ bullshit[1] = m_hwnd;
+ CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit);
+ if (pCtrl) {
+ pCtrl->Unsubclass();
+ arControls.remove(pCtrl);
+ }
+
+ evas_object_del(m_hwnd);
+ m_hwnd = nullptr;
+}
+
+bool CCtrlBase::OnApply()
+{
+ m_bChanged = false;
+ return true;
+}
+
+void CCtrlBase::OnReset()
+{
+}
+
+void CCtrlBase::Show(bool bShow)
+{
+ // ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE);
+}
+
+void CCtrlBase::Enable(bool bIsEnable)
+{
+ // ::EnableWindow(m_hwnd, bIsEnable);
+}
+
+bool CCtrlBase::Enabled() const
+{
+ return (m_hwnd != nullptr);
+}
+
+void CCtrlBase::NotifyChange()
+{
+ if (!m_parentWnd)
+ return;
+
+ if (m_parentWnd->IsInitialized()) {
+ m_bChanged = true;
+ if (!m_bSilent)
+ m_parentWnd->NotifyChange();
+ }
+
+ OnChange(this);
+}
+
+LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const
+{
+ // return ::SendMessage(m_hwnd, Msg, wParam, lParam);
+ return 0;
+}
+
+void CCtrlBase::SetText(const wchar_t *text)
+{
+ // ::SetWindowText(m_hwnd, text);
+}
+
+void CCtrlBase::SetTextA(const char *text)
+{
+ // ::SetWindowTextA(m_hwnd, text);
+}
+
+void CCtrlBase::SetDraw(bool bEnable)
+{
+ // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0);
+}
+
+void CCtrlBase::SetInt(int value)
+{
+ wchar_t buf[32] = { 0 };
+ mir_snwprintf(buf, L"%d", value);
+ SetText(buf);
+}
+
+wchar_t* CCtrlBase::GetText() const
+{
+ return mir_wstrdup(L"");
+}
+
+char* CCtrlBase::GetTextA() const
+{
+ return mir_strdup("");
+}
+
+char* CCtrlBase::GetTextU() const
+{
+ return mir_utf8encodeW(ptrW(GetText()));
+}
+
+wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const
+{
+ // GetWindowTextW(m_hwnd, buf, (int)size);
+ buf[size - 1] = 0;
+ return buf;
+}
+
+char* CCtrlBase::GetTextA(char *buf, size_t size) const
+{
+ // GetWindowTextA(m_hwnd, buf, (int)size);
+ buf[size - 1] = 0;
+ return buf;
+}
+
+char* CCtrlBase::GetTextU(char *buf, size_t size) const
+{
+ ptrW wszText(GetText());
+ strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE);
+ return buf;
+}
+
+int CCtrlBase::GetInt() const
+{
+ // int length = GetWindowTextLengthW(m_hwnd) + 1;
+ // wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t));
+ // GetWindowTextW(m_hwnd, result, length);
+ // return _wtoi(result);
+ return 0;
+}
+
+void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+ pos.iCurr = -1;
+
+ // if (pos.pt.x == 0 && pos.pt.y == 0)
+ // GetCursorPos(&pos.pt);
+}
+
+LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM)
+{
+ return FALSE;
+}
+
+void CCtrlBase::Subclass()
+{
+ // mir_subclassWindow(m_hwnd, GlobalSubclassWndProc);
+
+ mir_cslock lck(csCtrl);
+ arControls.insert(this);
+}
+
+void CCtrlBase::Unsubclass()
+{
+ // mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc);
+}
diff --git a/src/mir_core/src/Linux/CCtrlButton.cpp b/src/mir_core/src/Linux/CCtrlButton.cpp index e8c107b888..da569730c2 100644 --- a/src/mir_core/src/Linux/CCtrlButton.cpp +++ b/src/mir_core/src/Linux/CCtrlButton.cpp @@ -1,55 +1,55 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlButton - -CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} - -BOOL CCtrlButton::OnCommand(MWindow, uint16_t, uint16_t idCode) -{ - // if (idCode == BN_CLICKED) - // OnClick(this); - return FALSE; -} - -void CCtrlButton::Click() -{ - // if (Enabled()) - // ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0); -} - -bool CCtrlButton::IsPushed() const -{ - // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED; - return false; -} - -void CCtrlButton::Push(bool bPushed) -{ - // if (Enabled()) - // ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlButton
+
+CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{}
+
+BOOL CCtrlButton::OnCommand(MWindow, uint16_t, uint16_t idCode)
+{
+ // if (idCode == BN_CLICKED)
+ // OnClick(this);
+ return FALSE;
+}
+
+void CCtrlButton::Click()
+{
+ // if (Enabled())
+ // ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0);
+}
+
+bool CCtrlButton::IsPushed() const
+{
+ // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ return false;
+}
+
+void CCtrlButton::Push(bool bPushed)
+{
+ // if (Enabled())
+ // ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0);
+}
diff --git a/src/mir_core/src/Linux/CCtrlCheck.cpp b/src/mir_core/src/Linux/CCtrlCheck.cpp index 7c821f0329..112db0b66a 100644 --- a/src/mir_core/src/Linux/CCtrlCheck.cpp +++ b/src/mir_core/src/Linux/CCtrlCheck.cpp @@ -1,70 +1,70 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCheck class - -CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{ - m_bNotifiable = true; -} - -BOOL CCtrlCheck::OnCommand(MWindow, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlCheck::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetState()); - return true; -} - -void CCtrlCheck::OnReset() -{ - if (m_dbLink != nullptr) - SetState(LoadInt()); -} - -int CCtrlCheck::GetState() const -{ - // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0); - return false; -} - -void CCtrlCheck::SetState(int state) -{ - // ::SendMessage(m_hwnd, BM_SETCHECK, state, 0); -} - -bool CCtrlCheck::IsChecked() -{ - // return GetState() == BST_CHECKED; - return false; -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCheck class
+
+CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{
+ m_bNotifiable = true;
+}
+
+BOOL CCtrlCheck::OnCommand(MWindow, uint16_t, uint16_t)
+{
+ NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlCheck::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetState());
+ return true;
+}
+
+void CCtrlCheck::OnReset()
+{
+ if (m_dbLink != nullptr)
+ SetState(LoadInt());
+}
+
+int CCtrlCheck::GetState() const
+{
+ // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0);
+ return false;
+}
+
+void CCtrlCheck::SetState(int state)
+{
+ // ::SendMessage(m_hwnd, BM_SETCHECK, state, 0);
+}
+
+bool CCtrlCheck::IsChecked()
+{
+ // return GetState() == BST_CHECKED;
+ return false;
+}
diff --git a/src/mir_core/src/Linux/CCtrlClc.cpp b/src/mir_core/src/Linux/CCtrlClc.cpp index cf4f05cda6..57f5607ef2 100644 --- a/src/mir_core/src/Linux/CCtrlClc.cpp +++ b/src/mir_core/src/Linux/CCtrlClc.cpp @@ -1,207 +1,207 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlClc - -CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh }; - switch (pnmh->code) { - case CLN_EXPANDED: OnExpanded(&evt); break; - case CLN_LISTREBUILT: OnListRebuilt(&evt); break; - case CLN_ITEMCHECKED: OnItemChecked(&evt); break; - case CLN_DRAGGING: OnDragging(&evt); break; - case CLN_DROPPED: OnDropped(&evt); break; - case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break; - case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break; - case CLN_DRAGSTOP: OnDragStop(&evt); break; - case CLN_NEWCONTACT: OnNewContact(&evt); break; - case CLN_CONTACTMOVED: OnContactMoved(&evt); break; - case CLN_CHECKCHANGED: OnCheckChanged(&evt); break; - case NM_CLICK: OnClick(&evt); break; - } - return FALSE; -} - -void CCtrlClc::AddContact(MCONTACT hContact) -{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0); -} - -void CCtrlClc::AddGroup(HANDLE hGroup) -{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0); -} - -void CCtrlClc::AutoRebuild() -{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0); -} - -void CCtrlClc::DeleteItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::EditLabel(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0); -} - -void CCtrlClc::EndEditLabel(bool save) -{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0); -} - -void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk) -{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE); -} - -void CCtrlClc::Expand(HANDLE hItem, uint32_t flags) -{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags); -} - -HANDLE CCtrlClc::FindContact(MCONTACT hContact) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0); -} - -HANDLE CCtrlClc::FindGroup(MGROUP hGroup) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0); -} - -COLORREF CCtrlClc::GetBkColor() const -{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0); -} - -bool CCtrlClc::GetCheck(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false; -} - -int CCtrlClc::GetCount() const -{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0); -} - -HWND CCtrlClc::GetEditControl() const -{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0); -} - -uint32_t CCtrlClc::GetExpand(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0); -} - -int CCtrlClc::GetExtraColumns() const -{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0); -} - -uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const -{ - return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF); -} - -HIMAGELIST CCtrlClc::GetExtraImageList() const -{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0); -} - -HFONT CCtrlClc::GetFont(int iFontId) const -{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0); -} - -HANDLE CCtrlClc::GetSelection() const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0); -} - -HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y)); -} - -void CCtrlClc::SelectItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::SetBkColor(COLORREF clBack) -{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0); -} - -void CCtrlClc::SetCheck(HANDLE hItem, bool check) -{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0); -} - -void CCtrlClc::SetExtraColumns(int iColumns) -{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0); -} - -void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage)); -} - -void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList); -} - -void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw) -{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId)); -} - -void CCtrlClc::SetItemText(HANDLE hItem, char *szText) -{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText); -} - -void CCtrlClc::SetHideEmptyGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0); -} - -bool CCtrlClc::GetHideOfflineRoot() const -{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false; -} - -void CCtrlClc::SetHideOfflineRoot(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9); -} - -void CCtrlClc::SetUseGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0); -} - -void CCtrlClc::SetOfflineModes(uint32_t modes) -{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0); -} - -uint32_t CCtrlClc::GetExStyle() const -{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0); -} - -void CCtrlClc::SetExStyle(uint32_t exStyle) -{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0); -} - -HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii) -{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii); -} - -int CCtrlClc::GetItemType(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0); -} - -HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlClc
+
+CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh };
+ switch (pnmh->code) {
+ case CLN_EXPANDED: OnExpanded(&evt); break;
+ case CLN_LISTREBUILT: OnListRebuilt(&evt); break;
+ case CLN_ITEMCHECKED: OnItemChecked(&evt); break;
+ case CLN_DRAGGING: OnDragging(&evt); break;
+ case CLN_DROPPED: OnDropped(&evt); break;
+ case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break;
+ case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break;
+ case CLN_DRAGSTOP: OnDragStop(&evt); break;
+ case CLN_NEWCONTACT: OnNewContact(&evt); break;
+ case CLN_CONTACTMOVED: OnContactMoved(&evt); break;
+ case CLN_CHECKCHANGED: OnCheckChanged(&evt); break;
+ case NM_CLICK: OnClick(&evt); break;
+ }
+ return FALSE;
+}
+
+void CCtrlClc::AddContact(MCONTACT hContact)
+{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0);
+}
+
+void CCtrlClc::AddGroup(HANDLE hGroup)
+{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0);
+}
+
+void CCtrlClc::AutoRebuild()
+{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0);
+}
+
+void CCtrlClc::DeleteItem(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::EditLabel(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::EndEditLabel(bool save)
+{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0);
+}
+
+void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk)
+{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE);
+}
+
+void CCtrlClc::Expand(HANDLE hItem, uint32_t flags)
+{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags);
+}
+
+HANDLE CCtrlClc::FindContact(MCONTACT hContact)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0);
+}
+
+HANDLE CCtrlClc::FindGroup(MGROUP hGroup)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0);
+}
+
+COLORREF CCtrlClc::GetBkColor() const
+{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0);
+}
+
+bool CCtrlClc::GetCheck(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false;
+}
+
+int CCtrlClc::GetCount() const
+{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0);
+}
+
+HWND CCtrlClc::GetEditControl() const
+{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0);
+}
+
+uint32_t CCtrlClc::GetExpand(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0);
+}
+
+int CCtrlClc::GetExtraColumns() const
+{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0);
+}
+
+uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const
+{
+ return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF);
+}
+
+HIMAGELIST CCtrlClc::GetExtraImageList() const
+{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0);
+}
+
+HFONT CCtrlClc::GetFont(int iFontId) const
+{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0);
+}
+
+HANDLE CCtrlClc::GetSelection() const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0);
+}
+
+HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y));
+}
+
+void CCtrlClc::SelectItem(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::SetBkColor(COLORREF clBack)
+{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0);
+}
+
+void CCtrlClc::SetCheck(HANDLE hItem, bool check)
+{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0);
+}
+
+void CCtrlClc::SetExtraColumns(int iColumns)
+{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0);
+}
+
+void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage)
+{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage));
+}
+
+void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList)
+{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList);
+}
+
+void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw)
+{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId));
+}
+
+void CCtrlClc::SetItemText(HANDLE hItem, char *szText)
+{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText);
+}
+
+void CCtrlClc::SetHideEmptyGroups(bool state)
+{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0);
+}
+
+bool CCtrlClc::GetHideOfflineRoot() const
+{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false;
+}
+
+void CCtrlClc::SetHideOfflineRoot(bool state)
+{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9);
+}
+
+void CCtrlClc::SetUseGroups(bool state)
+{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0);
+}
+
+void CCtrlClc::SetOfflineModes(uint32_t modes)
+{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0);
+}
+
+uint32_t CCtrlClc::GetExStyle() const
+{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0);
+}
+
+void CCtrlClc::SetExStyle(uint32_t exStyle)
+{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0);
+}
+
+HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii);
+}
+
+int CCtrlClc::GetItemType(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0);
+}
+
+HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem);
+}
diff --git a/src/mir_core/src/Linux/CCtrlColor.cpp b/src/mir_core/src/Linux/CCtrlColor.cpp index 97f5e48e02..b61fb5760a 100644 --- a/src/mir_core/src/Linux/CCtrlColor.cpp +++ b/src/mir_core/src/Linux/CCtrlColor.cpp @@ -1,61 +1,61 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlColor class - -CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlColor::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetColor()); - return true; -} - -void CCtrlColor::OnReset() -{ - if (m_dbLink != nullptr) - SetColor(LoadInt()); -} - -uint32_t CCtrlColor::GetColor() -{ - return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0); -} - -void CCtrlColor::SetColor(uint32_t dwValue) -{ - ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlColor class
+
+CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) :
+ CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t)
+{
+ NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlColor::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetColor());
+ return true;
+}
+
+void CCtrlColor::OnReset()
+{
+ if (m_dbLink != nullptr)
+ SetColor(LoadInt());
+}
+
+uint32_t CCtrlColor::GetColor()
+{
+ return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0);
+}
+
+void CCtrlColor::SetColor(uint32_t dwValue)
+{
+ ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue);
+}
diff --git a/src/mir_core/src/Linux/CCtrlCombo.cpp b/src/mir_core/src/Linux/CCtrlCombo.cpp index 063588e39e..52a85f1115 100644 --- a/src/mir_core/src/Linux/CCtrlCombo.cpp +++ b/src/mir_core/src/Linux/CCtrlCombo.cpp @@ -1,169 +1,169 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCombo class - -CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlCombo::OnCommand(MWindow, uint16_t, uint16_t idCode) -{ - switch (idCode) { - // case CBN_CLOSEUP: OnCloseup(this); break; - // case CBN_DROPDOWN: OnDropdown(this); break; - // case CBN_SELCHANGE: OnSelChanged(this); break; - // case CBN_KILLFOCUS: OnKillFocus(this); break; - - // case CBN_EDITCHANGE: - // case CBN_EDITUPDATE: - // case CBN_SELENDOK: - // NotifyChange(); - break; - } - return TRUE; -} - -void CCtrlCombo::OnInit() -{ - CSuper::OnInit(); - OnReset(); -} - -bool CCtrlCombo::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - // int len = GetWindowTextLength(m_hwnd) + 1; - // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - // GetWindowText(m_hwnd, buf, len); - // SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlCombo::OnReset() -{ - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -LPARAM CCtrlCombo::GetCurData() const -{ - return GetItemData(GetCurSel()); -} - -// selects line with userdata passed -int CCtrlCombo::SelectData(LPARAM data) -{ - int ret = -1, nCount = GetCount(); - - for (int i = 0; i < nCount; i++) - if (GetItemData(i) == data) { - ret = i; - break; - } - - return SetCurSel(ret); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Windows API - -int CCtrlCombo::AddString(const wchar_t *text, LPARAM data) -{ - return -1; -} - -int CCtrlCombo::AddStringA(const char *text, LPARAM data) -{ - return -1; -} - -void CCtrlCombo::DeleteString(int index) -{ -} - -int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact) -{ return 0; -} - -int CCtrlCombo::FindStringA(const char *str, int index, bool exact) -{ return 0; -} - -int CCtrlCombo::GetCount() const -{ return 0; -} - -int CCtrlCombo::GetCurSel() const -{ return 0; -} - -bool CCtrlCombo::GetDroppedState() const -{ return 0; -} - -LPARAM CCtrlCombo::GetItemData(int index) const -{ return 0; -} - -wchar_t* CCtrlCombo::GetItemText(int index) const -{ return 0; -} - -wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const -{ return 0; -} - -int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data) -{ return 0; -} - -void CCtrlCombo::ResetContent() -{ -} - -int CCtrlCombo::SelectString(const wchar_t *str) -{ return 0; -} - -int CCtrlCombo::SetCurSel(int index) -{ return 0; -} - -void CCtrlCombo::SetItemData(int index, LPARAM data) -{ -} - -void CCtrlCombo::ShowDropdown(bool show) -{ -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCombo class
+
+CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlCombo::OnCommand(MWindow, uint16_t, uint16_t idCode)
+{
+ switch (idCode) {
+ // case CBN_CLOSEUP: OnCloseup(this); break;
+ // case CBN_DROPDOWN: OnDropdown(this); break;
+ // case CBN_SELCHANGE: OnSelChanged(this); break;
+ // case CBN_KILLFOCUS: OnKillFocus(this); break;
+
+ // case CBN_EDITCHANGE:
+ // case CBN_EDITUPDATE:
+ // case CBN_SELENDOK:
+ // NotifyChange();
+ break;
+ }
+ return TRUE;
+}
+
+void CCtrlCombo::OnInit()
+{
+ CSuper::OnInit();
+ OnReset();
+}
+
+bool CCtrlCombo::OnApply()
+{
+ CSuper::OnApply();
+
+ if (GetDataType() == DBVT_WCHAR) {
+ // int len = GetWindowTextLength(m_hwnd) + 1;
+ // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len);
+ // GetWindowText(m_hwnd, buf, len);
+ // SaveText(buf);
+ }
+ else if (GetDataType() != DBVT_DELETED) {
+ SaveInt(GetInt());
+ }
+ return true;
+}
+
+void CCtrlCombo::OnReset()
+{
+ if (GetDataType() == DBVT_WCHAR)
+ SetText(LoadText());
+ else if (GetDataType() != DBVT_DELETED)
+ SetInt(LoadInt());
+}
+
+LPARAM CCtrlCombo::GetCurData() const
+{
+ return GetItemData(GetCurSel());
+}
+
+// selects line with userdata passed
+int CCtrlCombo::SelectData(LPARAM data)
+{
+ int ret = -1, nCount = GetCount();
+
+ for (int i = 0; i < nCount; i++)
+ if (GetItemData(i) == data) {
+ ret = i;
+ break;
+ }
+
+ return SetCurSel(ret);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Windows API
+
+int CCtrlCombo::AddString(const wchar_t *text, LPARAM data)
+{
+ return -1;
+}
+
+int CCtrlCombo::AddStringA(const char *text, LPARAM data)
+{
+ return -1;
+}
+
+void CCtrlCombo::DeleteString(int index)
+{
+}
+
+int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact)
+{ return 0;
+}
+
+int CCtrlCombo::FindStringA(const char *str, int index, bool exact)
+{ return 0;
+}
+
+int CCtrlCombo::GetCount() const
+{ return 0;
+}
+
+int CCtrlCombo::GetCurSel() const
+{ return 0;
+}
+
+bool CCtrlCombo::GetDroppedState() const
+{ return 0;
+}
+
+LPARAM CCtrlCombo::GetItemData(int index) const
+{ return 0;
+}
+
+wchar_t* CCtrlCombo::GetItemText(int index) const
+{ return 0;
+}
+
+wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const
+{ return 0;
+}
+
+int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data)
+{ return 0;
+}
+
+void CCtrlCombo::ResetContent()
+{
+}
+
+int CCtrlCombo::SelectString(const wchar_t *str)
+{ return 0;
+}
+
+int CCtrlCombo::SetCurSel(int index)
+{ return 0;
+}
+
+void CCtrlCombo::SetItemData(int index, LPARAM data)
+{
+}
+
+void CCtrlCombo::ShowDropdown(bool show)
+{
+}
diff --git a/src/mir_core/src/Linux/CCtrlData.cpp b/src/mir_core/src/Linux/CCtrlData.cpp index fab5ca0409..2a60d9d31a 100644 --- a/src/mir_core/src/Linux/CCtrlData.cpp +++ b/src/mir_core/src/Linux/CCtrlData.cpp @@ -1,52 +1,52 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlData class - -CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_dbLink(nullptr) -{} - -CCtrlData::~CCtrlData() -{ - delete m_dbLink; -} - -void CCtrlData::OnInit() -{ - CCtrlBase::OnInit(); - OnReset(); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlData class
+
+CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl),
+ m_dbLink(nullptr)
+{}
+
+CCtrlData::~CCtrlData()
+{
+ delete m_dbLink;
+}
+
+void CCtrlData::OnInit()
+{
+ CCtrlBase::OnInit();
+ OnReset();
+}
+
+void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue)
+{
+ m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue);
+}
+
+void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue)
+{
+ m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue);
+}
diff --git a/src/mir_core/src/Linux/CCtrlEdit.cpp b/src/mir_core/src/Linux/CCtrlEdit.cpp index 7f1ac14cc2..42a9dd6327 100644 --- a/src/mir_core/src/Linux/CCtrlEdit.cpp +++ b/src/mir_core/src/Linux/CCtrlEdit.cpp @@ -1,68 +1,68 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlEdit class - -CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlEdit::OnCommand(MWindow, uint16_t, uint16_t idCode) -{ - // if (idCode == EN_CHANGE) - // NotifyChange(); - return TRUE; -} - -bool CCtrlEdit::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - // int len = GetWindowTextLength(m_hwnd) + 1; - // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - // GetWindowText(m_hwnd, buf, len); - // SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlEdit::OnReset() -{ - // m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0; - - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -void CCtrlEdit::SetMaxLength(unsigned int len) -{ - // SendMsg(EM_SETLIMITTEXT, len, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlEdit class
+
+CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlEdit::OnCommand(MWindow, uint16_t, uint16_t idCode)
+{
+ // if (idCode == EN_CHANGE)
+ // NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlEdit::OnApply()
+{
+ CSuper::OnApply();
+
+ if (GetDataType() == DBVT_WCHAR) {
+ // int len = GetWindowTextLength(m_hwnd) + 1;
+ // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len);
+ // GetWindowText(m_hwnd, buf, len);
+ // SaveText(buf);
+ }
+ else if (GetDataType() != DBVT_DELETED) {
+ SaveInt(GetInt());
+ }
+ return true;
+}
+
+void CCtrlEdit::OnReset()
+{
+ // m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0;
+
+ if (GetDataType() == DBVT_WCHAR)
+ SetText(LoadText());
+ else if (GetDataType() != DBVT_DELETED)
+ SetInt(LoadInt());
+}
+
+void CCtrlEdit::SetMaxLength(unsigned int len)
+{
+ // SendMsg(EM_SETLIMITTEXT, len, 0);
+}
diff --git a/src/mir_core/src/Linux/CCtrlHyperlink.cpp b/src/mir_core/src/Linux/CCtrlHyperlink.cpp index ca92d19dbc..0e0d93a689 100644 --- a/src/mir_core/src/Linux/CCtrlHyperlink.cpp +++ b/src/mir_core/src/Linux/CCtrlHyperlink.cpp @@ -1,54 +1,54 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlHyperlink - -CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url) - : CCtrlBase(wnd, idCtrl), - m_url(url) -{ - OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick); -} - -BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t) -{ - OnClick(this); - return FALSE; -} - -void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*) -{ - ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW); -} - -void CCtrlHyperlink::SetUrl(const char *url) -{ - m_url = url; -} - -const char* CCtrlHyperlink::GetUrl() -{ - return m_url; -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlHyperlink
+
+CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url)
+ : CCtrlBase(wnd, idCtrl),
+ m_url(url)
+{
+ OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick);
+}
+
+BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t)
+{
+ OnClick(this);
+ return FALSE;
+}
+
+void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*)
+{
+ ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW);
+}
+
+void CCtrlHyperlink::SetUrl(const char *url)
+{
+ m_url = url;
+}
+
+const char* CCtrlHyperlink::GetUrl()
+{
+ return m_url;
+}
diff --git a/src/mir_core/src/Linux/CCtrlLabel.cpp b/src/mir_core/src/Linux/CCtrlLabel.cpp index 0c097c0655..ff05f3c8cf 100644 --- a/src/mir_core/src/Linux/CCtrlLabel.cpp +++ b/src/mir_core/src/Linux/CCtrlLabel.cpp @@ -1,30 +1,30 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlLabel - -CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlLabel
+
+CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{}
diff --git a/src/mir_core/src/Linux/CCtrlListBox.cpp b/src/mir_core/src/Linux/CCtrlListBox.cpp index abaa31a786..6d9f58fc23 100644 --- a/src/mir_core/src/Linux/CCtrlListBox.cpp +++ b/src/mir_core/src/Linux/CCtrlListBox.cpp @@ -1,160 +1,160 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListBox class - -CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - switch (idCode) { - case LBN_DBLCLK: OnDblClick(this); break; - case LBN_SELCANCEL: OnSelCancel(this); break; - case LBN_SELCHANGE: OnSelChange(this); break; - } - return TRUE; -} - -void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetCurSel(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - - CSuper::GetCaretPos(pos); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CCtrlListBox::AddString(const wchar_t *text, LPARAM data) -{ - int iItem = ListBox_AddString(m_hwnd, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::DeleteString(int index) -{ ListBox_DeleteString(m_hwnd, index); -} - -int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact) -{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlListBox::GetCount() const -{ return ListBox_GetCount(m_hwnd); -} - -int CCtrlListBox::GetCurSel() const -{ return ListBox_GetCurSel(m_hwnd); -} - -LPARAM CCtrlListBox::GetItemData(int index) const -{ return ListBox_GetItemData(m_hwnd, index); -} - -int CCtrlListBox::GetItemRect(int index, RECT *pResult) const -{ return ListBox_GetItemRect(m_hwnd, index, pResult); -} - -wchar_t* CCtrlListBox::GetItemText(int index) const -{ - wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - return result; -} - -wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const -{ - wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - mir_wstrncpy(buf, result, size); - return buf; -} - -bool CCtrlListBox::GetSel(int index) const -{ return ListBox_GetSel(m_hwnd, index) ? true : false; -} - -int CCtrlListBox::GetSelCount() const -{ return ListBox_GetSelCount(m_hwnd); -} - -int* CCtrlListBox::GetSelItems(int *items, int count) const -{ - ListBox_GetSelItems(m_hwnd, count, items); - return items; -} - -int* CCtrlListBox::GetSelItems() const -{ - int count = GetSelCount() + 1; - int *result = (int *)mir_alloc(sizeof(int) * count); - ListBox_GetSelItems(m_hwnd, count, result); - result[count-1] = -1; - return result; -} - -int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data) -{ - int iItem = ListBox_InsertString(m_hwnd, pos, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::ResetContent() -{ ListBox_ResetContent(m_hwnd); -} - -int CCtrlListBox::SelectString(const wchar_t *str) -{ return ListBox_SelectString(m_hwnd, 0, str); -} - -int CCtrlListBox::SetCurSel(int index) -{ return ListBox_SetCurSel(m_hwnd, index); -} - -void CCtrlListBox::SetItemData(int index, LPARAM data) -{ ListBox_SetItemData(m_hwnd, index, data); -} - -void CCtrlListBox::SetItemHeight(int index, int iHeight) -{ ListBox_SetItemHeight(m_hwnd, index, iHeight); -} - -void CCtrlListBox::SetSel(int index, bool sel) -{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListBox class
+
+CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ switch (idCode) {
+ case LBN_DBLCLK: OnDblClick(this); break;
+ case LBN_SELCANCEL: OnSelCancel(this); break;
+ case LBN_SELCHANGE: OnSelChange(this); break;
+ }
+ return TRUE;
+}
+
+void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+ if (pos.pt.x == 0 && pos.pt.y == 0) {
+ pos.iCurr = GetCurSel();
+ if (pos.iCurr != -1) {
+ RECT rc;
+ GetItemRect(pos.iCurr, &rc);
+ pos.pt.x = rc.left + 8;
+ pos.pt.y = rc.top + 8;
+ ClientToScreen(m_hwnd, &pos.pt);
+ return;
+ }
+ }
+
+ CSuper::GetCaretPos(pos);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CCtrlListBox::AddString(const wchar_t *text, LPARAM data)
+{
+ int iItem = ListBox_AddString(m_hwnd, text);
+ ListBox_SetItemData(m_hwnd, iItem, data);
+ return iItem;
+}
+
+void CCtrlListBox::DeleteString(int index)
+{ ListBox_DeleteString(m_hwnd, index);
+}
+
+int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact)
+{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str);
+}
+
+int CCtrlListBox::GetCount() const
+{ return ListBox_GetCount(m_hwnd);
+}
+
+int CCtrlListBox::GetCurSel() const
+{ return ListBox_GetCurSel(m_hwnd);
+}
+
+LPARAM CCtrlListBox::GetItemData(int index) const
+{ return ListBox_GetItemData(m_hwnd, index);
+}
+
+int CCtrlListBox::GetItemRect(int index, RECT *pResult) const
+{ return ListBox_GetItemRect(m_hwnd, index, pResult);
+}
+
+wchar_t* CCtrlListBox::GetItemText(int index) const
+{
+ wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result);
+ return result;
+}
+
+wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const
+{
+ wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result);
+ mir_wstrncpy(buf, result, size);
+ return buf;
+}
+
+bool CCtrlListBox::GetSel(int index) const
+{ return ListBox_GetSel(m_hwnd, index) ? true : false;
+}
+
+int CCtrlListBox::GetSelCount() const
+{ return ListBox_GetSelCount(m_hwnd);
+}
+
+int* CCtrlListBox::GetSelItems(int *items, int count) const
+{
+ ListBox_GetSelItems(m_hwnd, count, items);
+ return items;
+}
+
+int* CCtrlListBox::GetSelItems() const
+{
+ int count = GetSelCount() + 1;
+ int *result = (int *)mir_alloc(sizeof(int) * count);
+ ListBox_GetSelItems(m_hwnd, count, result);
+ result[count-1] = -1;
+ return result;
+}
+
+int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data)
+{
+ int iItem = ListBox_InsertString(m_hwnd, pos, text);
+ ListBox_SetItemData(m_hwnd, iItem, data);
+ return iItem;
+}
+
+void CCtrlListBox::ResetContent()
+{ ListBox_ResetContent(m_hwnd);
+}
+
+int CCtrlListBox::SelectString(const wchar_t *str)
+{ return ListBox_SelectString(m_hwnd, 0, str);
+}
+
+int CCtrlListBox::SetCurSel(int index)
+{ return ListBox_SetCurSel(m_hwnd, index);
+}
+
+void CCtrlListBox::SetItemData(int index, LPARAM data)
+{ ListBox_SetItemData(m_hwnd, index, data);
+}
+
+void CCtrlListBox::SetItemHeight(int index, int iHeight)
+{ ListBox_SetItemHeight(m_hwnd, index, iHeight);
+}
+
+void CCtrlListBox::SetSel(int index, bool sel)
+{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index);
+}
diff --git a/src/mir_core/src/Linux/CCtrlListView.cpp b/src/mir_core/src/Linux/CCtrlListView.cpp index eb57951a3c..40bb1f481e 100644 --- a/src/mir_core/src/Linux/CCtrlListView.cpp +++ b/src/mir_core/src/Linux/CCtrlListView.cpp @@ -1,551 +1,551 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListView - -CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, pnmh }; - - switch (pnmh->code) { - case NM_CLICK: OnClick(&evt); return TRUE; - case NM_DBLCLK: OnDoubleClick(&evt); return TRUE; - case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; - case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE; - case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; - case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; - case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE; - case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE; - case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE; - case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; - case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; - case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE; - case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; - case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; - case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE; - case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE; - case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE; - case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE; - case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE; - case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE; - case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; - - case LVN_ITEMCHANGED: - if (!m_parentWnd || !m_parentWnd->IsInitialized()) - return FALSE; - - OnItemChanged(&evt); - - // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes - if (evt.nmlv->uChanged & LVIF_STATE) - if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0) - NotifyChange(); - return TRUE; - - case LVN_ODSTATECHANGED: - NotifyChange(); - return TRUE; - } - - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param) -{ - int result = l1 - l2; - int newItem = HIWORD(param); - int oldItem = LOWORD(param); - if (newItem > oldItem) - return (l1 == oldItem && l2 <= newItem) ? 1 : result; - - return (l2 == oldItem && l1 >= newItem) ? 1 : result; -} - -int CCtrlListView::MoveItem(int idx, int direction) -{ - if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0)) - return idx; - - if (idx < 0) - idx = GetNextItem(-1, LVNI_FOCUSED); - SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction)); - return idx + direction; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CCtrlListView::SetCurSel(int idx) -{ - SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); -} - -// additional api -HIMAGELIST CCtrlListView::CreateImageList(int iImageList) -{ - HIMAGELIST hIml = GetImageList(iImageList); - if (hIml) - return hIml; - - hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - SetImageList(hIml, iImageList); - return hIml; -} - -void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx) -{ - LVCOLUMN lvc; - lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc.iImage = 0; - lvc.pszText = (LPWSTR)name; - lvc.cx = cx; - lvc.iSubItem = iSubItem; - InsertColumn(iSubItem, &lvc); -} - -void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name) -{ - LVGROUP lvg = { 0 }; - lvg.cbSize = sizeof(lvg); - lvg.mask = LVGF_HEADER | LVGF_GROUPID; - lvg.pszHeader = (LPWSTR)name; - lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader); - lvg.iGroupId = iGroupId; - InsertGroup(-1, &lvg); -} - -int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE; - lvi.iSubItem = 0; - lvi.pszText = (LPWSTR)text; - lvi.iImage = iIcon; - lvi.lParam = lParam; - if (iGroupId >= 0) { - lvi.mask |= LVIF_GROUPID; - lvi.iGroupId = iGroupId; - } - - return InsertItem(&lvi); -} - -void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_TEXT; - lvi.iItem = iItem; - lvi.iSubItem = iSubItem; - lvi.pszText = (LPWSTR)text; - if (iIcon >= 0) { - lvi.mask |= LVIF_IMAGE; - lvi.iImage = iIcon; - } - - SetItem(&lvi); -} - -LPARAM CCtrlListView::GetItemData(int iItem) const -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM; - lvi.iItem = iItem; - return GetItem(&lvi) ? lvi.lParam : -1; -} - -void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - - // position is empty, let's fill it using selection - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetSelectionMark(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc, TRUE); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - // position is present, let's calculate current item - else { - LVHITTESTINFO hti; - hti.pt = pos.pt; - ScreenToClient(m_hwnd, &hti.pt); - if (SubItemHitTest(&hti) != -1) { - pos.iCurr = hti.iItem; - return; - } - } - CSuper::GetCaretPos(pos); -} - -// classic api -uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount) -{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount); -} -void CCtrlListView::Arrange(UINT code) -{ ListView_Arrange(m_hwnd, code); -} -void CCtrlListView::CancelEditLabel() -{ ListView_CancelEditLabel(m_hwnd); -} -HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft) -{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft); -} -void CCtrlListView::DeleteAllItems() -{ ListView_DeleteAllItems(m_hwnd); -} -void CCtrlListView::DeleteColumn(int iCol) -{ ListView_DeleteColumn(m_hwnd, iCol); -} -void CCtrlListView::DeleteItem(int iItem) -{ ListView_DeleteItem(m_hwnd, iItem); -} -HWND CCtrlListView::EditLabel(int iItem) -{ return ListView_EditLabel(m_hwnd, iItem); -} -int CCtrlListView::EnableGroupView(BOOL fEnable) -{ return ListView_EnableGroupView(m_hwnd, fEnable); -} -BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK) -{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK); -} -int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi) -{ return ListView_FindItem(m_hwnd, iStart, plvfi); -} -COLORREF CCtrlListView::GetBkColor() const -{ return ListView_GetBkColor(m_hwnd); -} -void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const -{ ListView_GetBkImage(m_hwnd, plvbki); -} -UINT CCtrlListView::GetCallbackMask() const -{ return ListView_GetCallbackMask(m_hwnd); -} -BOOL CCtrlListView::GetCheckState(UINT iIndex) const -{ return ListView_GetCheckState(m_hwnd, iIndex); -} -void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const -{ ListView_GetColumn(m_hwnd, iCol, pcol); -} -void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const -{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -int CCtrlListView::GetColumnWidth(int iCol) const -{ return ListView_GetColumnWidth(m_hwnd, iCol); -} -int CCtrlListView::GetCountPerPage() const -{ return ListView_GetCountPerPage(m_hwnd); -} -HWND CCtrlListView::GetEditControl() const -{ return ListView_GetEditControl(m_hwnd); -} -uint32_t CCtrlListView::GetExtendedListViewStyle() const -{ return ListView_GetExtendedListViewStyle(m_hwnd); -} -void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const -{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics); -} -HWND CCtrlListView::GetHeader() const -{ return ListView_GetHeader(m_hwnd); -} -HCURSOR CCtrlListView::GetHotCursor() const -{ return ListView_GetHotCursor(m_hwnd); -} -INT CCtrlListView::GetHotItem() const -{ return ListView_GetHotItem(m_hwnd); -} -uint32_t CCtrlListView::GetHoverTime() const -{ return ListView_GetHoverTime(m_hwnd); -} -HIMAGELIST CCtrlListView::GetImageList(int iImageList) const -{ return ListView_GetImageList(m_hwnd, iImageList); -} -BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const -{ return ListView_GetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::GetInsertMarkColor() const -{ return ListView_GetInsertMarkColor(m_hwnd); -} -int CCtrlListView::GetInsertMarkRect(LPRECT prc) const -{ return ListView_GetInsertMarkRect(m_hwnd, prc); -} -BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const -{ return ListView_GetISearchString(m_hwnd, lpsz); -} -bool CCtrlListView::GetItem(LPLVITEM pitem) const -{ return ListView_GetItem(m_hwnd, pitem) == TRUE; -} -int CCtrlListView::GetItemCount() const -{ return ListView_GetItemCount(m_hwnd); -} -void CCtrlListView::GetItemPosition(int i, POINT *ppt) const -{ ListView_GetItemPosition(m_hwnd, i, ppt); -} -void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const -{ ListView_GetItemRect(m_hwnd, i, prc, code); -} -uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const -{ return ListView_GetItemSpacing(m_hwnd, fSmall); -} -UINT CCtrlListView::GetItemState(int i, UINT mask) const -{ return ListView_GetItemState(m_hwnd, i, mask); -} -void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const -{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax); -} -int CCtrlListView::GetNextItem(int iStart, UINT flags) const -{ return ListView_GetNextItem(m_hwnd, iStart, flags); -} -BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const -{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas); -} -BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const -{ return ListView_GetOrigin(m_hwnd, lpptOrg); -} -COLORREF CCtrlListView::GetOutlineColor() const -{ return ListView_GetOutlineColor(m_hwnd); -} -UINT CCtrlListView::GetSelectedColumn() const -{ return ListView_GetSelectedColumn(m_hwnd); -} -UINT CCtrlListView::GetSelectedCount() const -{ return ListView_GetSelectedCount(m_hwnd); -} -INT CCtrlListView::GetSelectionMark() const -{ return ListView_GetSelectionMark(m_hwnd); -} -int CCtrlListView::GetStringWidth(LPCSTR psz) const -{ return ListView_GetStringWidth(m_hwnd, psz); -} -BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const -{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect); -} -COLORREF CCtrlListView::GetTextBkColor() const -{ return ListView_GetTextBkColor(m_hwnd); -} -COLORREF CCtrlListView::GetTextColor() const -{ return ListView_GetTextColor(m_hwnd); -} -void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const -{ ListView_GetTileInfo(m_hwnd, plvtinfo); -} -void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const -{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::GetToolTips() const -{ return ListView_GetToolTips(m_hwnd); -} -int CCtrlListView::GetTopIndex() const -{ return ListView_GetTopIndex(m_hwnd); -} -BOOL CCtrlListView::GetUnicodeFormat() const -{ return ListView_GetUnicodeFormat(m_hwnd); -} -uint32_t CCtrlListView::GetView() const -{ return ListView_GetView(m_hwnd); -} -BOOL CCtrlListView::GetViewRect(RECT *prc) const -{ return ListView_GetViewRect(m_hwnd, prc); -} -void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const -{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -BOOL CCtrlListView::HasGroup(int dwGroupId) -{ return ListView_HasGroup(m_hwnd, dwGroupId); -} -int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const -{ return ListView_HitTest(m_hwnd, pinfo); -} -int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol) -{ return ListView_InsertColumn(m_hwnd, iCol, pcol); -} -int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp) -{ return ListView_InsertGroup(m_hwnd, index, pgrp); -} -void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert) -{ ListView_InsertGroupSorted(m_hwnd, structInsert); -} -int CCtrlListView::InsertItem(const LVITEM *pitem) -{ return ListView_InsertItem(m_hwnd, pitem); -} -BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim) -{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim); -} -BOOL CCtrlListView::IsGroupViewEnabled() -{ return ListView_IsGroupViewEnabled(m_hwnd); -} -UINT CCtrlListView::MapIDToIndex(UINT id) -{ return ListView_MapIDToIndex(m_hwnd, id); -} -UINT CCtrlListView::MapIndexToID(UINT index) -{ return ListView_MapIndexToID(m_hwnd, index); -} -BOOL CCtrlListView::RedrawItems(int iFirst, int iLast) -{ return ListView_RedrawItems(m_hwnd, iFirst, iLast); -} -void CCtrlListView::RemoveAllGroups() -{ ListView_RemoveAllGroups(m_hwnd); -} -int CCtrlListView::RemoveGroup(int iGroupId) -{ return ListView_RemoveGroup(m_hwnd, iGroupId); -} -BOOL CCtrlListView::Scroll(int dx, int dy) -{ return ListView_Scroll(m_hwnd, dx, dy); -} -BOOL CCtrlListView::SetBkColor(COLORREF clrBk) -{ return ListView_SetBkColor(m_hwnd, clrBk); -} -BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki) -{ return ListView_SetBkImage(m_hwnd, plvbki); -} -BOOL CCtrlListView::SetCallbackMask(UINT mask) -{ return ListView_SetCallbackMask(m_hwnd, mask); -} -void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck) -{ ListView_SetCheckState(m_hwnd, iIndex, fCheck); -} -BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol) -{ return ListView_SetColumn(m_hwnd, iCol, pcol); -} -BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray) -{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -BOOL CCtrlListView::SetColumnWidth(int iCol, int cx) -{ return ListView_SetColumnWidth(m_hwnd, iCol, cx); -} -void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle); -} -void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle); -} -int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp) -{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp); -} -void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) -{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics); -} -HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor) -{ return ListView_SetHotCursor(m_hwnd, hCursor); -} -INT CCtrlListView::SetHotItem(INT iIndex) -{ return ListView_SetHotItem(m_hwnd, iIndex); -} -void CCtrlListView::SetHoverTime(uint32_t dwHoverTime) -{ ListView_SetHoverTime(m_hwnd, dwHoverTime); -} -uint32_t CCtrlListView::SetIconSpacing(int cx, int cy) -{ return ListView_SetIconSpacing(m_hwnd, cx, cy); -} -HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList) -{ return ListView_SetImageList(m_hwnd, himl, iImageList); -} -BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip) -{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip); -} -BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim) -{ return ListView_SetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color) -{ return ListView_SetInsertMarkColor(m_hwnd, color); -} -BOOL CCtrlListView::SetItem(const LVITEM *pitem) -{ return ListView_SetItem(m_hwnd, pitem); -} -void CCtrlListView::SetItemCount(int cItems) -{ ListView_SetItemCount(m_hwnd, cItems); -} -void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags) -{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags); -} -BOOL CCtrlListView::SetItemPosition(int i, int x, int y) -{ return ListView_SetItemPosition(m_hwnd, i, x, y); -} -void CCtrlListView::SetItemPosition32(int iItem, int x, int y) -{ ListView_SetItemPosition32(m_hwnd, iItem, x, y); -} -void CCtrlListView::SetItemState(int i, UINT state, UINT mask) -{ ListView_SetItemState(m_hwnd, i, state, mask); -} -void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText) -{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText); -} -COLORREF CCtrlListView::SetOutlineColor(COLORREF color) -{ return ListView_SetOutlineColor(m_hwnd, color); -} -void CCtrlListView::SetSelectedColumn(int iCol) -{ ListView_SetSelectedColumn(m_hwnd, iCol); -} -INT CCtrlListView::SetSelectionMark(INT iIndex) -{ return ListView_SetSelectionMark(m_hwnd, iIndex); -} -BOOL CCtrlListView::SetTextBkColor(COLORREF clrText) -{ return ListView_SetTextBkColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTextColor(COLORREF clrText) -{ return ListView_SetTextColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo) -{ return ListView_SetTileInfo(m_hwnd, plvtinfo); -} -BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) -{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::SetToolTips(HWND ToolTip) -{ return ListView_SetToolTips(m_hwnd, ToolTip); -} -BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode) -{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode); -} -int CCtrlListView::SetView(uint32_t iView) -{ return ListView_SetView(m_hwnd, iView); -} -void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc) -{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv) -{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv); -} -BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort); -} -BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort); -} -INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const -{ return ListView_SubItemHitTest(m_hwnd, pInfo); -} -BOOL CCtrlListView::Update(int iItem) -{ return ListView_Update(m_hwnd, iItem); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListView
+
+CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, pnmh };
+
+ switch (pnmh->code) {
+ case NM_CLICK: OnClick(&evt); return TRUE;
+ case NM_DBLCLK: OnDoubleClick(&evt); return TRUE;
+ case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE;
+ case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE;
+ case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE;
+ case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE;
+ case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE;
+ case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE;
+ case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE;
+ case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE;
+ case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE;
+ case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE;
+ case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE;
+ case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE;
+ case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE;
+ case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE;
+ case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE;
+ case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE;
+ case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE;
+ case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE;
+ case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE;
+
+ case LVN_ITEMCHANGED:
+ if (!m_parentWnd || !m_parentWnd->IsInitialized())
+ return FALSE;
+
+ OnItemChanged(&evt);
+
+ // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes
+ if (evt.nmlv->uChanged & LVIF_STATE)
+ if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0)
+ NotifyChange();
+ return TRUE;
+
+ case LVN_ODSTATECHANGED:
+ NotifyChange();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param)
+{
+ int result = l1 - l2;
+ int newItem = HIWORD(param);
+ int oldItem = LOWORD(param);
+ if (newItem > oldItem)
+ return (l1 == oldItem && l2 <= newItem) ? 1 : result;
+
+ return (l2 == oldItem && l1 >= newItem) ? 1 : result;
+}
+
+int CCtrlListView::MoveItem(int idx, int direction)
+{
+ if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0))
+ return idx;
+
+ if (idx < 0)
+ idx = GetNextItem(-1, LVNI_FOCUSED);
+ SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction));
+ return idx + direction;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CCtrlListView::SetCurSel(int idx)
+{
+ SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
+}
+
+// additional api
+HIMAGELIST CCtrlListView::CreateImageList(int iImageList)
+{
+ HIMAGELIST hIml = GetImageList(iImageList);
+ if (hIml)
+ return hIml;
+
+ hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);
+ SetImageList(hIml, iImageList);
+ return hIml;
+}
+
+void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx)
+{
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
+ lvc.iImage = 0;
+ lvc.pszText = (LPWSTR)name;
+ lvc.cx = cx;
+ lvc.iSubItem = iSubItem;
+ InsertColumn(iSubItem, &lvc);
+}
+
+void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name)
+{
+ LVGROUP lvg = { 0 };
+ lvg.cbSize = sizeof(lvg);
+ lvg.mask = LVGF_HEADER | LVGF_GROUPID;
+ lvg.pszHeader = (LPWSTR)name;
+ lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader);
+ lvg.iGroupId = iGroupId;
+ InsertGroup(-1, &lvg);
+}
+
+int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId)
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE;
+ lvi.iSubItem = 0;
+ lvi.pszText = (LPWSTR)text;
+ lvi.iImage = iIcon;
+ lvi.lParam = lParam;
+ if (iGroupId >= 0) {
+ lvi.mask |= LVIF_GROUPID;
+ lvi.iGroupId = iGroupId;
+ }
+
+ return InsertItem(&lvi);
+}
+
+void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon)
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = iItem;
+ lvi.iSubItem = iSubItem;
+ lvi.pszText = (LPWSTR)text;
+ if (iIcon >= 0) {
+ lvi.mask |= LVIF_IMAGE;
+ lvi.iImage = iIcon;
+ }
+
+ SetItem(&lvi);
+}
+
+LPARAM CCtrlListView::GetItemData(int iItem) const
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = iItem;
+ return GetItem(&lvi) ? lvi.lParam : -1;
+}
+
+void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+
+ // position is empty, let's fill it using selection
+ if (pos.pt.x == 0 && pos.pt.y == 0) {
+ pos.iCurr = GetSelectionMark();
+ if (pos.iCurr != -1) {
+ RECT rc;
+ GetItemRect(pos.iCurr, &rc, TRUE);
+ pos.pt.x = rc.left + 8;
+ pos.pt.y = rc.top + 8;
+ ClientToScreen(m_hwnd, &pos.pt);
+ return;
+ }
+ }
+ // position is present, let's calculate current item
+ else {
+ LVHITTESTINFO hti;
+ hti.pt = pos.pt;
+ ScreenToClient(m_hwnd, &hti.pt);
+ if (SubItemHitTest(&hti) != -1) {
+ pos.iCurr = hti.iItem;
+ return;
+ }
+ }
+ CSuper::GetCaretPos(pos);
+}
+
+// classic api
+uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount)
+{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount);
+}
+void CCtrlListView::Arrange(UINT code)
+{ ListView_Arrange(m_hwnd, code);
+}
+void CCtrlListView::CancelEditLabel()
+{ ListView_CancelEditLabel(m_hwnd);
+}
+HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft)
+{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft);
+}
+void CCtrlListView::DeleteAllItems()
+{ ListView_DeleteAllItems(m_hwnd);
+}
+void CCtrlListView::DeleteColumn(int iCol)
+{ ListView_DeleteColumn(m_hwnd, iCol);
+}
+void CCtrlListView::DeleteItem(int iItem)
+{ ListView_DeleteItem(m_hwnd, iItem);
+}
+HWND CCtrlListView::EditLabel(int iItem)
+{ return ListView_EditLabel(m_hwnd, iItem);
+}
+int CCtrlListView::EnableGroupView(BOOL fEnable)
+{ return ListView_EnableGroupView(m_hwnd, fEnable);
+}
+BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK)
+{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK);
+}
+int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi)
+{ return ListView_FindItem(m_hwnd, iStart, plvfi);
+}
+COLORREF CCtrlListView::GetBkColor() const
+{ return ListView_GetBkColor(m_hwnd);
+}
+void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const
+{ ListView_GetBkImage(m_hwnd, plvbki);
+}
+UINT CCtrlListView::GetCallbackMask() const
+{ return ListView_GetCallbackMask(m_hwnd);
+}
+BOOL CCtrlListView::GetCheckState(UINT iIndex) const
+{ return ListView_GetCheckState(m_hwnd, iIndex);
+}
+void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const
+{ ListView_GetColumn(m_hwnd, iCol, pcol);
+}
+void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const
+{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray);
+}
+int CCtrlListView::GetColumnWidth(int iCol) const
+{ return ListView_GetColumnWidth(m_hwnd, iCol);
+}
+int CCtrlListView::GetCountPerPage() const
+{ return ListView_GetCountPerPage(m_hwnd);
+}
+HWND CCtrlListView::GetEditControl() const
+{ return ListView_GetEditControl(m_hwnd);
+}
+uint32_t CCtrlListView::GetExtendedListViewStyle() const
+{ return ListView_GetExtendedListViewStyle(m_hwnd);
+}
+void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const
+{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics);
+}
+HWND CCtrlListView::GetHeader() const
+{ return ListView_GetHeader(m_hwnd);
+}
+HCURSOR CCtrlListView::GetHotCursor() const
+{ return ListView_GetHotCursor(m_hwnd);
+}
+INT CCtrlListView::GetHotItem() const
+{ return ListView_GetHotItem(m_hwnd);
+}
+uint32_t CCtrlListView::GetHoverTime() const
+{ return ListView_GetHoverTime(m_hwnd);
+}
+HIMAGELIST CCtrlListView::GetImageList(int iImageList) const
+{ return ListView_GetImageList(m_hwnd, iImageList);
+}
+BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const
+{ return ListView_GetInsertMark(m_hwnd, plvim);
+}
+COLORREF CCtrlListView::GetInsertMarkColor() const
+{ return ListView_GetInsertMarkColor(m_hwnd);
+}
+int CCtrlListView::GetInsertMarkRect(LPRECT prc) const
+{ return ListView_GetInsertMarkRect(m_hwnd, prc);
+}
+BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const
+{ return ListView_GetISearchString(m_hwnd, lpsz);
+}
+bool CCtrlListView::GetItem(LPLVITEM pitem) const
+{ return ListView_GetItem(m_hwnd, pitem) == TRUE;
+}
+int CCtrlListView::GetItemCount() const
+{ return ListView_GetItemCount(m_hwnd);
+}
+void CCtrlListView::GetItemPosition(int i, POINT *ppt) const
+{ ListView_GetItemPosition(m_hwnd, i, ppt);
+}
+void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const
+{ ListView_GetItemRect(m_hwnd, i, prc, code);
+}
+uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const
+{ return ListView_GetItemSpacing(m_hwnd, fSmall);
+}
+UINT CCtrlListView::GetItemState(int i, UINT mask) const
+{ return ListView_GetItemState(m_hwnd, i, mask);
+}
+void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const
+{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax);
+}
+int CCtrlListView::GetNextItem(int iStart, UINT flags) const
+{ return ListView_GetNextItem(m_hwnd, iStart, flags);
+}
+BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const
+{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas);
+}
+BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const
+{ return ListView_GetOrigin(m_hwnd, lpptOrg);
+}
+COLORREF CCtrlListView::GetOutlineColor() const
+{ return ListView_GetOutlineColor(m_hwnd);
+}
+UINT CCtrlListView::GetSelectedColumn() const
+{ return ListView_GetSelectedColumn(m_hwnd);
+}
+UINT CCtrlListView::GetSelectedCount() const
+{ return ListView_GetSelectedCount(m_hwnd);
+}
+INT CCtrlListView::GetSelectionMark() const
+{ return ListView_GetSelectionMark(m_hwnd);
+}
+int CCtrlListView::GetStringWidth(LPCSTR psz) const
+{ return ListView_GetStringWidth(m_hwnd, psz);
+}
+BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const
+{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect);
+}
+COLORREF CCtrlListView::GetTextBkColor() const
+{ return ListView_GetTextBkColor(m_hwnd);
+}
+COLORREF CCtrlListView::GetTextColor() const
+{ return ListView_GetTextColor(m_hwnd);
+}
+void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const
+{ ListView_GetTileInfo(m_hwnd, plvtinfo);
+}
+void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const
+{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo);
+}
+HWND CCtrlListView::GetToolTips() const
+{ return ListView_GetToolTips(m_hwnd);
+}
+int CCtrlListView::GetTopIndex() const
+{ return ListView_GetTopIndex(m_hwnd);
+}
+BOOL CCtrlListView::GetUnicodeFormat() const
+{ return ListView_GetUnicodeFormat(m_hwnd);
+}
+uint32_t CCtrlListView::GetView() const
+{ return ListView_GetView(m_hwnd);
+}
+BOOL CCtrlListView::GetViewRect(RECT *prc) const
+{ return ListView_GetViewRect(m_hwnd, prc);
+}
+void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const
+{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc);
+}
+BOOL CCtrlListView::HasGroup(int dwGroupId)
+{ return ListView_HasGroup(m_hwnd, dwGroupId);
+}
+int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const
+{ return ListView_HitTest(m_hwnd, pinfo);
+}
+int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol)
+{ return ListView_InsertColumn(m_hwnd, iCol, pcol);
+}
+int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp)
+{ return ListView_InsertGroup(m_hwnd, index, pgrp);
+}
+void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert)
+{ ListView_InsertGroupSorted(m_hwnd, structInsert);
+}
+int CCtrlListView::InsertItem(const LVITEM *pitem)
+{ return ListView_InsertItem(m_hwnd, pitem);
+}
+BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim)
+{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim);
+}
+BOOL CCtrlListView::IsGroupViewEnabled()
+{ return ListView_IsGroupViewEnabled(m_hwnd);
+}
+UINT CCtrlListView::MapIDToIndex(UINT id)
+{ return ListView_MapIDToIndex(m_hwnd, id);
+}
+UINT CCtrlListView::MapIndexToID(UINT index)
+{ return ListView_MapIndexToID(m_hwnd, index);
+}
+BOOL CCtrlListView::RedrawItems(int iFirst, int iLast)
+{ return ListView_RedrawItems(m_hwnd, iFirst, iLast);
+}
+void CCtrlListView::RemoveAllGroups()
+{ ListView_RemoveAllGroups(m_hwnd);
+}
+int CCtrlListView::RemoveGroup(int iGroupId)
+{ return ListView_RemoveGroup(m_hwnd, iGroupId);
+}
+BOOL CCtrlListView::Scroll(int dx, int dy)
+{ return ListView_Scroll(m_hwnd, dx, dy);
+}
+BOOL CCtrlListView::SetBkColor(COLORREF clrBk)
+{ return ListView_SetBkColor(m_hwnd, clrBk);
+}
+BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki)
+{ return ListView_SetBkImage(m_hwnd, plvbki);
+}
+BOOL CCtrlListView::SetCallbackMask(UINT mask)
+{ return ListView_SetCallbackMask(m_hwnd, mask);
+}
+void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck)
+{ ListView_SetCheckState(m_hwnd, iIndex, fCheck);
+}
+BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol)
+{ return ListView_SetColumn(m_hwnd, iCol, pcol);
+}
+BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray)
+{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray);
+}
+BOOL CCtrlListView::SetColumnWidth(int iCol, int cx)
+{ return ListView_SetColumnWidth(m_hwnd, iCol, cx);
+}
+void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle)
+{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle);
+}
+void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle)
+{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle);
+}
+int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp)
+{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp);
+}
+void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics)
+{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics);
+}
+HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor)
+{ return ListView_SetHotCursor(m_hwnd, hCursor);
+}
+INT CCtrlListView::SetHotItem(INT iIndex)
+{ return ListView_SetHotItem(m_hwnd, iIndex);
+}
+void CCtrlListView::SetHoverTime(uint32_t dwHoverTime)
+{ ListView_SetHoverTime(m_hwnd, dwHoverTime);
+}
+uint32_t CCtrlListView::SetIconSpacing(int cx, int cy)
+{ return ListView_SetIconSpacing(m_hwnd, cx, cy);
+}
+HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList)
+{ return ListView_SetImageList(m_hwnd, himl, iImageList);
+}
+BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip)
+{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip);
+}
+BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim)
+{ return ListView_SetInsertMark(m_hwnd, plvim);
+}
+COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color)
+{ return ListView_SetInsertMarkColor(m_hwnd, color);
+}
+BOOL CCtrlListView::SetItem(const LVITEM *pitem)
+{ return ListView_SetItem(m_hwnd, pitem);
+}
+void CCtrlListView::SetItemCount(int cItems)
+{ ListView_SetItemCount(m_hwnd, cItems);
+}
+void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags)
+{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags);
+}
+BOOL CCtrlListView::SetItemPosition(int i, int x, int y)
+{ return ListView_SetItemPosition(m_hwnd, i, x, y);
+}
+void CCtrlListView::SetItemPosition32(int iItem, int x, int y)
+{ ListView_SetItemPosition32(m_hwnd, iItem, x, y);
+}
+void CCtrlListView::SetItemState(int i, UINT state, UINT mask)
+{ ListView_SetItemState(m_hwnd, i, state, mask);
+}
+void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText)
+{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText);
+}
+COLORREF CCtrlListView::SetOutlineColor(COLORREF color)
+{ return ListView_SetOutlineColor(m_hwnd, color);
+}
+void CCtrlListView::SetSelectedColumn(int iCol)
+{ ListView_SetSelectedColumn(m_hwnd, iCol);
+}
+INT CCtrlListView::SetSelectionMark(INT iIndex)
+{ return ListView_SetSelectionMark(m_hwnd, iIndex);
+}
+BOOL CCtrlListView::SetTextBkColor(COLORREF clrText)
+{ return ListView_SetTextBkColor(m_hwnd, clrText);
+}
+BOOL CCtrlListView::SetTextColor(COLORREF clrText)
+{ return ListView_SetTextColor(m_hwnd, clrText);
+}
+BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo)
+{ return ListView_SetTileInfo(m_hwnd, plvtinfo);
+}
+BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo)
+{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo);
+}
+HWND CCtrlListView::SetToolTips(HWND ToolTip)
+{ return ListView_SetToolTips(m_hwnd, ToolTip);
+}
+BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode)
+{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode);
+}
+int CCtrlListView::SetView(uint32_t iView)
+{ return ListView_SetView(m_hwnd, iView);
+}
+void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc)
+{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc);
+}
+int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv)
+{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv);
+}
+BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
+{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort);
+}
+BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
+{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort);
+}
+INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const
+{ return ListView_SubItemHitTest(m_hwnd, pInfo);
+}
+BOOL CCtrlListView::Update(int iItem)
+{ return ListView_Update(m_hwnd, iItem);
+}
diff --git a/src/mir_core/src/Linux/CCtrlMButton.cpp b/src/mir_core/src/Linux/CCtrlMButton.cpp index 8d9198e144..cc05a3a8e4 100644 --- a/src/mir_core/src/Linux/CCtrlMButton.cpp +++ b/src/mir_core/src/Linux/CCtrlMButton.cpp @@ -1,62 +1,62 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlMButton - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(hIcon), - m_toolTip(tooltip) -{} - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(::Skin_LoadIcon(iCoreIcon)), - m_toolTip(tooltip) -{} - -CCtrlMButton::~CCtrlMButton() -{ - ::IcoLib_ReleaseIcon(m_hIcon); -} - -void CCtrlMButton::OnInit() -{ - CCtrlButton::OnInit(); - - SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon); - SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0); - SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0); -} - -void CCtrlMButton::MakeFlat() -{ - SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0); -} - -void CCtrlMButton::MakePush() -{ - SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlMButton
+
+CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip)
+ : CCtrlButton(dlg, ctrlId),
+ m_hIcon(hIcon),
+ m_toolTip(tooltip)
+{}
+
+CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip)
+ : CCtrlButton(dlg, ctrlId),
+ m_hIcon(::Skin_LoadIcon(iCoreIcon)),
+ m_toolTip(tooltip)
+{}
+
+CCtrlMButton::~CCtrlMButton()
+{
+ ::IcoLib_ReleaseIcon(m_hIcon);
+}
+
+void CCtrlMButton::OnInit()
+{
+ CCtrlButton::OnInit();
+
+ SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon);
+ SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0);
+ SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0);
+}
+
+void CCtrlMButton::MakeFlat()
+{
+ SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0);
+}
+
+void CCtrlMButton::MakePush()
+{
+ SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0);
+}
diff --git a/src/mir_core/src/Linux/CCtrlPages.cpp b/src/mir_core/src/Linux/CCtrlPages.cpp index 512c32e142..c2a95553c5 100644 --- a/src/mir_core/src/Linux/CCtrlPages.cpp +++ b/src/mir_core/src/Linux/CCtrlPages.cpp @@ -1,411 +1,411 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static volatile long g_order = 1; - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlPages - -struct CCtrlPages::TPageInfo : public MZeroedObject -{ - TPageInfo() - { - m_iOrder = InterlockedIncrement(&g_order); - } - - ~TPageInfo() - { - if (m_hIcon) - DestroyIcon(m_hIcon); - } - - int m_iOrder; - ptrW m_ptszHeader; - HICON m_hIcon; - bool m_bChanged, m_bScheduledResize; - CDlgBase *m_pDlg; -}; - -CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId), - m_hIml(nullptr), - m_pActivePage(nullptr), - m_pages(4, NumericKeySortT) -{} - -void CCtrlPages::OnInit() -{ - CSuper::OnInit(); - Subclass(); - - for (auto &it : m_pages) - InsertPage(it); - m_pages.destroy(); - - ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT); - - TPageInfo *info = GetCurrPage(); - if (info) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - int tabCount; - - switch (msg) { - case WM_SIZE: - if (TPageInfo *pCurrInfo = GetCurrPage()) { - tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p == nullptr) - continue; - if (p == pCurrInfo) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - else p->m_bScheduledResize = true; - } - } - break; - - case PSM_CHANGED: - if (TPageInfo *info = GetCurrPage()) - info->m_bChanged = TRUE; - return TRUE; - - case PSM_FORCECHANGED: - tabCount = GetCount(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p) { - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - if (pshn.hdr.hwndFrom != nullptr) - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - } - break; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} - -void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg) -{ - TPageInfo *info = new TPageInfo; - info->m_pDlg = pDlg; - info->m_hIcon = hIcon; - info->m_ptszHeader = mir_wstrdup(ptszName); - - if (m_hwnd != nullptr) { - InsertPage(info); - - if (GetCount() == 1) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - } - m_pages.insert(info); -} - -void CCtrlPages::ActivatePage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - if (info == nullptr || info->m_pDlg == nullptr) - return; - - if (m_pActivePage != nullptr) - ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE); - - m_pActivePage = info->m_pDlg; - if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - - TabCtrl_SetCurSel(m_hwnd, iPage); - ShowPage(m_pActivePage); - ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0); -} - -void CCtrlPages::CheckRowCount() -{ - int iRowCount = TabCtrl_GetRowCount(m_hwnd); - if (m_numRows != iRowCount) { - m_numRows = iRowCount; - for (auto &p : m_pages) - p->m_bScheduledResize = true; - } -} - -int CCtrlPages::GetCount() -{ - return TabCtrl_GetItemCount(m_hwnd); -} - -CDlgBase* CCtrlPages::GetNthPage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - return (info == nullptr) ? nullptr : info->m_pDlg; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage() -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, iPage, &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -int CCtrlPages::GetDlgIndex(CDlgBase *pDlg) -{ - int tabCount = TabCtrl_GetItemCount(m_hwnd); - for (int i = 0; i < tabCount; i++) { - TCITEM tci; - tci.mask = TCIF_PARAM | TCIF_IMAGE; - TabCtrl_GetItem(m_hwnd, i, &tci); - TPageInfo *pPage = (TPageInfo *)tci.lParam; - if (pPage == nullptr) - continue; - - if (pPage->m_pDlg == pDlg) - return i; - } - - return -1; -} - -void CCtrlPages::InsertPage(TPageInfo *pPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)pPage; - tci.pszText = TranslateW_LP(pPage->m_ptszHeader); - if (pPage->m_hIcon) { - if (!m_hIml) { - m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - TabCtrl_SetImageList(m_hwnd, m_hIml); - } - - tci.mask |= TCIF_IMAGE; - tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon); - } - - TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci); - - CheckRowCount(); -} - -void CCtrlPages::RemovePage(int iPage) -{ - TPageInfo *p = GetItemPage(iPage); - if (p == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, iPage); - m_pages.remove(p); - delete p; - - CheckRowCount(); -} - -void CCtrlPages::ShowPage(CDlgBase *pDlg) -{ - if (pDlg->GetHwnd() == nullptr) { - pDlg->SetParent(m_hwnd); - pDlg->Create(); - - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE); - - EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - ShowWindow(pDlg->GetHwnd(), SW_SHOW); -} - -void CCtrlPages::SwapPages(int idx1, int idx2) -{ - TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2); - if (p1 == nullptr || p2 == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, idx1); - - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)p1; - tci.pszText = TranslateW_LP(p1->m_ptszHeader); - TabCtrl_InsertItem(m_hwnd, idx2, &tci); -} - -BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh) -{ - TPageInfo *info; - PSHNOTIFY pshn; - - switch (pnmh->code) { - case TCN_SELCHANGING: - if (info = GetCurrPage()) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) { - SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE); - return TRUE; - } - } - return TRUE; - - case TCN_SELCHANGE: - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - - if (info = GetCurrPage()) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - else m_pActivePage = nullptr; - return TRUE; - } - - return FALSE; -} - -void CCtrlPages::OnReset() -{ - CSuper::OnReset(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -bool CCtrlPages::OnApply() -{ - PSHNOTIFY pshn; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - if (m_pActivePage != nullptr) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) - return false; - } - - pshn.hdr.code = PSN_APPLY; - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) { - TabCtrl_SetCurSel(m_hwnd, i); - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - m_pActivePage = p->m_pDlg; - m_pActivePage->Show(); - return false; - } - } - - CSuper::OnApply(); - return true; -} - -void CCtrlPages::OnDestroy() -{ - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr; - if (pDlg->GetHwnd()) - pDlg->Close(); - delete p; - } - - TabCtrl_DeleteAllItems(m_hwnd); - - if (m_hIml) { - TabCtrl_SetImageList(m_hwnd, nullptr); - ImageList_Destroy(m_hIml); - } - - CSuper::OnDestroy(); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static volatile long g_order = 1;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlPages
+
+struct CCtrlPages::TPageInfo : public MZeroedObject
+{
+ TPageInfo()
+ {
+ m_iOrder = InterlockedIncrement(&g_order);
+ }
+
+ ~TPageInfo()
+ {
+ if (m_hIcon)
+ DestroyIcon(m_hIcon);
+ }
+
+ int m_iOrder;
+ ptrW m_ptszHeader;
+ HICON m_hIcon;
+ bool m_bChanged, m_bScheduledResize;
+ CDlgBase *m_pDlg;
+};
+
+CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId),
+ m_hIml(nullptr),
+ m_pActivePage(nullptr),
+ m_pages(4, NumericKeySortT)
+{}
+
+void CCtrlPages::OnInit()
+{
+ CSuper::OnInit();
+ Subclass();
+
+ for (auto &it : m_pages)
+ InsertPage(it);
+ m_pages.destroy();
+
+ ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT);
+
+ TPageInfo *info = GetCurrPage();
+ if (info) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom = m_pActivePage->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+}
+
+LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int tabCount;
+
+ switch (msg) {
+ case WM_SIZE:
+ if (TPageInfo *pCurrInfo = GetCurrPage()) {
+ tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p == nullptr)
+ continue;
+ if (p == pCurrInfo) {
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+ else p->m_bScheduledResize = true;
+ }
+ }
+ break;
+
+ case PSM_CHANGED:
+ if (TPageInfo *info = GetCurrPage())
+ info->m_bChanged = TRUE;
+ return TRUE;
+
+ case PSM_FORCECHANGED:
+ tabCount = GetCount();
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p) {
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ if (pshn.hdr.hwndFrom != nullptr)
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+ }
+ break;
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
+
+void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg)
+{
+ TPageInfo *info = new TPageInfo;
+ info->m_pDlg = pDlg;
+ info->m_hIcon = hIcon;
+ info->m_ptszHeader = mir_wstrdup(ptszName);
+
+ if (m_hwnd != nullptr) {
+ InsertPage(info);
+
+ if (GetCount() == 1) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+ }
+ }
+ m_pages.insert(info);
+}
+
+void CCtrlPages::ActivatePage(int iPage)
+{
+ TPageInfo *info = GetItemPage(iPage);
+ if (info == nullptr || info->m_pDlg == nullptr)
+ return;
+
+ if (m_pActivePage != nullptr)
+ ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE);
+
+ m_pActivePage = info->m_pDlg;
+ if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) {
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+
+ TabCtrl_SetCurSel(m_hwnd, iPage);
+ ShowPage(m_pActivePage);
+ ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0);
+}
+
+void CCtrlPages::CheckRowCount()
+{
+ int iRowCount = TabCtrl_GetRowCount(m_hwnd);
+ if (m_numRows != iRowCount) {
+ m_numRows = iRowCount;
+ for (auto &p : m_pages)
+ p->m_bScheduledResize = true;
+ }
+}
+
+int CCtrlPages::GetCount()
+{
+ return TabCtrl_GetItemCount(m_hwnd);
+}
+
+CDlgBase* CCtrlPages::GetNthPage(int iPage)
+{
+ TPageInfo *info = GetItemPage(iPage);
+ return (info == nullptr) ? nullptr : info->m_pDlg;
+}
+
+CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage()
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM;
+ if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci))
+ return nullptr;
+
+ return (TPageInfo*)tci.lParam;
+}
+
+CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage)
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM;
+ if (!TabCtrl_GetItem(m_hwnd, iPage, &tci))
+ return nullptr;
+
+ return (TPageInfo*)tci.lParam;
+}
+
+int CCtrlPages::GetDlgIndex(CDlgBase *pDlg)
+{
+ int tabCount = TabCtrl_GetItemCount(m_hwnd);
+ for (int i = 0; i < tabCount; i++) {
+ TCITEM tci;
+ tci.mask = TCIF_PARAM | TCIF_IMAGE;
+ TabCtrl_GetItem(m_hwnd, i, &tci);
+ TPageInfo *pPage = (TPageInfo *)tci.lParam;
+ if (pPage == nullptr)
+ continue;
+
+ if (pPage->m_pDlg == pDlg)
+ return i;
+ }
+
+ return -1;
+}
+
+void CCtrlPages::InsertPage(TPageInfo *pPage)
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM | TCIF_TEXT;
+ tci.lParam = (LPARAM)pPage;
+ tci.pszText = TranslateW_LP(pPage->m_ptszHeader);
+ if (pPage->m_hIcon) {
+ if (!m_hIml) {
+ m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);
+ TabCtrl_SetImageList(m_hwnd, m_hIml);
+ }
+
+ tci.mask |= TCIF_IMAGE;
+ tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon);
+ }
+
+ TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci);
+
+ CheckRowCount();
+}
+
+void CCtrlPages::RemovePage(int iPage)
+{
+ TPageInfo *p = GetItemPage(iPage);
+ if (p == nullptr)
+ return;
+
+ TabCtrl_DeleteItem(m_hwnd, iPage);
+ m_pages.remove(p);
+ delete p;
+
+ CheckRowCount();
+}
+
+void CCtrlPages::ShowPage(CDlgBase *pDlg)
+{
+ if (pDlg->GetHwnd() == nullptr) {
+ pDlg->SetParent(m_hwnd);
+ pDlg->Create();
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE);
+
+ EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB);
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom = pDlg->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+ ShowWindow(pDlg->GetHwnd(), SW_SHOW);
+}
+
+void CCtrlPages::SwapPages(int idx1, int idx2)
+{
+ TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2);
+ if (p1 == nullptr || p2 == nullptr)
+ return;
+
+ TabCtrl_DeleteItem(m_hwnd, idx1);
+
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM | TCIF_TEXT;
+ tci.lParam = (LPARAM)p1;
+ tci.pszText = TranslateW_LP(p1->m_ptszHeader);
+ TabCtrl_InsertItem(m_hwnd, idx2, &tci);
+}
+
+BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh)
+{
+ TPageInfo *info;
+ PSHNOTIFY pshn;
+
+ switch (pnmh->code) {
+ case TCN_SELCHANGING:
+ if (info = GetCurrPage()) {
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) {
+ SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ }
+ return TRUE;
+
+ case TCN_SELCHANGE:
+ if (m_pActivePage != nullptr)
+ m_pActivePage->Hide();
+
+ if (info = GetCurrPage()) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+ }
+ else m_pActivePage = nullptr;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void CCtrlPages::OnReset()
+{
+ CSuper::OnReset();
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged)
+ continue;
+
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+}
+
+bool CCtrlPages::OnApply()
+{
+ PSHNOTIFY pshn;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+
+ if (m_pActivePage != nullptr) {
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = m_pActivePage->GetHwnd();
+ if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn))
+ return false;
+ }
+
+ pshn.hdr.code = PSN_APPLY;
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged)
+ continue;
+
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) {
+ TabCtrl_SetCurSel(m_hwnd, i);
+ if (m_pActivePage != nullptr)
+ m_pActivePage->Hide();
+ m_pActivePage = p->m_pDlg;
+ m_pActivePage->Show();
+ return false;
+ }
+ }
+
+ CSuper::OnApply();
+ return true;
+}
+
+void CCtrlPages::OnDestroy()
+{
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr;
+ if (pDlg->GetHwnd())
+ pDlg->Close();
+ delete p;
+ }
+
+ TabCtrl_DeleteAllItems(m_hwnd);
+
+ if (m_hIml) {
+ TabCtrl_SetImageList(m_hwnd, nullptr);
+ ImageList_Destroy(m_hIml);
+ }
+
+ CSuper::OnDestroy();
+}
diff --git a/src/mir_core/src/Linux/CCtrlSlider.cpp b/src/mir_core/src/Linux/CCtrlSlider.cpp index 69aeb24796..9938736e42 100644 --- a/src/mir_core/src/Linux/CCtrlSlider.cpp +++ b/src/mir_core/src/Linux/CCtrlSlider.cpp @@ -1,70 +1,70 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSlider class - -CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax) -{ - m_bNotifiable = true; -} - -BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == WM_HSCROLL) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSlider::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetPosition()); - return true; -} - -void CCtrlSlider::OnReset() -{ - SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int CCtrlSlider::GetPosition() const -{ - return SendMsg(TBM_GETPOS, 0, 0); -} - -void CCtrlSlider::SetPosition(int wPos) -{ - SendMsg(TBM_SETPOS, TRUE, wPos); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlSlider class
+
+CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) :
+ CCtrlData(dlg, ctrlId),
+ m_wMin(wMin),
+ m_wMax(wMax)
+{
+ m_bNotifiable = true;
+}
+
+BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ if (idCode == WM_HSCROLL) {
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool CCtrlSlider::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetPosition());
+ return true;
+}
+
+void CCtrlSlider::OnReset()
+{
+ SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax));
+
+ if (m_dbLink != nullptr)
+ SetPosition(LoadInt());
+}
+
+int CCtrlSlider::GetPosition() const
+{
+ return SendMsg(TBM_GETPOS, 0, 0);
+}
+
+void CCtrlSlider::SetPosition(int wPos)
+{
+ SendMsg(TBM_SETPOS, TRUE, wPos);
+}
diff --git a/src/mir_core/src/Linux/CCtrlSpin.cpp b/src/mir_core/src/Linux/CCtrlSpin.cpp index 54d43e933a..54dc5ffd07 100644 --- a/src/mir_core/src/Linux/CCtrlSpin.cpp +++ b/src/mir_core/src/Linux/CCtrlSpin.cpp @@ -1,81 +1,81 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSpin class - -CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax), - m_wCurr(0) -{} - -BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == UDN_DELTAPOS) { - auto *pEvent = (NMUPDOWN *)pnmh; - m_wCurr = pEvent->iPos + pEvent->iDelta; - - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSpin::OnApply() -{ - CSuper::OnApply(); - - m_wCurr = SendMsg(UDM_GETPOS, 0, 0); - if (m_dbLink != nullptr) - SaveInt(m_wCurr); - - HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0); - if (hwndBuddy) { - wchar_t buf[100]; - _itow(m_wCurr, buf, 10); - ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf)); - } - - return true; -} - -void CCtrlSpin::OnReset() -{ - SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int16_t CCtrlSpin::GetPosition() -{ - return m_wCurr; -} - -void CCtrlSpin::SetPosition(int16_t wPos) -{ - SendMsg(UDM_SETPOS, 0, m_wCurr = wPos); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlSpin class
+
+CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) :
+ CCtrlData(dlg, ctrlId),
+ m_wMin(wMin),
+ m_wMax(wMax),
+ m_wCurr(0)
+{}
+
+BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh)
+{
+ if (pnmh->code == UDN_DELTAPOS) {
+ auto *pEvent = (NMUPDOWN *)pnmh;
+ m_wCurr = pEvent->iPos + pEvent->iDelta;
+
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool CCtrlSpin::OnApply()
+{
+ CSuper::OnApply();
+
+ m_wCurr = SendMsg(UDM_GETPOS, 0, 0);
+ if (m_dbLink != nullptr)
+ SaveInt(m_wCurr);
+
+ HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0);
+ if (hwndBuddy) {
+ wchar_t buf[100];
+ _itow(m_wCurr, buf, 10);
+ ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf));
+ }
+
+ return true;
+}
+
+void CCtrlSpin::OnReset()
+{
+ SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin));
+
+ if (m_dbLink != nullptr)
+ SetPosition(LoadInt());
+}
+
+int16_t CCtrlSpin::GetPosition()
+{
+ return m_wCurr;
+}
+
+void CCtrlSpin::SetPosition(int16_t wPos)
+{
+ SendMsg(UDM_SETPOS, 0, m_wCurr = wPos);
+}
diff --git a/src/mir_core/src/Linux/CCtrlTreeOpts.cpp b/src/mir_core/src/Linux/CCtrlTreeOpts.cpp index 13a6b79bd3..f0b1be1fb9 100644 --- a/src/mir_core/src/Linux/CCtrlTreeOpts.cpp +++ b/src/mir_core/src/Linux/CCtrlTreeOpts.cpp @@ -1,216 +1,216 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -enum { IMG_GROUP, IMG_CHECK, IMG_NOCHECK, IMG_GRPOPEN, IMG_GRPCLOSED }; - -CCtrlTreeOpts::CCtrlTreeOpts(CDlgBase* dlg, int ctrlId): - CCtrlTreeView(dlg, ctrlId), - m_options(5) -{ -} - -CCtrlTreeOpts::~CCtrlTreeOpts() -{ -} - -void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, CMOption<bool> &option) -{ - auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::CMOPTION); - p->m_option = &option; - m_options.insert(p, m_options.getCount()); -} - -void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, bool &option) -{ - auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::BOOL); - p->m_pBool = &option; - m_options.insert(p, m_options.getCount()); -} - -void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, uint32_t &option, uint32_t mask) -{ - auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::MASK); - p->m_pDword = &option; - p->m_mask = mask; - m_options.insert(p, m_options.getCount()); -} - -BOOL CCtrlTreeOpts::OnNotify(int idCtrl, NMHDR *pnmh) -{ - switch (pnmh->code) { - case TVN_KEYDOWN: - { - LPNMTVKEYDOWN lpnmtvkd = (LPNMTVKEYDOWN)pnmh; - HTREEITEM hti; - if ((lpnmtvkd->wVKey == VK_SPACE) && (hti = GetSelection())) - ProcessItemClick(hti); - } - break; - - case NM_CLICK: - TVHITTESTINFO htti; - htti.pt.x = (short)LOWORD(GetMessagePos()); - htti.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(pnmh->hwndFrom, &htti.pt); - if (HitTest(&htti)) - if (htti.flags & TVHT_ONITEMICON) - ProcessItemClick(htti.hItem); - break; - - case TVN_ITEMEXPANDED: - LPNMTREEVIEW lpnmtv = (LPNMTREEVIEW)pnmh; - TVITEM tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvi.hItem = lpnmtv->itemNew.hItem; - tvi.iImage = tvi.iSelectedImage = (lpnmtv->itemNew.state & TVIS_EXPANDED) ? IMG_GRPOPEN : IMG_GRPCLOSED; - SendMessage(pnmh->hwndFrom, TVM_SETITEM, 0, (LPARAM)&tvi); - break; - } - - return CSuper::OnNotify(idCtrl, pnmh); -} - -void CCtrlTreeOpts::OnInit() -{ - CSuper::OnInit(); - - SelectItem(nullptr); - DeleteAllItems(); - - HIMAGELIST hImgLst = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_COLOR32 | ILC_MASK, 5, 1); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_MIRANDA); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_TICK); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_NOTICK); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPOPEN); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPSHUT); - SetImageList(hImgLst, TVSIL_NORMAL); - - /* build options tree. based on code from IcoLib */ - for (auto &it : m_options) { - if (it->m_pwszSection) { - HTREEITEM hSection = FindNamedItem(nullptr, it->m_pwszSection); - if (!hSection) { - TVINSERTSTRUCT tvis = {}; - tvis.hParent = hSection; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvis.item.pszText = (LPWSTR)it->m_pwszSection; - tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED | TVIS_BOLD; - tvis.item.iImage = tvis.item.iSelectedImage = IMG_GRPOPEN; - hSection = InsertItem(&tvis); - } - - bool bValue; - switch (it->m_type) { - case COptionsItem::CMOPTION: - bValue = *it->m_option; - break; - case COptionsItem::BOOL: - bValue = *it->m_pBool; - break; - case COptionsItem::MASK: - bValue = (*it->m_pDword & it->m_mask) != 0; - break; - default: - continue; - } - - TVINSERTSTRUCT tvis = {}; - tvis.hParent = hSection; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvis.item.pszText = (LPWSTR)it->m_pwszName; - tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED; - tvis.item.lParam = m_options.indexOf(&it); - tvis.item.iImage = tvis.item.iSelectedImage = (bValue) ? IMG_CHECK : IMG_NOCHECK; - - it->m_hItem = InsertItem(&tvis); - } - } - - TranslateTree(); - ShowWindow(m_hwnd, SW_SHOW); - SelectItem(FindNamedItem(nullptr, nullptr)); -} - -void CCtrlTreeOpts::OnDestroy() -{ - ImageList_Destroy(GetImageList(TVSIL_NORMAL)); -} - -bool CCtrlTreeOpts::OnApply() -{ - CSuper::OnApply(); - - for (auto &it : m_options) { - TVITEMEX tvi; - GetItem(it->m_hItem, &tvi); - - bool bValue = (tvi.iImage == IMG_CHECK); - switch (it->m_type) { - case COptionsItem::CMOPTION: - *it->m_option = bValue; - break; - case COptionsItem::BOOL: - *it->m_pBool = bValue; - break; - case COptionsItem::MASK: - if (bValue) - *it->m_pDword |= it->m_mask; - else - *it->m_pDword &= ~it->m_mask; - break; - } - } - return true; -} - -void CCtrlTreeOpts::ProcessItemClick(HTREEITEM hti) -{ - TVITEMEX tvi; - GetItem(hti, &tvi); - switch (tvi.iImage) { - case IMG_GRPOPEN: - tvi.iImage = tvi.iSelectedImage = IMG_GRPCLOSED; - Expand(tvi.hItem, TVE_COLLAPSE); - break; - - case IMG_GRPCLOSED: - tvi.iImage = tvi.iSelectedImage = IMG_GRPOPEN; - Expand(tvi.hItem, TVE_EXPAND); - break; - - case IMG_CHECK: - tvi.iImage = tvi.iSelectedImage = IMG_NOCHECK; - NotifyChange(); - break; - - case IMG_NOCHECK: - tvi.iImage = tvi.iSelectedImage = IMG_CHECK; - NotifyChange(); - break; - } - - SetItem(&tvi); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+enum { IMG_GROUP, IMG_CHECK, IMG_NOCHECK, IMG_GRPOPEN, IMG_GRPCLOSED };
+
+CCtrlTreeOpts::CCtrlTreeOpts(CDlgBase* dlg, int ctrlId):
+ CCtrlTreeView(dlg, ctrlId),
+ m_options(5)
+{
+}
+
+CCtrlTreeOpts::~CCtrlTreeOpts()
+{
+}
+
+void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, CMOption<bool> &option)
+{
+ auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::CMOPTION);
+ p->m_option = &option;
+ m_options.insert(p, m_options.getCount());
+}
+
+void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, bool &option)
+{
+ auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::BOOL);
+ p->m_pBool = &option;
+ m_options.insert(p, m_options.getCount());
+}
+
+void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, uint32_t &option, uint32_t mask)
+{
+ auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::MASK);
+ p->m_pDword = &option;
+ p->m_mask = mask;
+ m_options.insert(p, m_options.getCount());
+}
+
+BOOL CCtrlTreeOpts::OnNotify(int idCtrl, NMHDR *pnmh)
+{
+ switch (pnmh->code) {
+ case TVN_KEYDOWN:
+ {
+ LPNMTVKEYDOWN lpnmtvkd = (LPNMTVKEYDOWN)pnmh;
+ HTREEITEM hti;
+ if ((lpnmtvkd->wVKey == VK_SPACE) && (hti = GetSelection()))
+ ProcessItemClick(hti);
+ }
+ break;
+
+ case NM_CLICK:
+ TVHITTESTINFO htti;
+ htti.pt.x = (short)LOWORD(GetMessagePos());
+ htti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(pnmh->hwndFrom, &htti.pt);
+ if (HitTest(&htti))
+ if (htti.flags & TVHT_ONITEMICON)
+ ProcessItemClick(htti.hItem);
+ break;
+
+ case TVN_ITEMEXPANDED:
+ LPNMTREEVIEW lpnmtv = (LPNMTREEVIEW)pnmh;
+ TVITEM tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvi.hItem = lpnmtv->itemNew.hItem;
+ tvi.iImage = tvi.iSelectedImage = (lpnmtv->itemNew.state & TVIS_EXPANDED) ? IMG_GRPOPEN : IMG_GRPCLOSED;
+ SendMessage(pnmh->hwndFrom, TVM_SETITEM, 0, (LPARAM)&tvi);
+ break;
+ }
+
+ return CSuper::OnNotify(idCtrl, pnmh);
+}
+
+void CCtrlTreeOpts::OnInit()
+{
+ CSuper::OnInit();
+
+ SelectItem(nullptr);
+ DeleteAllItems();
+
+ HIMAGELIST hImgLst = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_COLOR32 | ILC_MASK, 5, 1);
+ ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_MIRANDA);
+ ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_TICK);
+ ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_NOTICK);
+ ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPOPEN);
+ ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPSHUT);
+ SetImageList(hImgLst, TVSIL_NORMAL);
+
+ /* build options tree. based on code from IcoLib */
+ for (auto &it : m_options) {
+ if (it->m_pwszSection) {
+ HTREEITEM hSection = FindNamedItem(nullptr, it->m_pwszSection);
+ if (!hSection) {
+ TVINSERTSTRUCT tvis = {};
+ tvis.hParent = hSection;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.item.pszText = (LPWSTR)it->m_pwszSection;
+ tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED | TVIS_BOLD;
+ tvis.item.iImage = tvis.item.iSelectedImage = IMG_GRPOPEN;
+ hSection = InsertItem(&tvis);
+ }
+
+ bool bValue;
+ switch (it->m_type) {
+ case COptionsItem::CMOPTION:
+ bValue = *it->m_option;
+ break;
+ case COptionsItem::BOOL:
+ bValue = *it->m_pBool;
+ break;
+ case COptionsItem::MASK:
+ bValue = (*it->m_pDword & it->m_mask) != 0;
+ break;
+ default:
+ continue;
+ }
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.hParent = hSection;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.item.pszText = (LPWSTR)it->m_pwszName;
+ tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED;
+ tvis.item.lParam = m_options.indexOf(&it);
+ tvis.item.iImage = tvis.item.iSelectedImage = (bValue) ? IMG_CHECK : IMG_NOCHECK;
+
+ it->m_hItem = InsertItem(&tvis);
+ }
+ }
+
+ TranslateTree();
+ ShowWindow(m_hwnd, SW_SHOW);
+ SelectItem(FindNamedItem(nullptr, nullptr));
+}
+
+void CCtrlTreeOpts::OnDestroy()
+{
+ ImageList_Destroy(GetImageList(TVSIL_NORMAL));
+}
+
+bool CCtrlTreeOpts::OnApply()
+{
+ CSuper::OnApply();
+
+ for (auto &it : m_options) {
+ TVITEMEX tvi;
+ GetItem(it->m_hItem, &tvi);
+
+ bool bValue = (tvi.iImage == IMG_CHECK);
+ switch (it->m_type) {
+ case COptionsItem::CMOPTION:
+ *it->m_option = bValue;
+ break;
+ case COptionsItem::BOOL:
+ *it->m_pBool = bValue;
+ break;
+ case COptionsItem::MASK:
+ if (bValue)
+ *it->m_pDword |= it->m_mask;
+ else
+ *it->m_pDword &= ~it->m_mask;
+ break;
+ }
+ }
+ return true;
+}
+
+void CCtrlTreeOpts::ProcessItemClick(HTREEITEM hti)
+{
+ TVITEMEX tvi;
+ GetItem(hti, &tvi);
+ switch (tvi.iImage) {
+ case IMG_GRPOPEN:
+ tvi.iImage = tvi.iSelectedImage = IMG_GRPCLOSED;
+ Expand(tvi.hItem, TVE_COLLAPSE);
+ break;
+
+ case IMG_GRPCLOSED:
+ tvi.iImage = tvi.iSelectedImage = IMG_GRPOPEN;
+ Expand(tvi.hItem, TVE_EXPAND);
+ break;
+
+ case IMG_CHECK:
+ tvi.iImage = tvi.iSelectedImage = IMG_NOCHECK;
+ NotifyChange();
+ break;
+
+ case IMG_NOCHECK:
+ tvi.iImage = tvi.iSelectedImage = IMG_CHECK;
+ NotifyChange();
+ break;
+ }
+
+ SetItem(&tvi);
+}
diff --git a/src/mir_core/src/Linux/CCtrlTreeView.cpp b/src/mir_core/src/Linux/CCtrlTreeView.cpp index 390f1618fc..5d2174fc16 100644 --- a/src/mir_core/src/Linux/CCtrlTreeView.cpp +++ b/src/mir_core/src/Linux/CCtrlTreeView.cpp @@ -1,817 +1,817 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -int ImageList_AddIcon_IconLibLoaded(HIMAGELIST hIml, int iconId) -{ - HICON hIcon = Skin_LoadIcon(iconId); - int res = ImageList_AddIcon(hIml, hIcon); - IcoLib_ReleaseIcon(hIcon); - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlTreeView - -CCtrlTreeView::CCtrlTreeView(CDlgBase *dlg, int ctrlId) : - CCtrlBase(dlg, ctrlId), - m_dwFlags(0), - m_hDragItem(nullptr) -{} - -void CCtrlTreeView::SetFlags(uint32_t dwFlags) -{ - if (dwFlags & MTREE_CHECKBOX) - m_bCheckBox = true; - - if (dwFlags & MTREE_MULTISELECT) - m_bMultiSelect = true; - - if (dwFlags & MTREE_DND) { - m_bDndEnabled = true; - m_bDragging = false; - m_hDragItem = nullptr; - } -} - -void CCtrlTreeView::OnInit() -{ - CSuper::OnInit(); - - Subclass(); - - if (m_bCheckBox) { - HIMAGELIST himlCheckBoxes = ::ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 2); - ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_NOTICK); - ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_TICK); - SetImageList(himlCheckBoxes, TVSIL_NORMAL); - } -} - -void CCtrlTreeView::OnDestroy() -{ - if (m_bCheckBox) - ::ImageList_Destroy(GetImageList(TVSIL_NORMAL)); - - CSuper::OnDestroy(); -} - -HTREEITEM CCtrlTreeView::MoveItemAbove(HTREEITEM hItem, HTREEITEM hInsertAfter, HTREEITEM hParent) -{ - if (hItem == nullptr || hInsertAfter == nullptr) - return nullptr; - - if (hItem == hInsertAfter) - return hItem; - - wchar_t name[128]; - TVINSERTSTRUCT tvis = {}; - tvis.itemex.mask = (UINT)-1; - tvis.itemex.pszText = name; - tvis.itemex.cchTextMax = _countof(name); - tvis.itemex.hItem = hItem; - if (!GetItem(&tvis.itemex)) - return nullptr; - - OBJLIST<TVINSERTSTRUCT> arChildren(1); - for (HTREEITEM p = GetChild(hItem); p; p = GetNextSibling(p)) { - wchar_t buf[128]; - TVINSERTSTRUCT tvis2 = {}; - tvis2.itemex.mask = (UINT)-1; - tvis2.itemex.pszText = buf; - tvis2.itemex.cchTextMax = _countof(buf); - tvis2.itemex.hItem = p; - if (GetItem(&tvis2.itemex)) { - tvis2.itemex.pszText = mir_wstrdup(tvis2.itemex.pszText); - arChildren.insert(new TVINSERTSTRUCT(tvis2)); - - tvis2.itemex.lParam = 0; - SetItem(&tvis2.itemex); - } - } - - // the pointed lParam will be freed inside TVN_DELETEITEM - // so lets substitute it with 0 - LPARAM saveOldData = tvis.itemex.lParam; - tvis.itemex.lParam = 0; - SetItem(&tvis.itemex); - - // now current item contain lParam = 0 we can delete it. the memory will be kept. - DeleteItem(hItem); - - for (auto &it : arChildren) - DeleteItem(it->itemex.hItem); - - tvis.itemex.stateMask = tvis.itemex.state; - tvis.itemex.lParam = saveOldData; - tvis.hParent = hParent; - tvis.hInsertAfter = hInsertAfter; - auto hNewItem = InsertItem(&tvis); - - hInsertAfter = nullptr; - for (auto &it : arChildren) { - it->hParent = hNewItem; - it->hInsertAfter = hInsertAfter; - hInsertAfter = InsertItem(it); - - mir_free(it->itemex.pszText); - } - - return hNewItem; -} - -LRESULT CCtrlTreeView::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - TVHITTESTINFO hti; - - switch (msg) { - case WM_MOUSEMOVE: - if (m_bDragging) { - hti.pt.x = (short)LOWORD(lParam); - hti.pt.y = (short)HIWORD(lParam); - HitTest(&hti); - if (hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT)) { - HTREEITEM it = hti.hItem; - hti.pt.y -= GetItemHeight() / 2; - HitTest(&hti); - if (!(hti.flags & TVHT_ABOVE)) - SetInsertMark(hti.hItem, 1); - else - SetInsertMark(it, 0); - } - else { - if (hti.flags & TVHT_ABOVE) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0); - if (hti.flags & TVHT_BELOW) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0); - SetInsertMark(nullptr, 0); - } - } - break; - - case WM_LBUTTONUP: - if (m_bDragging) { - SetInsertMark(nullptr, 0); - m_bDragging = false; - ReleaseCapture(); - - hti.pt.x = (short)LOWORD(lParam); - hti.pt.y = (short)HIWORD(lParam) - GetItemHeight() / 2; - HitTest(&hti); - if (m_hDragItem == hti.hItem) - break; - - if (hti.flags & TVHT_ABOVE) - hti.hItem = TVI_FIRST; - else if (hti.flags & TVHT_BELOW) - hti.hItem = TVI_LAST; - - HTREEITEM insertAfter = hti.hItem, hParent; - if (insertAfter != TVI_FIRST) { - hParent = GetParent(insertAfter); - if (GetChild(insertAfter) != nullptr) { - hParent = insertAfter; - insertAfter = TVI_FIRST; - } - } - else hParent = nullptr; - - HTREEITEM FirstItem = nullptr; - if (m_bMultiSelect) { - LIST<_TREEITEM> arItems(10); - GetSelected(arItems); - - // Proceed moving - for (auto &it : arItems) { - if (!insertAfter) - break; - if (GetParent(it) != hParent) // prevent subitems from being inserted at the same level - continue; - - insertAfter = MoveItemAbove(it, insertAfter, hParent); - if (it == arItems[0]) - FirstItem = insertAfter; - } - } - else FirstItem = MoveItemAbove(m_hDragItem, insertAfter, hParent); - if (FirstItem) - SelectItem(FirstItem); - - NotifyChange(); - } - break; - - case WM_LBUTTONDOWN: - if (!m_bMultiSelect) - break; - - hti.pt.x = (short)LOWORD(lParam); - hti.pt.y = (short)HIWORD(lParam); - if (!TreeView_HitTest(m_hwnd, &hti)) { - UnselectAll(); - break; - } - - if (!m_bDndEnabled) - if (!(wParam & (MK_CONTROL | MK_SHIFT)) || !(hti.flags & (TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMRIGHT))) { - UnselectAll(); - TreeView_SelectItem(m_hwnd, hti.hItem); - break; - } - - if (wParam & MK_CONTROL) { - LIST<_TREEITEM> selected(1); - GetSelected(selected); - - // Check if have to deselect it - for (int i = 0; i < selected.getCount(); i++) { - if (selected[i] == hti.hItem) { - // Deselect it - UnselectAll(); - selected.remove(i); - - if (i > 0) - hti.hItem = selected[0]; - else if (i < selected.getCount()) - hti.hItem = selected[i]; - else - hti.hItem = nullptr; - break; - } - } - - TreeView_SelectItem(m_hwnd, hti.hItem); - Select(selected); - } - else if (wParam & MK_SHIFT) { - HTREEITEM hItem = TreeView_GetSelection(m_hwnd); - if (hItem == nullptr) - break; - - LIST<_TREEITEM> selected(1); - GetSelected(selected); - - TreeView_SelectItem(m_hwnd, hti.hItem); - Select(selected); - SelectRange(hItem, hti.hItem); - } - break; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} - -BOOL CCtrlTreeView::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, pnmh }; - - switch (pnmh->code) { - case NM_RCLICK: OnRightClick(&evt); return TRUE; - case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; - case TVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; - case TVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; - case TVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; - case TVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; - case TVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; - case TVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; - case TVN_ITEMEXPANDED: OnItemExpanded(&evt); return TRUE; - case TVN_ITEMEXPANDING: OnItemExpanding(&evt); return TRUE; - case TVN_SELCHANGED: OnSelChanged(&evt); return TRUE; - case TVN_SELCHANGING: OnSelChanging(&evt); return TRUE; - case TVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; - case TVN_SINGLEEXPAND: OnSingleExpand(&evt); return TRUE; - - case TVN_BEGINDRAG: - OnBeginDrag(&evt); - - // user-defined can clear the event code to disable dragging - if (m_bDndEnabled && pnmh->code) { - ::SetCapture(m_hwnd); - m_bDragging = true; - m_hDragItem = evt.nmtv->itemNew.hItem; - SelectItem(m_hDragItem); - } - return TRUE; - - case TVN_KEYDOWN: - if (evt.nmtvkey->wVKey == VK_SPACE) { - evt.hItem = GetSelection(); - if (m_bCheckBox) - InvertCheck(evt.hItem); - OnItemChanged(&evt); - NotifyChange(); - } - - OnKeyDown(&evt); - return TRUE; - } - - if (pnmh->code == NM_CLICK) { - TVHITTESTINFO hti; - hti.pt.x = (short)LOWORD(GetMessagePos()); - hti.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(pnmh->hwndFrom, &hti.pt); - if (HitTest(&hti)) { - if (m_bCheckBox && (hti.flags & TVHT_ONITEMICON) || !m_bCheckBox && (hti.flags & TVHT_ONITEMSTATEICON)) { - if (m_bCheckBox) - InvertCheck(hti.hItem); - else - SelectItem(hti.hItem); - - evt.hItem = hti.hItem; - OnItemChanged(&evt); - NotifyChange(); - } - } - } - - return FALSE; -} - -void CCtrlTreeView::InvertCheck(HTREEITEM hItem) -{ - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_STATEEX; - tvi.hItem = hItem; - if (!GetItem(&tvi)) - return; - - if (IsWinVerVistaPlus() && (tvi.uStateEx & TVIS_EX_DISABLED)) - return; - - tvi.iImage = tvi.iSelectedImage = !tvi.iImage; - SetItem(&tvi); - - SelectItem(hItem); -} - -void CCtrlTreeView::TranslateItem(HTREEITEM hItem) -{ - TVITEMEX tvi; - wchar_t buf[128]; - GetItem(hItem, &tvi, buf, _countof(buf)); - tvi.pszText = TranslateW_LP(tvi.pszText); - SetItem(&tvi); -} - -void CCtrlTreeView::TranslateTree() -{ - HTREEITEM hItem = GetRoot(); - while (hItem) { - TranslateItem(hItem); - - HTREEITEM hItemTmp = nullptr; - if (hItemTmp = GetChild(hItem)) - hItem = hItemTmp; - else if (hItemTmp = GetNextSibling(hItem)) - hItem = hItemTmp; - else { - while (true) { - if (!(hItem = GetParent(hItem))) - break; - if (hItemTmp = GetNextSibling(hItem)) { - hItem = hItemTmp; - break; - } - } - } - } -} - -HTREEITEM CCtrlTreeView::FindNamedItem(HTREEITEM hItem, const wchar_t *name) -{ - TVITEMEX tvi = { 0 }; - wchar_t str[MAX_PATH]; - - if (hItem) - tvi.hItem = GetChild(hItem); - else - tvi.hItem = GetRoot(); - - if (!name) - return tvi.hItem; - - tvi.mask = TVIF_TEXT; - tvi.pszText = str; - tvi.cchTextMax = _countof(str); - - while (tvi.hItem) { - GetItem(&tvi); - - if (!mir_wstrcmp(tvi.pszText, name)) - return tvi.hItem; - - tvi.hItem = GetNextSibling(tvi.hItem); - } - return nullptr; -} - -void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi) const -{ - memset(tvi, 0, sizeof(*tvi)); - tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE; - tvi->hItem = hItem; - GetItem(tvi); -} - -void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi, wchar_t *szText, int iTextLength) const -{ - memset(tvi, 0, sizeof(*tvi)); - tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_TEXT; - tvi->hItem = hItem; - tvi->pszText = szText; - tvi->cchTextMax = iTextLength; - GetItem(tvi); -} - -bool CCtrlTreeView::IsSelected(HTREEITEM hItem) -{ - return (TVIS_SELECTED & TreeView_GetItemState(m_hwnd, hItem, TVIS_SELECTED)) == TVIS_SELECTED; -} - -void CCtrlTreeView::Select(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, TVIS_SELECTED, TVIS_SELECTED); -} - -void CCtrlTreeView::Unselect(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_SELECTED); -} - -void CCtrlTreeView::DropHilite(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, TVIS_DROPHILITED, TVIS_DROPHILITED); -} - -void CCtrlTreeView::DropUnhilite(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_DROPHILITED); -} - -void CCtrlTreeView::SelectAll() -{ - TreeView_SelectItem(m_hwnd, nullptr); - - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - Select(hItem); - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -void CCtrlTreeView::UnselectAll() -{ - TreeView_SelectItem(m_hwnd, nullptr); - - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - Unselect(hItem); - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -void CCtrlTreeView::SelectRange(HTREEITEM hStart, HTREEITEM hEnd) -{ - int start = 0, end = 0, i = 0; - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - if (hItem == hStart) - start = i; - if (hItem == hEnd) - end = i; - - i++; - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } - - if (end < start) { - int tmp = start; - start = end; - end = tmp; - } - - i = 0; - hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - if (i >= start) - Select(hItem); - if (i == end) - break; - - i++; - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -int CCtrlTreeView::GetNumSelected() -{ - int ret = 0; - for (HTREEITEM hItem = TreeView_GetRoot(m_hwnd); hItem; hItem = TreeView_GetNextSibling(m_hwnd, hItem)) - if (IsSelected(hItem)) - ret++; - - return ret; -} - -void CCtrlTreeView::GetSelected(LIST<_TREEITEM> &selected) -{ - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - if (IsSelected(hItem)) - selected.insert(hItem); - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -void CCtrlTreeView::Select(LIST<_TREEITEM> &selected) -{ - for (auto &it : selected) - if (it != nullptr) - Select(it); -} - -void CCtrlTreeView::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - - // position is empty, let's fill it using selection - if (pos.pt.x == 0 && pos.pt.y == 0) { - HTREEITEM hItem = GetSelection(); - if (hItem != nullptr) { - pos.pCtrl = this; - pos.hItem = hItem; - - RECT rc; - GetItemRect(hItem, &rc, TRUE); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - // position is present, let's calculate current item - else { - TVHITTESTINFO hti; - hti.pt = pos.pt; - ScreenToClient(m_hwnd, &hti.pt); - if (HitTest(&hti) && (hti.flags & TVHT_ONITEM)) { - pos.hItem = hti.hItem; - return; - } - } - - CSuper::GetCaretPos(pos); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -HIMAGELIST CCtrlTreeView::CreateDragImage(HTREEITEM hItem) -{ return TreeView_CreateDragImage(m_hwnd, hItem); -} - -void CCtrlTreeView::DeleteAllItems() -{ TreeView_DeleteAllItems(m_hwnd); -} - -void CCtrlTreeView::DeleteItem(HTREEITEM hItem) -{ TreeView_DeleteItem(m_hwnd, hItem); -} - -HWND CCtrlTreeView::EditLabel(HTREEITEM hItem) -{ return TreeView_EditLabel(m_hwnd, hItem); -} - -void CCtrlTreeView::EndEditLabelNow(BOOL cancel) -{ TreeView_EndEditLabelNow(m_hwnd, cancel); -} - -void CCtrlTreeView::EnsureVisible(HTREEITEM hItem) -{ TreeView_EnsureVisible(m_hwnd, hItem); -} - -void CCtrlTreeView::Expand(HTREEITEM hItem, uint32_t flag) -{ TreeView_Expand(m_hwnd, hItem, flag); -} - -COLORREF CCtrlTreeView::GetBkColor() const -{ return TreeView_GetBkColor(m_hwnd); -} - -uint32_t CCtrlTreeView::GetCheckState(HTREEITEM hItem) const -{ return TreeView_GetCheckState(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetChild(HTREEITEM hItem) const -{ return TreeView_GetChild(m_hwnd, hItem); -} - -int CCtrlTreeView::GetCount() const -{ return TreeView_GetCount(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetDropHilight() const -{ return TreeView_GetDropHilight(m_hwnd); -} - -HWND CCtrlTreeView::GetEditControl() const -{ return TreeView_GetEditControl(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetFirstVisible() const -{ return TreeView_GetFirstVisible(m_hwnd); -} - -HIMAGELIST CCtrlTreeView::GetImageList(int iImage) const -{ return TreeView_GetImageList(m_hwnd, iImage); -} - -int CCtrlTreeView::GetIndent() const -{ return TreeView_GetIndent(m_hwnd); -} - -COLORREF CCtrlTreeView::GetInsertMarkColor() const -{ return TreeView_GetInsertMarkColor(m_hwnd); -} - -bool CCtrlTreeView::GetItem(TVITEMEX *tvi) const -{ return TreeView_GetItem(m_hwnd, tvi) == TRUE; -} - -int CCtrlTreeView::GetItemHeight() const -{ return TreeView_GetItemHeight(m_hwnd); -} - -void CCtrlTreeView::GetItemRect(HTREEITEM hItem, RECT *rcItem, BOOL fItemRect) const -{ TreeView_GetItemRect(m_hwnd, hItem, rcItem, fItemRect); -} - -uint32_t CCtrlTreeView::GetItemState(HTREEITEM hItem, uint32_t stateMask) const -{ return TreeView_GetItemState(m_hwnd, hItem, stateMask); -} - -HTREEITEM CCtrlTreeView::GetLastVisible() const -{ return TreeView_GetLastVisible(m_hwnd); -} - -COLORREF CCtrlTreeView::GetLineColor() const -{ return TreeView_GetLineColor(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetNextItem(HTREEITEM hItem, uint32_t flag) const -{ return TreeView_GetNextItem(m_hwnd, hItem, flag); -} - -HTREEITEM CCtrlTreeView::GetNextSibling(HTREEITEM hItem) const -{ return TreeView_GetNextSibling(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetNextVisible(HTREEITEM hItem) const -{ return TreeView_GetNextVisible(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetParent(HTREEITEM hItem) const -{ return TreeView_GetParent(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetPrevSibling(HTREEITEM hItem) const -{ return TreeView_GetPrevSibling(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetPrevVisible(HTREEITEM hItem) const -{ return TreeView_GetPrevVisible(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetRoot() const -{ return TreeView_GetRoot(m_hwnd); -} - -uint32_t CCtrlTreeView::GetScrollTime() const -{ return TreeView_GetScrollTime(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetSelection() const -{ return TreeView_GetSelection(m_hwnd); -} - -COLORREF CCtrlTreeView::GetTextColor() const -{ return TreeView_GetTextColor(m_hwnd); -} - -HWND CCtrlTreeView::GetToolTips() const -{ return TreeView_GetToolTips(m_hwnd); -} - -BOOL CCtrlTreeView::GetUnicodeFormat() const -{ return TreeView_GetUnicodeFormat(m_hwnd); -} - -unsigned CCtrlTreeView::GetVisibleCount() const -{ return TreeView_GetVisibleCount(m_hwnd); -} - -HTREEITEM CCtrlTreeView::HitTest(TVHITTESTINFO *hti) const -{ return TreeView_HitTest(m_hwnd, hti); -} - -HTREEITEM CCtrlTreeView::InsertItem(TVINSERTSTRUCT *tvis) -{ return TreeView_InsertItem(m_hwnd, tvis); -} - -void CCtrlTreeView::Select(HTREEITEM hItem, uint32_t flag) -{ TreeView_Select(m_hwnd, hItem, flag); -} - -void CCtrlTreeView::SelectDropTarget(HTREEITEM hItem) -{ TreeView_SelectDropTarget(m_hwnd, hItem); -} - -void CCtrlTreeView::SelectItem(HTREEITEM hItem) -{ TreeView_SelectItem(m_hwnd, hItem); -} - -void CCtrlTreeView::SelectSetFirstVisible(HTREEITEM hItem) -{ TreeView_SelectSetFirstVisible(m_hwnd, hItem); -} - -COLORREF CCtrlTreeView::SetBkColor(COLORREF clBack) -{ return TreeView_SetBkColor(m_hwnd, clBack); -} - -void CCtrlTreeView::SetCheckState(HTREEITEM hItem, uint32_t state) -{ TreeView_SetCheckState(m_hwnd, hItem, state); -} - -void CCtrlTreeView::SetImageList(HIMAGELIST hIml, int iImage) -{ TreeView_SetImageList(m_hwnd, hIml, iImage); -} - -void CCtrlTreeView::SetIndent(int iIndent) -{ TreeView_SetIndent(m_hwnd, iIndent); -} - -void CCtrlTreeView::SetInsertMark(HTREEITEM hItem, BOOL fAfter) -{ TreeView_SetInsertMark(m_hwnd, hItem, fAfter); -} - -COLORREF CCtrlTreeView::SetInsertMarkColor(COLORREF clMark) -{ return TreeView_SetInsertMarkColor(m_hwnd, clMark); -} - -void CCtrlTreeView::SetItem(TVITEMEX *tvi) -{ TreeView_SetItem(m_hwnd, tvi); -} - -void CCtrlTreeView::SetItemHeight(short cyItem) -{ TreeView_SetItemHeight(m_hwnd, cyItem); -} - -void CCtrlTreeView::SetItemState(HTREEITEM hItem, uint32_t state, uint32_t stateMask) -{ TreeView_SetItemState(m_hwnd, hItem, state, stateMask); -} - -COLORREF CCtrlTreeView::SetLineColor(COLORREF clLine) -{ return TreeView_SetLineColor(m_hwnd, clLine); -} - -void CCtrlTreeView::SetScrollTime(UINT uMaxScrollTime) -{ TreeView_SetScrollTime(m_hwnd, uMaxScrollTime); -} - -COLORREF CCtrlTreeView::SetTextColor(COLORREF clText) -{ return TreeView_SetTextColor(m_hwnd, clText); -} - -HWND CCtrlTreeView::SetToolTips(HWND hwndToolTips) -{ return TreeView_SetToolTips(m_hwnd, hwndToolTips); -} - -BOOL CCtrlTreeView::SetUnicodeFormat(BOOL fUnicode) -{ return TreeView_SetUnicodeFormat(m_hwnd, fUnicode); -} - -void CCtrlTreeView::SortChildren(HTREEITEM hItem, BOOL fRecurse) -{ TreeView_SortChildren(m_hwnd, hItem, fRecurse); -} - -void CCtrlTreeView::SortChildrenCB(TVSORTCB *cb, BOOL fRecurse) -{ TreeView_SortChildrenCB(m_hwnd, cb, fRecurse); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+int ImageList_AddIcon_IconLibLoaded(HIMAGELIST hIml, int iconId)
+{
+ HICON hIcon = Skin_LoadIcon(iconId);
+ int res = ImageList_AddIcon(hIml, hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlTreeView
+
+CCtrlTreeView::CCtrlTreeView(CDlgBase *dlg, int ctrlId) :
+ CCtrlBase(dlg, ctrlId),
+ m_dwFlags(0),
+ m_hDragItem(nullptr)
+{}
+
+void CCtrlTreeView::SetFlags(uint32_t dwFlags)
+{
+ if (dwFlags & MTREE_CHECKBOX)
+ m_bCheckBox = true;
+
+ if (dwFlags & MTREE_MULTISELECT)
+ m_bMultiSelect = true;
+
+ if (dwFlags & MTREE_DND) {
+ m_bDndEnabled = true;
+ m_bDragging = false;
+ m_hDragItem = nullptr;
+ }
+}
+
+void CCtrlTreeView::OnInit()
+{
+ CSuper::OnInit();
+
+ Subclass();
+
+ if (m_bCheckBox) {
+ HIMAGELIST himlCheckBoxes = ::ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 2);
+ ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_NOTICK);
+ ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_TICK);
+ SetImageList(himlCheckBoxes, TVSIL_NORMAL);
+ }
+}
+
+void CCtrlTreeView::OnDestroy()
+{
+ if (m_bCheckBox)
+ ::ImageList_Destroy(GetImageList(TVSIL_NORMAL));
+
+ CSuper::OnDestroy();
+}
+
+HTREEITEM CCtrlTreeView::MoveItemAbove(HTREEITEM hItem, HTREEITEM hInsertAfter, HTREEITEM hParent)
+{
+ if (hItem == nullptr || hInsertAfter == nullptr)
+ return nullptr;
+
+ if (hItem == hInsertAfter)
+ return hItem;
+
+ wchar_t name[128];
+ TVINSERTSTRUCT tvis = {};
+ tvis.itemex.mask = (UINT)-1;
+ tvis.itemex.pszText = name;
+ tvis.itemex.cchTextMax = _countof(name);
+ tvis.itemex.hItem = hItem;
+ if (!GetItem(&tvis.itemex))
+ return nullptr;
+
+ OBJLIST<TVINSERTSTRUCT> arChildren(1);
+ for (HTREEITEM p = GetChild(hItem); p; p = GetNextSibling(p)) {
+ wchar_t buf[128];
+ TVINSERTSTRUCT tvis2 = {};
+ tvis2.itemex.mask = (UINT)-1;
+ tvis2.itemex.pszText = buf;
+ tvis2.itemex.cchTextMax = _countof(buf);
+ tvis2.itemex.hItem = p;
+ if (GetItem(&tvis2.itemex)) {
+ tvis2.itemex.pszText = mir_wstrdup(tvis2.itemex.pszText);
+ arChildren.insert(new TVINSERTSTRUCT(tvis2));
+
+ tvis2.itemex.lParam = 0;
+ SetItem(&tvis2.itemex);
+ }
+ }
+
+ // the pointed lParam will be freed inside TVN_DELETEITEM
+ // so lets substitute it with 0
+ LPARAM saveOldData = tvis.itemex.lParam;
+ tvis.itemex.lParam = 0;
+ SetItem(&tvis.itemex);
+
+ // now current item contain lParam = 0 we can delete it. the memory will be kept.
+ DeleteItem(hItem);
+
+ for (auto &it : arChildren)
+ DeleteItem(it->itemex.hItem);
+
+ tvis.itemex.stateMask = tvis.itemex.state;
+ tvis.itemex.lParam = saveOldData;
+ tvis.hParent = hParent;
+ tvis.hInsertAfter = hInsertAfter;
+ auto hNewItem = InsertItem(&tvis);
+
+ hInsertAfter = nullptr;
+ for (auto &it : arChildren) {
+ it->hParent = hNewItem;
+ it->hInsertAfter = hInsertAfter;
+ hInsertAfter = InsertItem(it);
+
+ mir_free(it->itemex.pszText);
+ }
+
+ return hNewItem;
+}
+
+LRESULT CCtrlTreeView::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ TVHITTESTINFO hti;
+
+ switch (msg) {
+ case WM_MOUSEMOVE:
+ if (m_bDragging) {
+ hti.pt.x = (short)LOWORD(lParam);
+ hti.pt.y = (short)HIWORD(lParam);
+ HitTest(&hti);
+ if (hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT)) {
+ HTREEITEM it = hti.hItem;
+ hti.pt.y -= GetItemHeight() / 2;
+ HitTest(&hti);
+ if (!(hti.flags & TVHT_ABOVE))
+ SetInsertMark(hti.hItem, 1);
+ else
+ SetInsertMark(it, 0);
+ }
+ else {
+ if (hti.flags & TVHT_ABOVE) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0);
+ if (hti.flags & TVHT_BELOW) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0);
+ SetInsertMark(nullptr, 0);
+ }
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ if (m_bDragging) {
+ SetInsertMark(nullptr, 0);
+ m_bDragging = false;
+ ReleaseCapture();
+
+ hti.pt.x = (short)LOWORD(lParam);
+ hti.pt.y = (short)HIWORD(lParam) - GetItemHeight() / 2;
+ HitTest(&hti);
+ if (m_hDragItem == hti.hItem)
+ break;
+
+ if (hti.flags & TVHT_ABOVE)
+ hti.hItem = TVI_FIRST;
+ else if (hti.flags & TVHT_BELOW)
+ hti.hItem = TVI_LAST;
+
+ HTREEITEM insertAfter = hti.hItem, hParent;
+ if (insertAfter != TVI_FIRST) {
+ hParent = GetParent(insertAfter);
+ if (GetChild(insertAfter) != nullptr) {
+ hParent = insertAfter;
+ insertAfter = TVI_FIRST;
+ }
+ }
+ else hParent = nullptr;
+
+ HTREEITEM FirstItem = nullptr;
+ if (m_bMultiSelect) {
+ LIST<_TREEITEM> arItems(10);
+ GetSelected(arItems);
+
+ // Proceed moving
+ for (auto &it : arItems) {
+ if (!insertAfter)
+ break;
+ if (GetParent(it) != hParent) // prevent subitems from being inserted at the same level
+ continue;
+
+ insertAfter = MoveItemAbove(it, insertAfter, hParent);
+ if (it == arItems[0])
+ FirstItem = insertAfter;
+ }
+ }
+ else FirstItem = MoveItemAbove(m_hDragItem, insertAfter, hParent);
+ if (FirstItem)
+ SelectItem(FirstItem);
+
+ NotifyChange();
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ if (!m_bMultiSelect)
+ break;
+
+ hti.pt.x = (short)LOWORD(lParam);
+ hti.pt.y = (short)HIWORD(lParam);
+ if (!TreeView_HitTest(m_hwnd, &hti)) {
+ UnselectAll();
+ break;
+ }
+
+ if (!m_bDndEnabled)
+ if (!(wParam & (MK_CONTROL | MK_SHIFT)) || !(hti.flags & (TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMRIGHT))) {
+ UnselectAll();
+ TreeView_SelectItem(m_hwnd, hti.hItem);
+ break;
+ }
+
+ if (wParam & MK_CONTROL) {
+ LIST<_TREEITEM> selected(1);
+ GetSelected(selected);
+
+ // Check if have to deselect it
+ for (int i = 0; i < selected.getCount(); i++) {
+ if (selected[i] == hti.hItem) {
+ // Deselect it
+ UnselectAll();
+ selected.remove(i);
+
+ if (i > 0)
+ hti.hItem = selected[0];
+ else if (i < selected.getCount())
+ hti.hItem = selected[i];
+ else
+ hti.hItem = nullptr;
+ break;
+ }
+ }
+
+ TreeView_SelectItem(m_hwnd, hti.hItem);
+ Select(selected);
+ }
+ else if (wParam & MK_SHIFT) {
+ HTREEITEM hItem = TreeView_GetSelection(m_hwnd);
+ if (hItem == nullptr)
+ break;
+
+ LIST<_TREEITEM> selected(1);
+ GetSelected(selected);
+
+ TreeView_SelectItem(m_hwnd, hti.hItem);
+ Select(selected);
+ SelectRange(hItem, hti.hItem);
+ }
+ break;
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
+
+BOOL CCtrlTreeView::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, pnmh };
+
+ switch (pnmh->code) {
+ case NM_RCLICK: OnRightClick(&evt); return TRUE;
+ case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE;
+ case TVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE;
+ case TVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE;
+ case TVN_DELETEITEM: OnDeleteItem(&evt); return TRUE;
+ case TVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE;
+ case TVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE;
+ case TVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE;
+ case TVN_ITEMEXPANDED: OnItemExpanded(&evt); return TRUE;
+ case TVN_ITEMEXPANDING: OnItemExpanding(&evt); return TRUE;
+ case TVN_SELCHANGED: OnSelChanged(&evt); return TRUE;
+ case TVN_SELCHANGING: OnSelChanging(&evt); return TRUE;
+ case TVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE;
+ case TVN_SINGLEEXPAND: OnSingleExpand(&evt); return TRUE;
+
+ case TVN_BEGINDRAG:
+ OnBeginDrag(&evt);
+
+ // user-defined can clear the event code to disable dragging
+ if (m_bDndEnabled && pnmh->code) {
+ ::SetCapture(m_hwnd);
+ m_bDragging = true;
+ m_hDragItem = evt.nmtv->itemNew.hItem;
+ SelectItem(m_hDragItem);
+ }
+ return TRUE;
+
+ case TVN_KEYDOWN:
+ if (evt.nmtvkey->wVKey == VK_SPACE) {
+ evt.hItem = GetSelection();
+ if (m_bCheckBox)
+ InvertCheck(evt.hItem);
+ OnItemChanged(&evt);
+ NotifyChange();
+ }
+
+ OnKeyDown(&evt);
+ return TRUE;
+ }
+
+ if (pnmh->code == NM_CLICK) {
+ TVHITTESTINFO hti;
+ hti.pt.x = (short)LOWORD(GetMessagePos());
+ hti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(pnmh->hwndFrom, &hti.pt);
+ if (HitTest(&hti)) {
+ if (m_bCheckBox && (hti.flags & TVHT_ONITEMICON) || !m_bCheckBox && (hti.flags & TVHT_ONITEMSTATEICON)) {
+ if (m_bCheckBox)
+ InvertCheck(hti.hItem);
+ else
+ SelectItem(hti.hItem);
+
+ evt.hItem = hti.hItem;
+ OnItemChanged(&evt);
+ NotifyChange();
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+void CCtrlTreeView::InvertCheck(HTREEITEM hItem)
+{
+ TVITEMEX tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_STATEEX;
+ tvi.hItem = hItem;
+ if (!GetItem(&tvi))
+ return;
+
+ if (IsWinVerVistaPlus() && (tvi.uStateEx & TVIS_EX_DISABLED))
+ return;
+
+ tvi.iImage = tvi.iSelectedImage = !tvi.iImage;
+ SetItem(&tvi);
+
+ SelectItem(hItem);
+}
+
+void CCtrlTreeView::TranslateItem(HTREEITEM hItem)
+{
+ TVITEMEX tvi;
+ wchar_t buf[128];
+ GetItem(hItem, &tvi, buf, _countof(buf));
+ tvi.pszText = TranslateW_LP(tvi.pszText);
+ SetItem(&tvi);
+}
+
+void CCtrlTreeView::TranslateTree()
+{
+ HTREEITEM hItem = GetRoot();
+ while (hItem) {
+ TranslateItem(hItem);
+
+ HTREEITEM hItemTmp = nullptr;
+ if (hItemTmp = GetChild(hItem))
+ hItem = hItemTmp;
+ else if (hItemTmp = GetNextSibling(hItem))
+ hItem = hItemTmp;
+ else {
+ while (true) {
+ if (!(hItem = GetParent(hItem)))
+ break;
+ if (hItemTmp = GetNextSibling(hItem)) {
+ hItem = hItemTmp;
+ break;
+ }
+ }
+ }
+ }
+}
+
+HTREEITEM CCtrlTreeView::FindNamedItem(HTREEITEM hItem, const wchar_t *name)
+{
+ TVITEMEX tvi = { 0 };
+ wchar_t str[MAX_PATH];
+
+ if (hItem)
+ tvi.hItem = GetChild(hItem);
+ else
+ tvi.hItem = GetRoot();
+
+ if (!name)
+ return tvi.hItem;
+
+ tvi.mask = TVIF_TEXT;
+ tvi.pszText = str;
+ tvi.cchTextMax = _countof(str);
+
+ while (tvi.hItem) {
+ GetItem(&tvi);
+
+ if (!mir_wstrcmp(tvi.pszText, name))
+ return tvi.hItem;
+
+ tvi.hItem = GetNextSibling(tvi.hItem);
+ }
+ return nullptr;
+}
+
+void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi) const
+{
+ memset(tvi, 0, sizeof(*tvi));
+ tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE;
+ tvi->hItem = hItem;
+ GetItem(tvi);
+}
+
+void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi, wchar_t *szText, int iTextLength) const
+{
+ memset(tvi, 0, sizeof(*tvi));
+ tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_TEXT;
+ tvi->hItem = hItem;
+ tvi->pszText = szText;
+ tvi->cchTextMax = iTextLength;
+ GetItem(tvi);
+}
+
+bool CCtrlTreeView::IsSelected(HTREEITEM hItem)
+{
+ return (TVIS_SELECTED & TreeView_GetItemState(m_hwnd, hItem, TVIS_SELECTED)) == TVIS_SELECTED;
+}
+
+void CCtrlTreeView::Select(HTREEITEM hItem)
+{
+ TreeView_SetItemState(m_hwnd, hItem, TVIS_SELECTED, TVIS_SELECTED);
+}
+
+void CCtrlTreeView::Unselect(HTREEITEM hItem)
+{
+ TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_SELECTED);
+}
+
+void CCtrlTreeView::DropHilite(HTREEITEM hItem)
+{
+ TreeView_SetItemState(m_hwnd, hItem, TVIS_DROPHILITED, TVIS_DROPHILITED);
+}
+
+void CCtrlTreeView::DropUnhilite(HTREEITEM hItem)
+{
+ TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_DROPHILITED);
+}
+
+void CCtrlTreeView::SelectAll()
+{
+ TreeView_SelectItem(m_hwnd, nullptr);
+
+ HTREEITEM hItem = TreeView_GetRoot(m_hwnd);
+ while (hItem) {
+ Select(hItem);
+ hItem = TreeView_GetNextSibling(m_hwnd, hItem);
+ }
+}
+
+void CCtrlTreeView::UnselectAll()
+{
+ TreeView_SelectItem(m_hwnd, nullptr);
+
+ HTREEITEM hItem = TreeView_GetRoot(m_hwnd);
+ while (hItem) {
+ Unselect(hItem);
+ hItem = TreeView_GetNextSibling(m_hwnd, hItem);
+ }
+}
+
+void CCtrlTreeView::SelectRange(HTREEITEM hStart, HTREEITEM hEnd)
+{
+ int start = 0, end = 0, i = 0;
+ HTREEITEM hItem = TreeView_GetRoot(m_hwnd);
+ while (hItem) {
+ if (hItem == hStart)
+ start = i;
+ if (hItem == hEnd)
+ end = i;
+
+ i++;
+ hItem = TreeView_GetNextSibling(m_hwnd, hItem);
+ }
+
+ if (end < start) {
+ int tmp = start;
+ start = end;
+ end = tmp;
+ }
+
+ i = 0;
+ hItem = TreeView_GetRoot(m_hwnd);
+ while (hItem) {
+ if (i >= start)
+ Select(hItem);
+ if (i == end)
+ break;
+
+ i++;
+ hItem = TreeView_GetNextSibling(m_hwnd, hItem);
+ }
+}
+
+int CCtrlTreeView::GetNumSelected()
+{
+ int ret = 0;
+ for (HTREEITEM hItem = TreeView_GetRoot(m_hwnd); hItem; hItem = TreeView_GetNextSibling(m_hwnd, hItem))
+ if (IsSelected(hItem))
+ ret++;
+
+ return ret;
+}
+
+void CCtrlTreeView::GetSelected(LIST<_TREEITEM> &selected)
+{
+ HTREEITEM hItem = TreeView_GetRoot(m_hwnd);
+ while (hItem) {
+ if (IsSelected(hItem))
+ selected.insert(hItem);
+ hItem = TreeView_GetNextSibling(m_hwnd, hItem);
+ }
+}
+
+void CCtrlTreeView::Select(LIST<_TREEITEM> &selected)
+{
+ for (auto &it : selected)
+ if (it != nullptr)
+ Select(it);
+}
+
+void CCtrlTreeView::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+
+ // position is empty, let's fill it using selection
+ if (pos.pt.x == 0 && pos.pt.y == 0) {
+ HTREEITEM hItem = GetSelection();
+ if (hItem != nullptr) {
+ pos.pCtrl = this;
+ pos.hItem = hItem;
+
+ RECT rc;
+ GetItemRect(hItem, &rc, TRUE);
+ pos.pt.x = rc.left + 8;
+ pos.pt.y = rc.top + 8;
+ ClientToScreen(m_hwnd, &pos.pt);
+ return;
+ }
+ }
+ // position is present, let's calculate current item
+ else {
+ TVHITTESTINFO hti;
+ hti.pt = pos.pt;
+ ScreenToClient(m_hwnd, &hti.pt);
+ if (HitTest(&hti) && (hti.flags & TVHT_ONITEM)) {
+ pos.hItem = hti.hItem;
+ return;
+ }
+ }
+
+ CSuper::GetCaretPos(pos);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HIMAGELIST CCtrlTreeView::CreateDragImage(HTREEITEM hItem)
+{ return TreeView_CreateDragImage(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::DeleteAllItems()
+{ TreeView_DeleteAllItems(m_hwnd);
+}
+
+void CCtrlTreeView::DeleteItem(HTREEITEM hItem)
+{ TreeView_DeleteItem(m_hwnd, hItem);
+}
+
+HWND CCtrlTreeView::EditLabel(HTREEITEM hItem)
+{ return TreeView_EditLabel(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::EndEditLabelNow(BOOL cancel)
+{ TreeView_EndEditLabelNow(m_hwnd, cancel);
+}
+
+void CCtrlTreeView::EnsureVisible(HTREEITEM hItem)
+{ TreeView_EnsureVisible(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::Expand(HTREEITEM hItem, uint32_t flag)
+{ TreeView_Expand(m_hwnd, hItem, flag);
+}
+
+COLORREF CCtrlTreeView::GetBkColor() const
+{ return TreeView_GetBkColor(m_hwnd);
+}
+
+uint32_t CCtrlTreeView::GetCheckState(HTREEITEM hItem) const
+{ return TreeView_GetCheckState(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetChild(HTREEITEM hItem) const
+{ return TreeView_GetChild(m_hwnd, hItem);
+}
+
+int CCtrlTreeView::GetCount() const
+{ return TreeView_GetCount(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetDropHilight() const
+{ return TreeView_GetDropHilight(m_hwnd);
+}
+
+HWND CCtrlTreeView::GetEditControl() const
+{ return TreeView_GetEditControl(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetFirstVisible() const
+{ return TreeView_GetFirstVisible(m_hwnd);
+}
+
+HIMAGELIST CCtrlTreeView::GetImageList(int iImage) const
+{ return TreeView_GetImageList(m_hwnd, iImage);
+}
+
+int CCtrlTreeView::GetIndent() const
+{ return TreeView_GetIndent(m_hwnd);
+}
+
+COLORREF CCtrlTreeView::GetInsertMarkColor() const
+{ return TreeView_GetInsertMarkColor(m_hwnd);
+}
+
+bool CCtrlTreeView::GetItem(TVITEMEX *tvi) const
+{ return TreeView_GetItem(m_hwnd, tvi) == TRUE;
+}
+
+int CCtrlTreeView::GetItemHeight() const
+{ return TreeView_GetItemHeight(m_hwnd);
+}
+
+void CCtrlTreeView::GetItemRect(HTREEITEM hItem, RECT *rcItem, BOOL fItemRect) const
+{ TreeView_GetItemRect(m_hwnd, hItem, rcItem, fItemRect);
+}
+
+uint32_t CCtrlTreeView::GetItemState(HTREEITEM hItem, uint32_t stateMask) const
+{ return TreeView_GetItemState(m_hwnd, hItem, stateMask);
+}
+
+HTREEITEM CCtrlTreeView::GetLastVisible() const
+{ return TreeView_GetLastVisible(m_hwnd);
+}
+
+COLORREF CCtrlTreeView::GetLineColor() const
+{ return TreeView_GetLineColor(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetNextItem(HTREEITEM hItem, uint32_t flag) const
+{ return TreeView_GetNextItem(m_hwnd, hItem, flag);
+}
+
+HTREEITEM CCtrlTreeView::GetNextSibling(HTREEITEM hItem) const
+{ return TreeView_GetNextSibling(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetNextVisible(HTREEITEM hItem) const
+{ return TreeView_GetNextVisible(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetParent(HTREEITEM hItem) const
+{ return TreeView_GetParent(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetPrevSibling(HTREEITEM hItem) const
+{ return TreeView_GetPrevSibling(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetPrevVisible(HTREEITEM hItem) const
+{ return TreeView_GetPrevVisible(m_hwnd, hItem);
+}
+
+HTREEITEM CCtrlTreeView::GetRoot() const
+{ return TreeView_GetRoot(m_hwnd);
+}
+
+uint32_t CCtrlTreeView::GetScrollTime() const
+{ return TreeView_GetScrollTime(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::GetSelection() const
+{ return TreeView_GetSelection(m_hwnd);
+}
+
+COLORREF CCtrlTreeView::GetTextColor() const
+{ return TreeView_GetTextColor(m_hwnd);
+}
+
+HWND CCtrlTreeView::GetToolTips() const
+{ return TreeView_GetToolTips(m_hwnd);
+}
+
+BOOL CCtrlTreeView::GetUnicodeFormat() const
+{ return TreeView_GetUnicodeFormat(m_hwnd);
+}
+
+unsigned CCtrlTreeView::GetVisibleCount() const
+{ return TreeView_GetVisibleCount(m_hwnd);
+}
+
+HTREEITEM CCtrlTreeView::HitTest(TVHITTESTINFO *hti) const
+{ return TreeView_HitTest(m_hwnd, hti);
+}
+
+HTREEITEM CCtrlTreeView::InsertItem(TVINSERTSTRUCT *tvis)
+{ return TreeView_InsertItem(m_hwnd, tvis);
+}
+
+void CCtrlTreeView::Select(HTREEITEM hItem, uint32_t flag)
+{ TreeView_Select(m_hwnd, hItem, flag);
+}
+
+void CCtrlTreeView::SelectDropTarget(HTREEITEM hItem)
+{ TreeView_SelectDropTarget(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::SelectItem(HTREEITEM hItem)
+{ TreeView_SelectItem(m_hwnd, hItem);
+}
+
+void CCtrlTreeView::SelectSetFirstVisible(HTREEITEM hItem)
+{ TreeView_SelectSetFirstVisible(m_hwnd, hItem);
+}
+
+COLORREF CCtrlTreeView::SetBkColor(COLORREF clBack)
+{ return TreeView_SetBkColor(m_hwnd, clBack);
+}
+
+void CCtrlTreeView::SetCheckState(HTREEITEM hItem, uint32_t state)
+{ TreeView_SetCheckState(m_hwnd, hItem, state);
+}
+
+void CCtrlTreeView::SetImageList(HIMAGELIST hIml, int iImage)
+{ TreeView_SetImageList(m_hwnd, hIml, iImage);
+}
+
+void CCtrlTreeView::SetIndent(int iIndent)
+{ TreeView_SetIndent(m_hwnd, iIndent);
+}
+
+void CCtrlTreeView::SetInsertMark(HTREEITEM hItem, BOOL fAfter)
+{ TreeView_SetInsertMark(m_hwnd, hItem, fAfter);
+}
+
+COLORREF CCtrlTreeView::SetInsertMarkColor(COLORREF clMark)
+{ return TreeView_SetInsertMarkColor(m_hwnd, clMark);
+}
+
+void CCtrlTreeView::SetItem(TVITEMEX *tvi)
+{ TreeView_SetItem(m_hwnd, tvi);
+}
+
+void CCtrlTreeView::SetItemHeight(short cyItem)
+{ TreeView_SetItemHeight(m_hwnd, cyItem);
+}
+
+void CCtrlTreeView::SetItemState(HTREEITEM hItem, uint32_t state, uint32_t stateMask)
+{ TreeView_SetItemState(m_hwnd, hItem, state, stateMask);
+}
+
+COLORREF CCtrlTreeView::SetLineColor(COLORREF clLine)
+{ return TreeView_SetLineColor(m_hwnd, clLine);
+}
+
+void CCtrlTreeView::SetScrollTime(UINT uMaxScrollTime)
+{ TreeView_SetScrollTime(m_hwnd, uMaxScrollTime);
+}
+
+COLORREF CCtrlTreeView::SetTextColor(COLORREF clText)
+{ return TreeView_SetTextColor(m_hwnd, clText);
+}
+
+HWND CCtrlTreeView::SetToolTips(HWND hwndToolTips)
+{ return TreeView_SetToolTips(m_hwnd, hwndToolTips);
+}
+
+BOOL CCtrlTreeView::SetUnicodeFormat(BOOL fUnicode)
+{ return TreeView_SetUnicodeFormat(m_hwnd, fUnicode);
+}
+
+void CCtrlTreeView::SortChildren(HTREEITEM hItem, BOOL fRecurse)
+{ TreeView_SortChildren(m_hwnd, hItem, fRecurse);
+}
+
+void CCtrlTreeView::SortChildrenCB(TVSORTCB *cb, BOOL fRecurse)
+{ TreeView_SortChildrenCB(m_hwnd, cb, fRecurse);
+}
diff --git a/src/mir_core/src/Linux/CDbLink.cpp b/src/mir_core/src/Linux/CDbLink.cpp index 2a0734cd3b..05925f871b 100644 --- a/src/mir_core/src/Linux/CDbLink.cpp +++ b/src/mir_core/src/Linux/CDbLink.cpp @@ -1,92 +1,92 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CDbLink class - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue) - : CDataLink(type) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_iDefault = iValue; - m_szDefault = nullptr; - dbv.type = DBVT_DELETED; -} - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue) - : CDataLink(type), - m_iDefault(0) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_szDefault = mir_wstrdup(szValue); - dbv.type = DBVT_DELETED; -} - -CDbLink::~CDbLink() -{ - mir_free(m_szModule); - mir_free(m_szSetting); - mir_free(m_szDefault); - if (dbv.type != DBVT_DELETED) - db_free(&dbv); -} - -uint32_t CDbLink::LoadInt() -{ - switch (m_type) { - case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault); - default: return m_iDefault; - } -} - -void CDbLink::SaveInt(uint32_t value) -{ - switch (m_type) { - case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break; - case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break; - case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break; - } -} - -wchar_t* CDbLink::LoadText() -{ - if (dbv.type != DBVT_DELETED) db_free(&dbv); - if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) { - if (dbv.type == DBVT_WCHAR) - return dbv.pwszVal; - return m_szDefault; - } - - dbv.type = DBVT_DELETED; - return m_szDefault; -} - -void CDbLink::SaveText(wchar_t *value) -{ - db_set_ws(0, m_szModule, m_szSetting, value); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDbLink class
+
+CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue)
+ : CDataLink(type)
+{
+ m_szModule = mir_strdup(szModule);
+ m_szSetting = mir_strdup(szSetting);
+ m_iDefault = iValue;
+ m_szDefault = nullptr;
+ dbv.type = DBVT_DELETED;
+}
+
+CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue)
+ : CDataLink(type),
+ m_iDefault(0)
+{
+ m_szModule = mir_strdup(szModule);
+ m_szSetting = mir_strdup(szSetting);
+ m_szDefault = mir_wstrdup(szValue);
+ dbv.type = DBVT_DELETED;
+}
+
+CDbLink::~CDbLink()
+{
+ mir_free(m_szModule);
+ mir_free(m_szSetting);
+ mir_free(m_szDefault);
+ if (dbv.type != DBVT_DELETED)
+ db_free(&dbv);
+}
+
+uint32_t CDbLink::LoadInt()
+{
+ switch (m_type) {
+ case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault);
+ default: return m_iDefault;
+ }
+}
+
+void CDbLink::SaveInt(uint32_t value)
+{
+ switch (m_type) {
+ case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break;
+ case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break;
+ case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break;
+ }
+}
+
+wchar_t* CDbLink::LoadText()
+{
+ if (dbv.type != DBVT_DELETED) db_free(&dbv);
+ if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) {
+ if (dbv.type == DBVT_WCHAR)
+ return dbv.pwszVal;
+ return m_szDefault;
+ }
+
+ dbv.type = DBVT_DELETED;
+ return m_szDefault;
+}
+
+void CDbLink::SaveText(wchar_t *value)
+{
+ db_set_ws(0, m_szModule, m_szSetting, value);
+}
diff --git a/src/mir_core/src/Linux/CDlgBase.cpp b/src/mir_core/src/Linux/CDlgBase.cpp index 3504a7e74c..2ec9ceb8c8 100644 --- a/src/mir_core/src/Linux/CDlgBase.cpp +++ b/src/mir_core/src/Linux/CDlgBase.cpp @@ -1,260 +1,260 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static mir_cs csDialogs; - -static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} -static LIST<CDlgBase> arDialogs(10, CompareDialogs); - -#pragma comment(lib, "uxtheme") - -///////////////////////////////////////////////////////////////////////////////////////// -// CDlgBase - -static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2) -{ - return c1->GetCtrlId() - c2->GetCtrlId(); -} - -static int CompareTimerId(const CTimer *t1, const CTimer *t2) -{ - return t1->GetEventId() - t2->GetEventId(); -} - -CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog) - : m_controls(1, CompareControlId), - m_timers(1, CompareTimerId), - m_pPlugin(pPlug) -{ - m_idDialog = idDialog; - m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL; -} - -CDlgBase::~CDlgBase() -{ - m_bInitialized = false; // prevent double call of destructor - // if (m_hwnd) - // DestroyWindow(m_hwnd); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// events - -bool CDlgBase::OnInitDialog() -{ - return true; -} - -bool CDlgBase::OnClose() -{ - return true; -} - -bool CDlgBase::OnApply() -{ - return true; -} - -void CDlgBase::OnChange() -{} - -void CDlgBase::OnDestroy() -{} - -void CDlgBase::OnReset() -{} - -void CDlgBase::OnTimer(CTimer*) -{} - -///////////////////////////////////////////////////////////////////////////////////////// -// methods - -void CDlgBase::Close() -{ - // ::SendMessage(m_hwnd, WM_CLOSE, 0, 0); -} - -void CDlgBase::Create() -{ - // CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); -} - -int CDlgBase::DoModal() -{ - m_isModal = true; - // return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); - return 0; //!!!!!!!! -} - -void CDlgBase::EndModal(INT_PTR nResult) -{ - // ::EndDialog(m_hwnd, nResult); -} - -HINSTANCE CDlgBase::GetInst() const -{ - return m_pPlugin.getInst(); -} - -void CDlgBase::NotifyChange(void) -{ - if (!m_bInitialized) - return; - - OnChange(); - - // if (m_hwndParent) - // SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0); -} - -void CDlgBase::Resize() -{ - // SendMessage(m_hwnd, WM_SIZE, 0, 0); -} - -void CDlgBase::SetCaption(const wchar_t *ptszCaption) -{ - // if (m_hwnd && ptszCaption) - // SetText(ptszCaption); -} - -void CDlgBase::SetDraw(bool bEnable) -{ - // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CDlgBase::Show(int nCmdShow) -{ - if (m_hwnd == nullptr) - Create(); - // ShowWindow(m_hwnd, nCmdShow); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue); -} - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// virtual methods - -int CDlgBase::Resizer(UTILRESIZECONTROL*) -{ - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -BOOL CALLBACK CDlgBase::GlobalFieldEnum(MWindow hwnd, LPARAM lParam) -{ - return TRUE; -} - -INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - return FALSE; -} - -int CDlgBase::GlobalDlgResizer(MWindow hwnd, LPARAM, UTILRESIZECONTROL *urc) -{ - CDlgBase *wnd = CDlgBase::Find(hwnd); - return (wnd == nullptr) ? 0 : wnd->Resizer(urc); -} - -void CDlgBase::ThemeDialogBackground(BOOL tabbed) -{ -} - -void CDlgBase::AddControl(CCtrlBase *ctrl) -{ - m_controls.insert(ctrl); -} - -void CDlgBase::RemoveControl(CCtrlBase *ctrl) -{ - m_controls.remove(ctrl); -} - -void CDlgBase::NotifyControls(void (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - (it->*fn)(); -} - -bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - if (!(it->*fn)()) - return false; - - return true; -} - -CCtrlBase* CDlgBase::FindControl(int idCtrl) -{ - CCtrlBase search(nullptr, idCtrl); - return m_controls.find(&search); -} - -CCtrlBase* CDlgBase::FindControl(MWindow hwnd) -{ - for (auto &it : m_controls) - if (it->GetHwnd() == hwnd) - return it; - - return nullptr; -} - -void CDlgBase::AddTimer(CTimer *timer) -{ - m_timers.insert(timer); -} - -void CDlgBase::RemoveTimer(UINT_PTR idEvent) -{ - CTimer search(nullptr, idEvent); - m_timers.remove(&search); -} - -CTimer* CDlgBase::FindTimer(int idEvent) -{ - CTimer search(nullptr, idEvent); - return m_timers.find(&search); -} - -CDlgBase* CDlgBase::Find(MWindow hwnd) -{ - void *bullshit[2]; // vfptr + hwnd - bullshit[1] = hwnd; - return arDialogs.find((CDlgBase*)&bullshit); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static mir_cs csDialogs;
+
+static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2)
+{
+ return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd();
+}
+static LIST<CDlgBase> arDialogs(10, CompareDialogs);
+
+#pragma comment(lib, "uxtheme")
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDlgBase
+
+static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2)
+{
+ return c1->GetCtrlId() - c2->GetCtrlId();
+}
+
+static int CompareTimerId(const CTimer *t1, const CTimer *t2)
+{
+ return t1->GetEventId() - t2->GetEventId();
+}
+
+CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog)
+ : m_controls(1, CompareControlId),
+ m_timers(1, CompareTimerId),
+ m_pPlugin(pPlug)
+{
+ m_idDialog = idDialog;
+ m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL;
+}
+
+CDlgBase::~CDlgBase()
+{
+ m_bInitialized = false; // prevent double call of destructor
+ // if (m_hwnd)
+ // DestroyWindow(m_hwnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// events
+
+bool CDlgBase::OnInitDialog()
+{
+ return true;
+}
+
+bool CDlgBase::OnClose()
+{
+ return true;
+}
+
+bool CDlgBase::OnApply()
+{
+ return true;
+}
+
+void CDlgBase::OnChange()
+{}
+
+void CDlgBase::OnDestroy()
+{}
+
+void CDlgBase::OnReset()
+{}
+
+void CDlgBase::OnTimer(CTimer*)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// methods
+
+void CDlgBase::Close()
+{
+ // ::SendMessage(m_hwnd, WM_CLOSE, 0, 0);
+}
+
+void CDlgBase::Create()
+{
+ // CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this);
+}
+
+int CDlgBase::DoModal()
+{
+ m_isModal = true;
+ // return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this);
+ return 0; //!!!!!!!!
+}
+
+void CDlgBase::EndModal(INT_PTR nResult)
+{
+ // ::EndDialog(m_hwnd, nResult);
+}
+
+HINSTANCE CDlgBase::GetInst() const
+{
+ return m_pPlugin.getInst();
+}
+
+void CDlgBase::NotifyChange(void)
+{
+ if (!m_bInitialized)
+ return;
+
+ OnChange();
+
+ // if (m_hwndParent)
+ // SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0);
+}
+
+void CDlgBase::Resize()
+{
+ // SendMessage(m_hwnd, WM_SIZE, 0, 0);
+}
+
+void CDlgBase::SetCaption(const wchar_t *ptszCaption)
+{
+ // if (m_hwnd && ptszCaption)
+ // SetText(ptszCaption);
+}
+
+void CDlgBase::SetDraw(bool bEnable)
+{
+ // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0);
+}
+
+void CDlgBase::Show(int nCmdShow)
+{
+ if (m_hwnd == nullptr)
+ Create();
+ // ShowWindow(m_hwnd, nCmdShow);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue)
+{
+ ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue);
+}
+
+void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue)
+{
+ ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// virtual methods
+
+int CDlgBase::Resizer(UTILRESIZECONTROL*)
+{
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+BOOL CALLBACK CDlgBase::GlobalFieldEnum(MWindow hwnd, LPARAM lParam)
+{
+ return TRUE;
+}
+
+INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ return FALSE;
+}
+
+int CDlgBase::GlobalDlgResizer(MWindow hwnd, LPARAM, UTILRESIZECONTROL *urc)
+{
+ CDlgBase *wnd = CDlgBase::Find(hwnd);
+ return (wnd == nullptr) ? 0 : wnd->Resizer(urc);
+}
+
+void CDlgBase::ThemeDialogBackground(BOOL tabbed)
+{
+}
+
+void CDlgBase::AddControl(CCtrlBase *ctrl)
+{
+ m_controls.insert(ctrl);
+}
+
+void CDlgBase::RemoveControl(CCtrlBase *ctrl)
+{
+ m_controls.remove(ctrl);
+}
+
+void CDlgBase::NotifyControls(void (CCtrlBase::*fn)())
+{
+ for (auto &it : m_controls)
+ (it->*fn)();
+}
+
+bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)())
+{
+ for (auto &it : m_controls)
+ if (!(it->*fn)())
+ return false;
+
+ return true;
+}
+
+CCtrlBase* CDlgBase::FindControl(int idCtrl)
+{
+ CCtrlBase search(nullptr, idCtrl);
+ return m_controls.find(&search);
+}
+
+CCtrlBase* CDlgBase::FindControl(MWindow hwnd)
+{
+ for (auto &it : m_controls)
+ if (it->GetHwnd() == hwnd)
+ return it;
+
+ return nullptr;
+}
+
+void CDlgBase::AddTimer(CTimer *timer)
+{
+ m_timers.insert(timer);
+}
+
+void CDlgBase::RemoveTimer(UINT_PTR idEvent)
+{
+ CTimer search(nullptr, idEvent);
+ m_timers.remove(&search);
+}
+
+CTimer* CDlgBase::FindTimer(int idEvent)
+{
+ CTimer search(nullptr, idEvent);
+ return m_timers.find(&search);
+}
+
+CDlgBase* CDlgBase::Find(MWindow hwnd)
+{
+ void *bullshit[2]; // vfptr + hwnd
+ bullshit[1] = hwnd;
+ return arDialogs.find((CDlgBase*)&bullshit);
+}
diff --git a/src/mir_core/src/Linux/CProgress.cpp b/src/mir_core/src/Linux/CProgress.cpp index 991c6f239d..e2d5321f59 100644 --- a/src/mir_core/src/Linux/CProgress.cpp +++ b/src/mir_core/src/Linux/CProgress.cpp @@ -1,53 +1,53 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlProgress - -CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{ -} - -void CCtrlProgress::SetRange(uint16_t max, uint16_t min) -{ - SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max)); -} - -void CCtrlProgress::SetPosition(uint16_t value) -{ - SendMsg(PBM_SETPOS, value, 0); -} - -void CCtrlProgress::SetStep(uint16_t value) -{ - SendMsg(PBM_SETSTEP, value, 0); -} - -uint16_t CCtrlProgress::Move(uint16_t delta) -{ - return delta == 0 - ? SendMsg(PBM_STEPIT, 0, 0) - : SendMsg(PBM_DELTAPOS, delta, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlProgress
+
+CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{
+}
+
+void CCtrlProgress::SetRange(uint16_t max, uint16_t min)
+{
+ SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max));
+}
+
+void CCtrlProgress::SetPosition(uint16_t value)
+{
+ SendMsg(PBM_SETPOS, value, 0);
+}
+
+void CCtrlProgress::SetStep(uint16_t value)
+{
+ SendMsg(PBM_SETSTEP, value, 0);
+}
+
+uint16_t CCtrlProgress::Move(uint16_t delta)
+{
+ return delta == 0
+ ? SendMsg(PBM_STEPIT, 0, 0)
+ : SendMsg(PBM_DELTAPOS, delta, 0);
+}
diff --git a/src/mir_core/src/Linux/CSplitter.cpp b/src/mir_core/src/Linux/CSplitter.cpp index e2ee6b6fc8..153beaf95b 100644 --- a/src/mir_core/src/Linux/CSplitter.cpp +++ b/src/mir_core/src/Linux/CSplitter.cpp @@ -1,83 +1,83 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CSplitter - -CSplitter::CSplitter(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_iPosition(0) -{ -} - -void CSplitter::OnInit() -{ - CSuper::OnInit(); - Subclass(); -} - -LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_NCHITTEST: - return HTCLIENT; - - case WM_SETCURSOR: - RECT rc; - GetClientRect(m_hwnd, &rc); - SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE); - return TRUE; - - case WM_LBUTTONDOWN: - SetCapture(m_hwnd); - return 0; - - case WM_MOUSEMOVE: - if (GetCapture() == m_hwnd) { - POINT pt = { 0, 0 }; - GetClientRect(m_hwnd, &rc); - if (rc.right > rc.bottom) { - pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.y; - } - else { - pt.x = LOWORD(GetMessagePos()) + rc.right / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.x; - } - - OnChange(this); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - } - return 0; - - case WM_LBUTTONUP: - ReleaseCapture(); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - return 0; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CSplitter
+
+CSplitter::CSplitter(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl),
+ m_iPosition(0)
+{
+}
+
+void CSplitter::OnInit()
+{
+ CSuper::OnInit();
+ Subclass();
+}
+
+LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_NCHITTEST:
+ return HTCLIENT;
+
+ case WM_SETCURSOR:
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE);
+ return TRUE;
+
+ case WM_LBUTTONDOWN:
+ SetCapture(m_hwnd);
+ return 0;
+
+ case WM_MOUSEMOVE:
+ if (GetCapture() == m_hwnd) {
+ POINT pt = { 0, 0 };
+ GetClientRect(m_hwnd, &rc);
+ if (rc.right > rc.bottom) {
+ pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2;
+ ScreenToClient(m_parentWnd->GetHwnd(), &pt);
+ m_iPosition = pt.y;
+ }
+ else {
+ pt.x = LOWORD(GetMessagePos()) + rc.right / 2;
+ ScreenToClient(m_parentWnd->GetHwnd(), &pt);
+ m_iPosition = pt.x;
+ }
+
+ OnChange(this);
+ PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0);
+ }
+ return 0;
+
+ case WM_LBUTTONUP:
+ ReleaseCapture();
+ PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0);
+ return 0;
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
diff --git a/src/mir_core/src/Linux/CTimer.cpp b/src/mir_core/src/Linux/CTimer.cpp index 5d3c8b01b1..07fc863c60 100644 --- a/src/mir_core/src/Linux/CTimer.cpp +++ b/src/mir_core/src/Linux/CTimer.cpp @@ -1,93 +1,93 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CTimer - -CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent) - : m_wnd(wnd), m_idEvent(idEvent) -{ - if (wnd) - wnd->AddTimer(this); -} - -CTimer::~CTimer() -{ - if (m_wnd) - m_wnd->RemoveTimer(m_idEvent); -} - -BOOL CTimer::OnTimer() -{ - OnEvent(this); - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CTimer::Start(int elapse) -{ - // ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr); -} - -bool CTimer::Stop() -{ - // return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent); - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct TStartParam -{ - CTimer *pTimer; - int period; -}; - -static INT_PTR CALLBACK stubStart(void *param) -{ - auto *p = (TStartParam *)param; - // return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr); - return 0; -} - -void CTimer::StartSafe(int elapse) -{ - TStartParam param = { this, elapse }; - CallFunctionSync(stubStart, ¶m); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static INT_PTR CALLBACK stubStop(void *param) -{ - auto *p = (CTimer*)param; - // return ::KillTimer(p->GetHwnd(), p->GetEventId()); - return 0; -} - -void CTimer::StopSafe() -{ - CallFunctionSync(stubStop, this); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CTimer
+
+CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent)
+ : m_wnd(wnd), m_idEvent(idEvent)
+{
+ if (wnd)
+ wnd->AddTimer(this);
+}
+
+CTimer::~CTimer()
+{
+ if (m_wnd)
+ m_wnd->RemoveTimer(m_idEvent);
+}
+
+BOOL CTimer::OnTimer()
+{
+ OnEvent(this);
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CTimer::Start(int elapse)
+{
+ // ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr);
+}
+
+bool CTimer::Stop()
+{
+ // return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent);
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct TStartParam
+{
+ CTimer *pTimer;
+ int period;
+};
+
+static INT_PTR CALLBACK stubStart(void *param)
+{
+ auto *p = (TStartParam *)param;
+ // return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr);
+ return 0;
+}
+
+void CTimer::StartSafe(int elapse)
+{
+ TStartParam param = { this, elapse };
+ CallFunctionSync(stubStart, ¶m);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR CALLBACK stubStop(void *param)
+{
+ auto *p = (CTimer*)param;
+ // return ::KillTimer(p->GetHwnd(), p->GetEventId());
+ return 0;
+}
+
+void CTimer::StopSafe()
+{
+ CallFunctionSync(stubStop, this);
+}
diff --git a/src/mir_core/src/Linux/cctrldate.cpp b/src/mir_core/src/Linux/cctrldate.cpp index 1967cb5678..253b549870 100644 --- a/src/mir_core/src/Linux/cctrldate.cpp +++ b/src/mir_core/src/Linux/cctrldate.cpp @@ -1,49 +1,49 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlDate class - -CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == DTN_DATETIMECHANGE) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -void CCtrlDate::GetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate); -} - -void CCtrlDate::SetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlDate class
+
+CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) :
+ CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh)
+{
+ if (pnmh->code == DTN_DATETIMECHANGE) {
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void CCtrlDate::GetTime(SYSTEMTIME *pDate)
+{
+ ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate);
+}
+
+void CCtrlDate::SetTime(SYSTEMTIME *pDate)
+{
+ ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate);
+}
diff --git a/src/mir_core/src/Linux/fileutil.cpp b/src/mir_core/src/Linux/fileutil.cpp index a5002f0a66..fadc70e430 100644 --- a/src/mir_core/src/Linux/fileutil.cpp +++ b/src/mir_core/src/Linux/fileutil.cpp @@ -1,89 +1,89 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "../stdafx.h" -#include <unistd.h> - -MIR_CORE_DLL(FILE*) _wfopen(const wchar_t *pwszFileName, const wchar_t *pwszMode) -{ - return fopen(T2Utf(pwszFileName), T2Utf(pwszMode)); -} - -MIR_CORE_DLL(int) _wchdir(const wchar_t *pwszPath) -{ - return chdir(T2Utf(pwszPath)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++() -{ - // if (ptr != nullptr) { - // if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) { - // ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE; - // ptr = nullptr; - // } - // } - return *this; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath) -{ - // if (pwszPath != nullptr) - // m_hFind = ::FindFirstFileW(pwszPath, &m_data); -} - -MFilePath::MFileIterator::~MFileIterator() -{ - // if (m_hFind != INVALID_HANDLE_VALUE) - // ::FindClose(m_hFind); -} - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin() -{ - // if (m_hFind == INVALID_HANDLE_VALUE) - // return MFilePath::MFileIterator::iterator(nullptr); - - return MFilePath::MFileIterator::iterator(this); -} - -bool MFilePath::MFileIterator::isDir() const -{ - //if (m_hFind == INVALID_HANDLE_VALUE) - // return false; - - return (m_flags & 1) != 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool MFilePath::isExist() const -{ - return ::access(T2Utf(c_str()), 0) == 0; -} - -bool MFilePath::move(const wchar_t *pwszDest) -{ - return ::rename(T2Utf(c_str()), T2Utf(pwszDest)) != 0; -} - -MFilePath::MFileIterator MFilePath::search() -{ - return MFileIterator(c_str()); -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "../stdafx.h"
+#include <unistd.h>
+
+MIR_CORE_DLL(FILE*) _wfopen(const wchar_t *pwszFileName, const wchar_t *pwszMode)
+{
+ return fopen(T2Utf(pwszFileName), T2Utf(pwszMode));
+}
+
+MIR_CORE_DLL(int) _wchdir(const wchar_t *pwszPath)
+{
+ return chdir(T2Utf(pwszPath));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++()
+{
+ // if (ptr != nullptr) {
+ // if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) {
+ // ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE;
+ // ptr = nullptr;
+ // }
+ // }
+ return *this;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath)
+{
+ // if (pwszPath != nullptr)
+ // m_hFind = ::FindFirstFileW(pwszPath, &m_data);
+}
+
+MFilePath::MFileIterator::~MFileIterator()
+{
+ // if (m_hFind != INVALID_HANDLE_VALUE)
+ // ::FindClose(m_hFind);
+}
+
+MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin()
+{
+ // if (m_hFind == INVALID_HANDLE_VALUE)
+ // return MFilePath::MFileIterator::iterator(nullptr);
+
+ return MFilePath::MFileIterator::iterator(this);
+}
+
+bool MFilePath::MFileIterator::isDir() const
+{
+ //if (m_hFind == INVALID_HANDLE_VALUE)
+ // return false;
+
+ return (m_flags & 1) != 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool MFilePath::isExist() const
+{
+ return ::access(T2Utf(c_str()), 0) == 0;
+}
+
+bool MFilePath::move(const wchar_t *pwszDest)
+{
+ return ::rename(T2Utf(c_str()), T2Utf(pwszDest)) != 0;
+}
+
+MFilePath::MFileIterator MFilePath::search()
+{
+ return MFileIterator(c_str());
+}
diff --git a/src/mir_core/src/Linux/strutil.cpp b/src/mir_core/src/Linux/strutil.cpp index a2b854fcc8..8c44a3dd2b 100644 --- a/src/mir_core/src/Linux/strutil.cpp +++ b/src/mir_core/src/Linux/strutil.cpp @@ -1,48 +1,48 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "../stdafx.h" - -MIR_CORE_DLL(char*) strlwr(char *str) -{ - for (char *p = str; *p; p++) - *p = tolower(*p); - - return str; -} - -MIR_CORE_DLL(char*) strupr(char *str) -{ - for (char *p = str; *p; p++) - *p = toupper(*p); - - return str; -} - -MIR_CORE_DLL(char*) strrev(char *str) -{ - if (!str || !*str) - return str; - - char *p1, *p2; - for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) { - *p1 ^= *p2; - *p2 ^= *p1; - *p1 ^= *p2; - } - return str; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "../stdafx.h"
+
+MIR_CORE_DLL(char*) strlwr(char *str)
+{
+ for (char *p = str; *p; p++)
+ *p = tolower(*p);
+
+ return str;
+}
+
+MIR_CORE_DLL(char*) strupr(char *str)
+{
+ for (char *p = str; *p; p++)
+ *p = toupper(*p);
+
+ return str;
+}
+
+MIR_CORE_DLL(char*) strrev(char *str)
+{
+ if (!str || !*str)
+ return str;
+
+ char *p1, *p2;
+ for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) {
+ *p1 ^= *p2;
+ *p2 ^= *p1;
+ *p1 ^= *p2;
+ }
+ return str;
+}
diff --git a/src/mir_core/src/Windows/CCtrlBase.cpp b/src/mir_core/src/Windows/CCtrlBase.cpp index 58787b02db..ad80ccffe7 100644 --- a/src/mir_core/src/Windows/CCtrlBase.cpp +++ b/src/mir_core/src/Windows/CCtrlBase.cpp @@ -1,224 +1,224 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static mir_cs csCtrl; - -static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} -static LIST<CCtrlBase> arControls(10, CompareControls); - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlBase - -CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) : - m_parentWnd(wnd), - m_idCtrl(idCtrl) -{ - if (wnd) - wnd->AddControl(this); -} - -CCtrlBase::~CCtrlBase() -{ -} - -void CCtrlBase::OnInit() -{ - m_hwnd = (m_idCtrl && m_parentWnd && m_parentWnd->GetHwnd()) ? GetDlgItem(m_parentWnd->GetHwnd(), m_idCtrl) : nullptr; -} - -void CCtrlBase::OnDestroy() -{ - PVOID bullshit[2]; // vfptr + hwnd - bullshit[1] = m_hwnd; - CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); - if (pCtrl) { - pCtrl->Unsubclass(); - arControls.remove(pCtrl); - } - - m_hwnd = nullptr; -} - -bool CCtrlBase::OnApply() -{ - m_bChanged = false; - return true; -} - -void CCtrlBase::OnReset() -{} - -void CCtrlBase::Show(bool bShow) -{ - ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE); -} - -void CCtrlBase::Enable(bool bIsEnable) -{ - ::EnableWindow(m_hwnd, bIsEnable); -} - -bool CCtrlBase::Enabled() const -{ - return (m_hwnd) ? IsWindowEnabled(m_hwnd) != 0 : false; -} - -void CCtrlBase::NotifyChange() -{ - if (!m_parentWnd) - return; - - if (m_parentWnd->IsInitialized()) { - m_bChanged = true; - if (!m_bSilent) - m_parentWnd->NotifyChange(); - } - - OnChange(this); -} - -LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const -{ - return ::SendMessage(m_hwnd, Msg, wParam, lParam); -} - -void CCtrlBase::SetText(const wchar_t *text) -{ - ::SetWindowText(m_hwnd, text); -} - -void CCtrlBase::SetTextA(const char *text) -{ - ::SetWindowTextA(m_hwnd, text); -} - -void CCtrlBase::SetDraw(bool bEnable) -{ - ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CCtrlBase::SetInt(int value) -{ - wchar_t buf[32] = { 0 }; - mir_snwprintf(buf, L"%d", value); - SetWindowText(m_hwnd, buf); -} - -wchar_t* CCtrlBase::GetText() const -{ - int length = GetWindowTextLengthW(m_hwnd); - wchar_t *result = (wchar_t *)mir_alloc((length+1) * sizeof(wchar_t)); - if (length) - GetWindowTextW(m_hwnd, result, length+1); - result[length] = 0; - return result; -} - -char* CCtrlBase::GetTextA() const -{ - int length = GetWindowTextLengthA(m_hwnd); - char *result = (char *)mir_alloc((length+1) * sizeof(char)); - if (length) - GetWindowTextA(m_hwnd, result, length+1); - result[length] = 0; - return result; -} - -char* CCtrlBase::GetTextU() const -{ - return mir_utf8encodeW(ptrW(GetText())); -} - -wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const -{ - GetWindowTextW(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextA(char *buf, size_t size) const -{ - GetWindowTextA(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextU(char *buf, size_t size) const -{ - ptrW wszText(GetText()); - strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE); - return buf; -} - -int CCtrlBase::GetInt() const -{ - int length = GetWindowTextLengthW(m_hwnd) + 1; - wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t)); - GetWindowTextW(m_hwnd, result, length); - return _wtoi(result); -} - -void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - pos.iCurr = -1; - - if (pos.pt.x == 0 && pos.pt.y == 0) - GetCursorPos(&pos.pt); -} - -LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM) -{ - return FALSE; -} - -LRESULT CALLBACK CCtrlBase::GlobalSubclassWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - PVOID bullshit[2]; // vfptr + hwnd - bullshit[1] = hwnd; - CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); - if (pCtrl) { - LRESULT res = pCtrl->CustomWndProc(msg, wParam, lParam); - if (res != 0) - return res; - } - - return mir_callNextSubclass(hwnd, GlobalSubclassWndProc, msg, wParam, lParam); -} - -void CCtrlBase::Subclass() -{ - mir_subclassWindow(m_hwnd, GlobalSubclassWndProc); - - mir_cslock lck(csCtrl); - arControls.insert(this); -} - -void CCtrlBase::Unsubclass() -{ - mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static mir_cs csCtrl;
+
+static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2)
+{
+ return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd();
+}
+static LIST<CCtrlBase> arControls(10, CompareControls);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlBase
+
+CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) :
+ m_parentWnd(wnd),
+ m_idCtrl(idCtrl)
+{
+ if (wnd)
+ wnd->AddControl(this);
+}
+
+CCtrlBase::~CCtrlBase()
+{
+}
+
+void CCtrlBase::OnInit()
+{
+ m_hwnd = (m_idCtrl && m_parentWnd && m_parentWnd->GetHwnd()) ? GetDlgItem(m_parentWnd->GetHwnd(), m_idCtrl) : nullptr;
+}
+
+void CCtrlBase::OnDestroy()
+{
+ PVOID bullshit[2]; // vfptr + hwnd
+ bullshit[1] = m_hwnd;
+ CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit);
+ if (pCtrl) {
+ pCtrl->Unsubclass();
+ arControls.remove(pCtrl);
+ }
+
+ m_hwnd = nullptr;
+}
+
+bool CCtrlBase::OnApply()
+{
+ m_bChanged = false;
+ return true;
+}
+
+void CCtrlBase::OnReset()
+{}
+
+void CCtrlBase::Show(bool bShow)
+{
+ ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE);
+}
+
+void CCtrlBase::Enable(bool bIsEnable)
+{
+ ::EnableWindow(m_hwnd, bIsEnable);
+}
+
+bool CCtrlBase::Enabled() const
+{
+ return (m_hwnd) ? IsWindowEnabled(m_hwnd) != 0 : false;
+}
+
+void CCtrlBase::NotifyChange()
+{
+ if (!m_parentWnd)
+ return;
+
+ if (m_parentWnd->IsInitialized()) {
+ m_bChanged = true;
+ if (!m_bSilent)
+ m_parentWnd->NotifyChange();
+ }
+
+ OnChange(this);
+}
+
+LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const
+{
+ return ::SendMessage(m_hwnd, Msg, wParam, lParam);
+}
+
+void CCtrlBase::SetText(const wchar_t *text)
+{
+ ::SetWindowText(m_hwnd, text);
+}
+
+void CCtrlBase::SetTextA(const char *text)
+{
+ ::SetWindowTextA(m_hwnd, text);
+}
+
+void CCtrlBase::SetDraw(bool bEnable)
+{
+ ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0);
+}
+
+void CCtrlBase::SetInt(int value)
+{
+ wchar_t buf[32] = { 0 };
+ mir_snwprintf(buf, L"%d", value);
+ SetWindowText(m_hwnd, buf);
+}
+
+wchar_t* CCtrlBase::GetText() const
+{
+ int length = GetWindowTextLengthW(m_hwnd);
+ wchar_t *result = (wchar_t *)mir_alloc((length+1) * sizeof(wchar_t));
+ if (length)
+ GetWindowTextW(m_hwnd, result, length+1);
+ result[length] = 0;
+ return result;
+}
+
+char* CCtrlBase::GetTextA() const
+{
+ int length = GetWindowTextLengthA(m_hwnd);
+ char *result = (char *)mir_alloc((length+1) * sizeof(char));
+ if (length)
+ GetWindowTextA(m_hwnd, result, length+1);
+ result[length] = 0;
+ return result;
+}
+
+char* CCtrlBase::GetTextU() const
+{
+ return mir_utf8encodeW(ptrW(GetText()));
+}
+
+wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const
+{
+ GetWindowTextW(m_hwnd, buf, (int)size);
+ buf[size - 1] = 0;
+ return buf;
+}
+
+char* CCtrlBase::GetTextA(char *buf, size_t size) const
+{
+ GetWindowTextA(m_hwnd, buf, (int)size);
+ buf[size - 1] = 0;
+ return buf;
+}
+
+char* CCtrlBase::GetTextU(char *buf, size_t size) const
+{
+ ptrW wszText(GetText());
+ strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE);
+ return buf;
+}
+
+int CCtrlBase::GetInt() const
+{
+ int length = GetWindowTextLengthW(m_hwnd) + 1;
+ wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t));
+ GetWindowTextW(m_hwnd, result, length);
+ return _wtoi(result);
+}
+
+void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+ pos.iCurr = -1;
+
+ if (pos.pt.x == 0 && pos.pt.y == 0)
+ GetCursorPos(&pos.pt);
+}
+
+LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM)
+{
+ return FALSE;
+}
+
+LRESULT CALLBACK CCtrlBase::GlobalSubclassWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ PVOID bullshit[2]; // vfptr + hwnd
+ bullshit[1] = hwnd;
+ CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit);
+ if (pCtrl) {
+ LRESULT res = pCtrl->CustomWndProc(msg, wParam, lParam);
+ if (res != 0)
+ return res;
+ }
+
+ return mir_callNextSubclass(hwnd, GlobalSubclassWndProc, msg, wParam, lParam);
+}
+
+void CCtrlBase::Subclass()
+{
+ mir_subclassWindow(m_hwnd, GlobalSubclassWndProc);
+
+ mir_cslock lck(csCtrl);
+ arControls.insert(this);
+}
+
+void CCtrlBase::Unsubclass()
+{
+ mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc);
+}
diff --git a/src/mir_core/src/Windows/CCtrlButton.cpp b/src/mir_core/src/Windows/CCtrlButton.cpp index bf84cad74f..b39e855332 100644 --- a/src/mir_core/src/Windows/CCtrlButton.cpp +++ b/src/mir_core/src/Windows/CCtrlButton.cpp @@ -1,54 +1,54 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlButton - -CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} - -BOOL CCtrlButton::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == BN_CLICKED) - OnClick(this); - return FALSE; -} - -void CCtrlButton::Click() -{ - if (Enabled()) - ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0); -} - -bool CCtrlButton::IsPushed() const -{ - return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED; -} - -void CCtrlButton::Push(bool bPushed) -{ - if (Enabled()) - ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlButton
+
+CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{}
+
+BOOL CCtrlButton::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ if (idCode == BN_CLICKED)
+ OnClick(this);
+ return FALSE;
+}
+
+void CCtrlButton::Click()
+{
+ if (Enabled())
+ ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0);
+}
+
+bool CCtrlButton::IsPushed() const
+{
+ return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED;
+}
+
+void CCtrlButton::Push(bool bPushed)
+{
+ if (Enabled())
+ ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0);
+}
diff --git a/src/mir_core/src/Windows/CCtrlCheck.cpp b/src/mir_core/src/Windows/CCtrlCheck.cpp index 9c1281e8b8..3258dce27f 100644 --- a/src/mir_core/src/Windows/CCtrlCheck.cpp +++ b/src/mir_core/src/Windows/CCtrlCheck.cpp @@ -1,68 +1,68 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCheck class - -CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{ - m_bNotifiable = true; -} - -BOOL CCtrlCheck::OnCommand(HWND, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlCheck::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetState()); - return true; -} - -void CCtrlCheck::OnReset() -{ - if (m_dbLink != nullptr) - SetState(LoadInt()); -} - -int CCtrlCheck::GetState() const -{ - return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0); -} - -void CCtrlCheck::SetState(int state) -{ - ::SendMessage(m_hwnd, BM_SETCHECK, state, 0); -} - -bool CCtrlCheck::IsChecked() -{ - return GetState() == BST_CHECKED; -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCheck class
+
+CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{
+ m_bNotifiable = true;
+}
+
+BOOL CCtrlCheck::OnCommand(HWND, uint16_t, uint16_t)
+{
+ NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlCheck::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetState());
+ return true;
+}
+
+void CCtrlCheck::OnReset()
+{
+ if (m_dbLink != nullptr)
+ SetState(LoadInt());
+}
+
+int CCtrlCheck::GetState() const
+{
+ return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0);
+}
+
+void CCtrlCheck::SetState(int state)
+{
+ ::SendMessage(m_hwnd, BM_SETCHECK, state, 0);
+}
+
+bool CCtrlCheck::IsChecked()
+{
+ return GetState() == BST_CHECKED;
+}
diff --git a/src/mir_core/src/Windows/CCtrlClc.cpp b/src/mir_core/src/Windows/CCtrlClc.cpp index 3e208679d4..14e88335a0 100644 --- a/src/mir_core/src/Windows/CCtrlClc.cpp +++ b/src/mir_core/src/Windows/CCtrlClc.cpp @@ -1,211 +1,211 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlClc - -CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh }; - switch (pnmh->code) { - case CLN_EXPANDED: OnExpanded(&evt); break; - case CLN_LISTREBUILT: OnListRebuilt(&evt); break; - case CLN_ITEMCHECKED: OnItemChecked(&evt); break; - case CLN_DRAGGING: OnDragging(&evt); break; - case CLN_DROPPED: OnDropped(&evt); break; - case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break; - case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break; - case CLN_DRAGSTOP: OnDragStop(&evt); break; - case CLN_NEWCONTACT: OnNewContact(&evt); break; - case CLN_CONTACTMOVED: OnContactMoved(&evt); break; - case NM_CLICK: OnClick(&evt); break; - - case CLN_CHECKCHANGED: - OnCheckChanged(&evt); - NotifyChange(); - break; - } - return FALSE; -} - -void CCtrlClc::AddContact(MCONTACT hContact) -{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0); -} - -void CCtrlClc::AddGroup(HANDLE hGroup) -{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0); -} - -void CCtrlClc::AutoRebuild() -{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0); -} - -void CCtrlClc::DeleteItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::EditLabel(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0); -} - -void CCtrlClc::EndEditLabel(bool save) -{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0); -} - -void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk) -{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE); -} - -void CCtrlClc::Expand(HANDLE hItem, uint32_t flags) -{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags); -} - -HANDLE CCtrlClc::FindContact(MCONTACT hContact) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0); -} - -HANDLE CCtrlClc::FindGroup(MGROUP hGroup) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0); -} - -COLORREF CCtrlClc::GetBkColor() const -{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0); -} - -bool CCtrlClc::GetCheck(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false; -} - -int CCtrlClc::GetCount() const -{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0); -} - -HWND CCtrlClc::GetEditControl() const -{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0); -} - -uint32_t CCtrlClc::GetExpand(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0); -} - -int CCtrlClc::GetExtraColumns() const -{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0); -} - -uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const -{ - return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF); -} - -HIMAGELIST CCtrlClc::GetExtraImageList() const -{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0); -} - -HFONT CCtrlClc::GetFont(int iFontId) const -{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0); -} - -HANDLE CCtrlClc::GetSelection() const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0); -} - -HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y)); -} - -void CCtrlClc::SelectItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::SetBkColor(COLORREF clBack) -{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0); -} - -void CCtrlClc::SetCheck(HANDLE hItem, bool check) -{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0); -} - -void CCtrlClc::SetExtraColumns(int iColumns) -{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0); -} - -void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage)); -} - -void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList); -} - -void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw) -{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId)); -} - -void CCtrlClc::SetItemText(HANDLE hItem, char *szText) -{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText); -} - -void CCtrlClc::SetHideEmptyGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0); -} - -bool CCtrlClc::GetHideOfflineRoot() const -{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false; -} - -void CCtrlClc::SetHideOfflineRoot(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9); -} - -void CCtrlClc::SetUseGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0); -} - -void CCtrlClc::SetOfflineModes(uint32_t modes) -{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0); -} - -uint32_t CCtrlClc::GetExStyle() const -{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0); -} - -void CCtrlClc::SetExStyle(uint32_t exStyle) -{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0); -} - -HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii) -{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii); -} - -int CCtrlClc::GetItemType(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0); -} - -HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlClc
+
+CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh };
+ switch (pnmh->code) {
+ case CLN_EXPANDED: OnExpanded(&evt); break;
+ case CLN_LISTREBUILT: OnListRebuilt(&evt); break;
+ case CLN_ITEMCHECKED: OnItemChecked(&evt); break;
+ case CLN_DRAGGING: OnDragging(&evt); break;
+ case CLN_DROPPED: OnDropped(&evt); break;
+ case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break;
+ case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break;
+ case CLN_DRAGSTOP: OnDragStop(&evt); break;
+ case CLN_NEWCONTACT: OnNewContact(&evt); break;
+ case CLN_CONTACTMOVED: OnContactMoved(&evt); break;
+ case NM_CLICK: OnClick(&evt); break;
+
+ case CLN_CHECKCHANGED:
+ OnCheckChanged(&evt);
+ NotifyChange();
+ break;
+ }
+ return FALSE;
+}
+
+void CCtrlClc::AddContact(MCONTACT hContact)
+{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0);
+}
+
+void CCtrlClc::AddGroup(HANDLE hGroup)
+{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0);
+}
+
+void CCtrlClc::AutoRebuild()
+{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0);
+}
+
+void CCtrlClc::DeleteItem(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::EditLabel(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::EndEditLabel(bool save)
+{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0);
+}
+
+void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk)
+{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE);
+}
+
+void CCtrlClc::Expand(HANDLE hItem, uint32_t flags)
+{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags);
+}
+
+HANDLE CCtrlClc::FindContact(MCONTACT hContact)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0);
+}
+
+HANDLE CCtrlClc::FindGroup(MGROUP hGroup)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0);
+}
+
+COLORREF CCtrlClc::GetBkColor() const
+{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0);
+}
+
+bool CCtrlClc::GetCheck(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false;
+}
+
+int CCtrlClc::GetCount() const
+{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0);
+}
+
+HWND CCtrlClc::GetEditControl() const
+{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0);
+}
+
+uint32_t CCtrlClc::GetExpand(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0);
+}
+
+int CCtrlClc::GetExtraColumns() const
+{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0);
+}
+
+uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const
+{
+ return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF);
+}
+
+HIMAGELIST CCtrlClc::GetExtraImageList() const
+{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0);
+}
+
+HFONT CCtrlClc::GetFont(int iFontId) const
+{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0);
+}
+
+HANDLE CCtrlClc::GetSelection() const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0);
+}
+
+HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y));
+}
+
+void CCtrlClc::SelectItem(HANDLE hItem)
+{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0);
+}
+
+void CCtrlClc::SetBkColor(COLORREF clBack)
+{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0);
+}
+
+void CCtrlClc::SetCheck(HANDLE hItem, bool check)
+{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0);
+}
+
+void CCtrlClc::SetExtraColumns(int iColumns)
+{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0);
+}
+
+void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage)
+{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage));
+}
+
+void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList)
+{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList);
+}
+
+void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw)
+{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId));
+}
+
+void CCtrlClc::SetItemText(HANDLE hItem, char *szText)
+{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText);
+}
+
+void CCtrlClc::SetHideEmptyGroups(bool state)
+{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0);
+}
+
+bool CCtrlClc::GetHideOfflineRoot() const
+{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false;
+}
+
+void CCtrlClc::SetHideOfflineRoot(bool state)
+{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9);
+}
+
+void CCtrlClc::SetUseGroups(bool state)
+{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0);
+}
+
+void CCtrlClc::SetOfflineModes(uint32_t modes)
+{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0);
+}
+
+uint32_t CCtrlClc::GetExStyle() const
+{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0);
+}
+
+void CCtrlClc::SetExStyle(uint32_t exStyle)
+{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0);
+}
+
+HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii)
+{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii);
+}
+
+int CCtrlClc::GetItemType(HANDLE hItem) const
+{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0);
+}
+
+HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const
+{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem);
+}
diff --git a/src/mir_core/src/Windows/CCtrlColor.cpp b/src/mir_core/src/Windows/CCtrlColor.cpp index 97f5e48e02..b61fb5760a 100644 --- a/src/mir_core/src/Windows/CCtrlColor.cpp +++ b/src/mir_core/src/Windows/CCtrlColor.cpp @@ -1,61 +1,61 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlColor class - -CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlColor::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetColor()); - return true; -} - -void CCtrlColor::OnReset() -{ - if (m_dbLink != nullptr) - SetColor(LoadInt()); -} - -uint32_t CCtrlColor::GetColor() -{ - return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0); -} - -void CCtrlColor::SetColor(uint32_t dwValue) -{ - ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlColor class
+
+CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) :
+ CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t)
+{
+ NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlColor::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetColor());
+ return true;
+}
+
+void CCtrlColor::OnReset()
+{
+ if (m_dbLink != nullptr)
+ SetColor(LoadInt());
+}
+
+uint32_t CCtrlColor::GetColor()
+{
+ return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0);
+}
+
+void CCtrlColor::SetColor(uint32_t dwValue)
+{
+ ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue);
+}
diff --git a/src/mir_core/src/Windows/CCtrlCombo.cpp b/src/mir_core/src/Windows/CCtrlCombo.cpp index 83cd721494..b935129b3f 100644 --- a/src/mir_core/src/Windows/CCtrlCombo.cpp +++ b/src/mir_core/src/Windows/CCtrlCombo.cpp @@ -1,185 +1,185 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCombo class - -CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlCombo::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - switch (idCode) { - case CBN_CLOSEUP: OnCloseup(this); break; - case CBN_DROPDOWN: OnDropdown(this); break; - case CBN_SELCHANGE: OnSelChanged(this); break; - case CBN_KILLFOCUS: OnKillFocus(this); break; - - case CBN_EDITCHANGE: - case CBN_EDITUPDATE: - case CBN_SELENDOK: - NotifyChange(); - break; - } - return TRUE; -} - -void CCtrlCombo::OnInit() -{ - CSuper::OnInit(); - OnReset(); -} - -bool CCtrlCombo::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - int len = GetWindowTextLength(m_hwnd) + 1; - wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - GetWindowText(m_hwnd, buf, len); - SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlCombo::OnReset() -{ - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -LPARAM CCtrlCombo::GetCurData() const -{ - return GetItemData(GetCurSel()); -} - -// selects line with userdata passed -int CCtrlCombo::SelectData(LPARAM data) -{ - int ret = -1, nCount = GetCount(); - - for (int i = 0; i < nCount; i++) - if (GetItemData(i) == data) { - ret = i; - break; - } - - return SetCurSel(ret); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Windows API - -int CCtrlCombo::AddString(const wchar_t *text, LPARAM data) -{ - int iItem = SendMessage(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text); - if (data) - SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); - return iItem; -} - -int CCtrlCombo::AddStringA(const char *text, LPARAM data) -{ - int iItem = SendMessageA(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text); - if (data) - SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); - return iItem; -} - -void CCtrlCombo::DeleteString(int index) -{ SendMessage(m_hwnd, CB_DELETESTRING, index, 0); -} - -int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact) -{ return SendMessage(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlCombo::FindStringA(const char *str, int index, bool exact) -{ return SendMessageA(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlCombo::GetCount() const -{ return SendMessage(m_hwnd, CB_GETCOUNT, 0, 0); -} - -int CCtrlCombo::GetCurSel() const -{ return SendMessage(m_hwnd, CB_GETCURSEL, 0, 0); -} - -bool CCtrlCombo::GetDroppedState() const -{ return SendMessage(m_hwnd, CB_GETDROPPEDSTATE, 0, 0) ? true : false; -} - -LPARAM CCtrlCombo::GetItemData(int index) const -{ return SendMessage(m_hwnd, CB_GETITEMDATA, index, 0); -} - -wchar_t* CCtrlCombo::GetItemText(int index) const -{ - wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result); - return result; -} - -wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const -{ - wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result); - mir_wstrncpy(buf, result, size); - return buf; -} - -int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data) -{ - int iItem = SendMessage(m_hwnd, CB_INSERTSTRING, pos, (LPARAM)text); - SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); - return iItem; -} - -void CCtrlCombo::ResetContent() -{ SendMessage(m_hwnd, CB_RESETCONTENT, 0, 0); -} - -int CCtrlCombo::SelectString(const wchar_t *str) -{ return SendMessage(m_hwnd, CB_SELECTSTRING, 0, (LPARAM)str); -} - -int CCtrlCombo::SetCurSel(int index) -{ return SendMessage(m_hwnd, CB_SETCURSEL, index, 0); -} - -void CCtrlCombo::SetItemData(int index, LPARAM data) -{ SendMessage(m_hwnd, CB_SETITEMDATA, index, data); -} - -void CCtrlCombo::ShowDropdown(bool show) -{ SendMessage(m_hwnd, CB_SHOWDROPDOWN, show ? TRUE : FALSE, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlCombo class
+
+CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlCombo::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ switch (idCode) {
+ case CBN_CLOSEUP: OnCloseup(this); break;
+ case CBN_DROPDOWN: OnDropdown(this); break;
+ case CBN_SELCHANGE: OnSelChanged(this); break;
+ case CBN_KILLFOCUS: OnKillFocus(this); break;
+
+ case CBN_EDITCHANGE:
+ case CBN_EDITUPDATE:
+ case CBN_SELENDOK:
+ NotifyChange();
+ break;
+ }
+ return TRUE;
+}
+
+void CCtrlCombo::OnInit()
+{
+ CSuper::OnInit();
+ OnReset();
+}
+
+bool CCtrlCombo::OnApply()
+{
+ CSuper::OnApply();
+
+ if (GetDataType() == DBVT_WCHAR) {
+ int len = GetWindowTextLength(m_hwnd) + 1;
+ wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len);
+ GetWindowText(m_hwnd, buf, len);
+ SaveText(buf);
+ }
+ else if (GetDataType() != DBVT_DELETED) {
+ SaveInt(GetInt());
+ }
+ return true;
+}
+
+void CCtrlCombo::OnReset()
+{
+ if (GetDataType() == DBVT_WCHAR)
+ SetText(LoadText());
+ else if (GetDataType() != DBVT_DELETED)
+ SetInt(LoadInt());
+}
+
+LPARAM CCtrlCombo::GetCurData() const
+{
+ return GetItemData(GetCurSel());
+}
+
+// selects line with userdata passed
+int CCtrlCombo::SelectData(LPARAM data)
+{
+ int ret = -1, nCount = GetCount();
+
+ for (int i = 0; i < nCount; i++)
+ if (GetItemData(i) == data) {
+ ret = i;
+ break;
+ }
+
+ return SetCurSel(ret);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Windows API
+
+int CCtrlCombo::AddString(const wchar_t *text, LPARAM data)
+{
+ int iItem = SendMessage(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text);
+ if (data)
+ SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data);
+ return iItem;
+}
+
+int CCtrlCombo::AddStringA(const char *text, LPARAM data)
+{
+ int iItem = SendMessageA(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text);
+ if (data)
+ SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data);
+ return iItem;
+}
+
+void CCtrlCombo::DeleteString(int index)
+{ SendMessage(m_hwnd, CB_DELETESTRING, index, 0);
+}
+
+int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact)
+{ return SendMessage(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str);
+}
+
+int CCtrlCombo::FindStringA(const char *str, int index, bool exact)
+{ return SendMessageA(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str);
+}
+
+int CCtrlCombo::GetCount() const
+{ return SendMessage(m_hwnd, CB_GETCOUNT, 0, 0);
+}
+
+int CCtrlCombo::GetCurSel() const
+{ return SendMessage(m_hwnd, CB_GETCURSEL, 0, 0);
+}
+
+bool CCtrlCombo::GetDroppedState() const
+{ return SendMessage(m_hwnd, CB_GETDROPPEDSTATE, 0, 0) ? true : false;
+}
+
+LPARAM CCtrlCombo::GetItemData(int index) const
+{ return SendMessage(m_hwnd, CB_GETITEMDATA, index, 0);
+}
+
+wchar_t* CCtrlCombo::GetItemText(int index) const
+{
+ wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result);
+ return result;
+}
+
+wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const
+{
+ wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result);
+ mir_wstrncpy(buf, result, size);
+ return buf;
+}
+
+int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data)
+{
+ int iItem = SendMessage(m_hwnd, CB_INSERTSTRING, pos, (LPARAM)text);
+ SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data);
+ return iItem;
+}
+
+void CCtrlCombo::ResetContent()
+{ SendMessage(m_hwnd, CB_RESETCONTENT, 0, 0);
+}
+
+int CCtrlCombo::SelectString(const wchar_t *str)
+{ return SendMessage(m_hwnd, CB_SELECTSTRING, 0, (LPARAM)str);
+}
+
+int CCtrlCombo::SetCurSel(int index)
+{ return SendMessage(m_hwnd, CB_SETCURSEL, index, 0);
+}
+
+void CCtrlCombo::SetItemData(int index, LPARAM data)
+{ SendMessage(m_hwnd, CB_SETITEMDATA, index, data);
+}
+
+void CCtrlCombo::ShowDropdown(bool show)
+{ SendMessage(m_hwnd, CB_SHOWDROPDOWN, show ? TRUE : FALSE, 0);
+}
diff --git a/src/mir_core/src/Windows/CCtrlData.cpp b/src/mir_core/src/Windows/CCtrlData.cpp index fab5ca0409..2a60d9d31a 100644 --- a/src/mir_core/src/Windows/CCtrlData.cpp +++ b/src/mir_core/src/Windows/CCtrlData.cpp @@ -1,52 +1,52 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlData class - -CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_dbLink(nullptr) -{} - -CCtrlData::~CCtrlData() -{ - delete m_dbLink; -} - -void CCtrlData::OnInit() -{ - CCtrlBase::OnInit(); - OnReset(); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlData class
+
+CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl),
+ m_dbLink(nullptr)
+{}
+
+CCtrlData::~CCtrlData()
+{
+ delete m_dbLink;
+}
+
+void CCtrlData::OnInit()
+{
+ CCtrlBase::OnInit();
+ OnReset();
+}
+
+void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue)
+{
+ m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue);
+}
+
+void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue)
+{
+ m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue);
+}
diff --git a/src/mir_core/src/Windows/CCtrlDate.cpp b/src/mir_core/src/Windows/CCtrlDate.cpp index 1967cb5678..253b549870 100644 --- a/src/mir_core/src/Windows/CCtrlDate.cpp +++ b/src/mir_core/src/Windows/CCtrlDate.cpp @@ -1,49 +1,49 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlDate class - -CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == DTN_DATETIMECHANGE) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -void CCtrlDate::GetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate); -} - -void CCtrlDate::SetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlDate class
+
+CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) :
+ CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh)
+{
+ if (pnmh->code == DTN_DATETIMECHANGE) {
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void CCtrlDate::GetTime(SYSTEMTIME *pDate)
+{
+ ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate);
+}
+
+void CCtrlDate::SetTime(SYSTEMTIME *pDate)
+{
+ ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate);
+}
diff --git a/src/mir_core/src/Windows/CCtrlEdit.cpp b/src/mir_core/src/Windows/CCtrlEdit.cpp index f035b5b14a..dd5bd2927e 100644 --- a/src/mir_core/src/Windows/CCtrlEdit.cpp +++ b/src/mir_core/src/Windows/CCtrlEdit.cpp @@ -1,68 +1,68 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlEdit class - -CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlEdit::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == EN_CHANGE) - NotifyChange(); - return TRUE; -} - -bool CCtrlEdit::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - int len = GetWindowTextLength(m_hwnd) + 1; - wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - GetWindowText(m_hwnd, buf, len); - SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlEdit::OnReset() -{ - m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0; - - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -void CCtrlEdit::SetMaxLength(unsigned int len) -{ - SendMsg(EM_SETLIMITTEXT, len, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlEdit class
+
+CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId)
+ : CCtrlData(dlg, ctrlId)
+{}
+
+BOOL CCtrlEdit::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ if (idCode == EN_CHANGE)
+ NotifyChange();
+ return TRUE;
+}
+
+bool CCtrlEdit::OnApply()
+{
+ CSuper::OnApply();
+
+ if (GetDataType() == DBVT_WCHAR) {
+ int len = GetWindowTextLength(m_hwnd) + 1;
+ wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len);
+ GetWindowText(m_hwnd, buf, len);
+ SaveText(buf);
+ }
+ else if (GetDataType() != DBVT_DELETED) {
+ SaveInt(GetInt());
+ }
+ return true;
+}
+
+void CCtrlEdit::OnReset()
+{
+ m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0;
+
+ if (GetDataType() == DBVT_WCHAR)
+ SetText(LoadText());
+ else if (GetDataType() != DBVT_DELETED)
+ SetInt(LoadInt());
+}
+
+void CCtrlEdit::SetMaxLength(unsigned int len)
+{
+ SendMsg(EM_SETLIMITTEXT, len, 0);
+}
diff --git a/src/mir_core/src/Windows/CCtrlHyperlink.cpp b/src/mir_core/src/Windows/CCtrlHyperlink.cpp index ca92d19dbc..0e0d93a689 100644 --- a/src/mir_core/src/Windows/CCtrlHyperlink.cpp +++ b/src/mir_core/src/Windows/CCtrlHyperlink.cpp @@ -1,54 +1,54 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlHyperlink - -CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url) - : CCtrlBase(wnd, idCtrl), - m_url(url) -{ - OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick); -} - -BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t) -{ - OnClick(this); - return FALSE; -} - -void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*) -{ - ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW); -} - -void CCtrlHyperlink::SetUrl(const char *url) -{ - m_url = url; -} - -const char* CCtrlHyperlink::GetUrl() -{ - return m_url; -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlHyperlink
+
+CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url)
+ : CCtrlBase(wnd, idCtrl),
+ m_url(url)
+{
+ OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick);
+}
+
+BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t)
+{
+ OnClick(this);
+ return FALSE;
+}
+
+void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*)
+{
+ ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW);
+}
+
+void CCtrlHyperlink::SetUrl(const char *url)
+{
+ m_url = url;
+}
+
+const char* CCtrlHyperlink::GetUrl()
+{
+ return m_url;
+}
diff --git a/src/mir_core/src/Windows/CCtrlLabel.cpp b/src/mir_core/src/Windows/CCtrlLabel.cpp index fd460fdf8b..8e1de5e33f 100644 --- a/src/mir_core/src/Windows/CCtrlLabel.cpp +++ b/src/mir_core/src/Windows/CCtrlLabel.cpp @@ -1,31 +1,31 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlLabel - -CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} - +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlLabel
+
+CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{}
+
diff --git a/src/mir_core/src/Windows/CCtrlListBox.cpp b/src/mir_core/src/Windows/CCtrlListBox.cpp index abaa31a786..6d9f58fc23 100644 --- a/src/mir_core/src/Windows/CCtrlListBox.cpp +++ b/src/mir_core/src/Windows/CCtrlListBox.cpp @@ -1,160 +1,160 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListBox class - -CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - switch (idCode) { - case LBN_DBLCLK: OnDblClick(this); break; - case LBN_SELCANCEL: OnSelCancel(this); break; - case LBN_SELCHANGE: OnSelChange(this); break; - } - return TRUE; -} - -void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetCurSel(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - - CSuper::GetCaretPos(pos); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CCtrlListBox::AddString(const wchar_t *text, LPARAM data) -{ - int iItem = ListBox_AddString(m_hwnd, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::DeleteString(int index) -{ ListBox_DeleteString(m_hwnd, index); -} - -int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact) -{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlListBox::GetCount() const -{ return ListBox_GetCount(m_hwnd); -} - -int CCtrlListBox::GetCurSel() const -{ return ListBox_GetCurSel(m_hwnd); -} - -LPARAM CCtrlListBox::GetItemData(int index) const -{ return ListBox_GetItemData(m_hwnd, index); -} - -int CCtrlListBox::GetItemRect(int index, RECT *pResult) const -{ return ListBox_GetItemRect(m_hwnd, index, pResult); -} - -wchar_t* CCtrlListBox::GetItemText(int index) const -{ - wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - return result; -} - -wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const -{ - wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - mir_wstrncpy(buf, result, size); - return buf; -} - -bool CCtrlListBox::GetSel(int index) const -{ return ListBox_GetSel(m_hwnd, index) ? true : false; -} - -int CCtrlListBox::GetSelCount() const -{ return ListBox_GetSelCount(m_hwnd); -} - -int* CCtrlListBox::GetSelItems(int *items, int count) const -{ - ListBox_GetSelItems(m_hwnd, count, items); - return items; -} - -int* CCtrlListBox::GetSelItems() const -{ - int count = GetSelCount() + 1; - int *result = (int *)mir_alloc(sizeof(int) * count); - ListBox_GetSelItems(m_hwnd, count, result); - result[count-1] = -1; - return result; -} - -int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data) -{ - int iItem = ListBox_InsertString(m_hwnd, pos, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::ResetContent() -{ ListBox_ResetContent(m_hwnd); -} - -int CCtrlListBox::SelectString(const wchar_t *str) -{ return ListBox_SelectString(m_hwnd, 0, str); -} - -int CCtrlListBox::SetCurSel(int index) -{ return ListBox_SetCurSel(m_hwnd, index); -} - -void CCtrlListBox::SetItemData(int index, LPARAM data) -{ ListBox_SetItemData(m_hwnd, index, data); -} - -void CCtrlListBox::SetItemHeight(int index, int iHeight) -{ ListBox_SetItemHeight(m_hwnd, index, iHeight); -} - -void CCtrlListBox::SetSel(int index, bool sel) -{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListBox class
+
+CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ switch (idCode) {
+ case LBN_DBLCLK: OnDblClick(this); break;
+ case LBN_SELCANCEL: OnSelCancel(this); break;
+ case LBN_SELCHANGE: OnSelChange(this); break;
+ }
+ return TRUE;
+}
+
+void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+ if (pos.pt.x == 0 && pos.pt.y == 0) {
+ pos.iCurr = GetCurSel();
+ if (pos.iCurr != -1) {
+ RECT rc;
+ GetItemRect(pos.iCurr, &rc);
+ pos.pt.x = rc.left + 8;
+ pos.pt.y = rc.top + 8;
+ ClientToScreen(m_hwnd, &pos.pt);
+ return;
+ }
+ }
+
+ CSuper::GetCaretPos(pos);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CCtrlListBox::AddString(const wchar_t *text, LPARAM data)
+{
+ int iItem = ListBox_AddString(m_hwnd, text);
+ ListBox_SetItemData(m_hwnd, iItem, data);
+ return iItem;
+}
+
+void CCtrlListBox::DeleteString(int index)
+{ ListBox_DeleteString(m_hwnd, index);
+}
+
+int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact)
+{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str);
+}
+
+int CCtrlListBox::GetCount() const
+{ return ListBox_GetCount(m_hwnd);
+}
+
+int CCtrlListBox::GetCurSel() const
+{ return ListBox_GetCurSel(m_hwnd);
+}
+
+LPARAM CCtrlListBox::GetItemData(int index) const
+{ return ListBox_GetItemData(m_hwnd, index);
+}
+
+int CCtrlListBox::GetItemRect(int index, RECT *pResult) const
+{ return ListBox_GetItemRect(m_hwnd, index, pResult);
+}
+
+wchar_t* CCtrlListBox::GetItemText(int index) const
+{
+ wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result);
+ return result;
+}
+
+wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const
+{
+ wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1));
+ SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result);
+ mir_wstrncpy(buf, result, size);
+ return buf;
+}
+
+bool CCtrlListBox::GetSel(int index) const
+{ return ListBox_GetSel(m_hwnd, index) ? true : false;
+}
+
+int CCtrlListBox::GetSelCount() const
+{ return ListBox_GetSelCount(m_hwnd);
+}
+
+int* CCtrlListBox::GetSelItems(int *items, int count) const
+{
+ ListBox_GetSelItems(m_hwnd, count, items);
+ return items;
+}
+
+int* CCtrlListBox::GetSelItems() const
+{
+ int count = GetSelCount() + 1;
+ int *result = (int *)mir_alloc(sizeof(int) * count);
+ ListBox_GetSelItems(m_hwnd, count, result);
+ result[count-1] = -1;
+ return result;
+}
+
+int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data)
+{
+ int iItem = ListBox_InsertString(m_hwnd, pos, text);
+ ListBox_SetItemData(m_hwnd, iItem, data);
+ return iItem;
+}
+
+void CCtrlListBox::ResetContent()
+{ ListBox_ResetContent(m_hwnd);
+}
+
+int CCtrlListBox::SelectString(const wchar_t *str)
+{ return ListBox_SelectString(m_hwnd, 0, str);
+}
+
+int CCtrlListBox::SetCurSel(int index)
+{ return ListBox_SetCurSel(m_hwnd, index);
+}
+
+void CCtrlListBox::SetItemData(int index, LPARAM data)
+{ ListBox_SetItemData(m_hwnd, index, data);
+}
+
+void CCtrlListBox::SetItemHeight(int index, int iHeight)
+{ ListBox_SetItemHeight(m_hwnd, index, iHeight);
+}
+
+void CCtrlListBox::SetSel(int index, bool sel)
+{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index);
+}
diff --git a/src/mir_core/src/Windows/CCtrlListView.cpp b/src/mir_core/src/Windows/CCtrlListView.cpp index eb57951a3c..40bb1f481e 100644 --- a/src/mir_core/src/Windows/CCtrlListView.cpp +++ b/src/mir_core/src/Windows/CCtrlListView.cpp @@ -1,551 +1,551 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListView - -CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, pnmh }; - - switch (pnmh->code) { - case NM_CLICK: OnClick(&evt); return TRUE; - case NM_DBLCLK: OnDoubleClick(&evt); return TRUE; - case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; - case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE; - case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; - case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; - case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE; - case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE; - case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE; - case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; - case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; - case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE; - case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; - case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; - case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE; - case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE; - case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE; - case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE; - case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE; - case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE; - case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; - - case LVN_ITEMCHANGED: - if (!m_parentWnd || !m_parentWnd->IsInitialized()) - return FALSE; - - OnItemChanged(&evt); - - // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes - if (evt.nmlv->uChanged & LVIF_STATE) - if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0) - NotifyChange(); - return TRUE; - - case LVN_ODSTATECHANGED: - NotifyChange(); - return TRUE; - } - - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param) -{ - int result = l1 - l2; - int newItem = HIWORD(param); - int oldItem = LOWORD(param); - if (newItem > oldItem) - return (l1 == oldItem && l2 <= newItem) ? 1 : result; - - return (l2 == oldItem && l1 >= newItem) ? 1 : result; -} - -int CCtrlListView::MoveItem(int idx, int direction) -{ - if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0)) - return idx; - - if (idx < 0) - idx = GetNextItem(-1, LVNI_FOCUSED); - SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction)); - return idx + direction; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CCtrlListView::SetCurSel(int idx) -{ - SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); -} - -// additional api -HIMAGELIST CCtrlListView::CreateImageList(int iImageList) -{ - HIMAGELIST hIml = GetImageList(iImageList); - if (hIml) - return hIml; - - hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - SetImageList(hIml, iImageList); - return hIml; -} - -void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx) -{ - LVCOLUMN lvc; - lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc.iImage = 0; - lvc.pszText = (LPWSTR)name; - lvc.cx = cx; - lvc.iSubItem = iSubItem; - InsertColumn(iSubItem, &lvc); -} - -void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name) -{ - LVGROUP lvg = { 0 }; - lvg.cbSize = sizeof(lvg); - lvg.mask = LVGF_HEADER | LVGF_GROUPID; - lvg.pszHeader = (LPWSTR)name; - lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader); - lvg.iGroupId = iGroupId; - InsertGroup(-1, &lvg); -} - -int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE; - lvi.iSubItem = 0; - lvi.pszText = (LPWSTR)text; - lvi.iImage = iIcon; - lvi.lParam = lParam; - if (iGroupId >= 0) { - lvi.mask |= LVIF_GROUPID; - lvi.iGroupId = iGroupId; - } - - return InsertItem(&lvi); -} - -void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_TEXT; - lvi.iItem = iItem; - lvi.iSubItem = iSubItem; - lvi.pszText = (LPWSTR)text; - if (iIcon >= 0) { - lvi.mask |= LVIF_IMAGE; - lvi.iImage = iIcon; - } - - SetItem(&lvi); -} - -LPARAM CCtrlListView::GetItemData(int iItem) const -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM; - lvi.iItem = iItem; - return GetItem(&lvi) ? lvi.lParam : -1; -} - -void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - - // position is empty, let's fill it using selection - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetSelectionMark(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc, TRUE); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - // position is present, let's calculate current item - else { - LVHITTESTINFO hti; - hti.pt = pos.pt; - ScreenToClient(m_hwnd, &hti.pt); - if (SubItemHitTest(&hti) != -1) { - pos.iCurr = hti.iItem; - return; - } - } - CSuper::GetCaretPos(pos); -} - -// classic api -uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount) -{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount); -} -void CCtrlListView::Arrange(UINT code) -{ ListView_Arrange(m_hwnd, code); -} -void CCtrlListView::CancelEditLabel() -{ ListView_CancelEditLabel(m_hwnd); -} -HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft) -{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft); -} -void CCtrlListView::DeleteAllItems() -{ ListView_DeleteAllItems(m_hwnd); -} -void CCtrlListView::DeleteColumn(int iCol) -{ ListView_DeleteColumn(m_hwnd, iCol); -} -void CCtrlListView::DeleteItem(int iItem) -{ ListView_DeleteItem(m_hwnd, iItem); -} -HWND CCtrlListView::EditLabel(int iItem) -{ return ListView_EditLabel(m_hwnd, iItem); -} -int CCtrlListView::EnableGroupView(BOOL fEnable) -{ return ListView_EnableGroupView(m_hwnd, fEnable); -} -BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK) -{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK); -} -int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi) -{ return ListView_FindItem(m_hwnd, iStart, plvfi); -} -COLORREF CCtrlListView::GetBkColor() const -{ return ListView_GetBkColor(m_hwnd); -} -void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const -{ ListView_GetBkImage(m_hwnd, plvbki); -} -UINT CCtrlListView::GetCallbackMask() const -{ return ListView_GetCallbackMask(m_hwnd); -} -BOOL CCtrlListView::GetCheckState(UINT iIndex) const -{ return ListView_GetCheckState(m_hwnd, iIndex); -} -void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const -{ ListView_GetColumn(m_hwnd, iCol, pcol); -} -void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const -{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -int CCtrlListView::GetColumnWidth(int iCol) const -{ return ListView_GetColumnWidth(m_hwnd, iCol); -} -int CCtrlListView::GetCountPerPage() const -{ return ListView_GetCountPerPage(m_hwnd); -} -HWND CCtrlListView::GetEditControl() const -{ return ListView_GetEditControl(m_hwnd); -} -uint32_t CCtrlListView::GetExtendedListViewStyle() const -{ return ListView_GetExtendedListViewStyle(m_hwnd); -} -void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const -{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics); -} -HWND CCtrlListView::GetHeader() const -{ return ListView_GetHeader(m_hwnd); -} -HCURSOR CCtrlListView::GetHotCursor() const -{ return ListView_GetHotCursor(m_hwnd); -} -INT CCtrlListView::GetHotItem() const -{ return ListView_GetHotItem(m_hwnd); -} -uint32_t CCtrlListView::GetHoverTime() const -{ return ListView_GetHoverTime(m_hwnd); -} -HIMAGELIST CCtrlListView::GetImageList(int iImageList) const -{ return ListView_GetImageList(m_hwnd, iImageList); -} -BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const -{ return ListView_GetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::GetInsertMarkColor() const -{ return ListView_GetInsertMarkColor(m_hwnd); -} -int CCtrlListView::GetInsertMarkRect(LPRECT prc) const -{ return ListView_GetInsertMarkRect(m_hwnd, prc); -} -BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const -{ return ListView_GetISearchString(m_hwnd, lpsz); -} -bool CCtrlListView::GetItem(LPLVITEM pitem) const -{ return ListView_GetItem(m_hwnd, pitem) == TRUE; -} -int CCtrlListView::GetItemCount() const -{ return ListView_GetItemCount(m_hwnd); -} -void CCtrlListView::GetItemPosition(int i, POINT *ppt) const -{ ListView_GetItemPosition(m_hwnd, i, ppt); -} -void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const -{ ListView_GetItemRect(m_hwnd, i, prc, code); -} -uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const -{ return ListView_GetItemSpacing(m_hwnd, fSmall); -} -UINT CCtrlListView::GetItemState(int i, UINT mask) const -{ return ListView_GetItemState(m_hwnd, i, mask); -} -void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const -{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax); -} -int CCtrlListView::GetNextItem(int iStart, UINT flags) const -{ return ListView_GetNextItem(m_hwnd, iStart, flags); -} -BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const -{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas); -} -BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const -{ return ListView_GetOrigin(m_hwnd, lpptOrg); -} -COLORREF CCtrlListView::GetOutlineColor() const -{ return ListView_GetOutlineColor(m_hwnd); -} -UINT CCtrlListView::GetSelectedColumn() const -{ return ListView_GetSelectedColumn(m_hwnd); -} -UINT CCtrlListView::GetSelectedCount() const -{ return ListView_GetSelectedCount(m_hwnd); -} -INT CCtrlListView::GetSelectionMark() const -{ return ListView_GetSelectionMark(m_hwnd); -} -int CCtrlListView::GetStringWidth(LPCSTR psz) const -{ return ListView_GetStringWidth(m_hwnd, psz); -} -BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const -{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect); -} -COLORREF CCtrlListView::GetTextBkColor() const -{ return ListView_GetTextBkColor(m_hwnd); -} -COLORREF CCtrlListView::GetTextColor() const -{ return ListView_GetTextColor(m_hwnd); -} -void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const -{ ListView_GetTileInfo(m_hwnd, plvtinfo); -} -void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const -{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::GetToolTips() const -{ return ListView_GetToolTips(m_hwnd); -} -int CCtrlListView::GetTopIndex() const -{ return ListView_GetTopIndex(m_hwnd); -} -BOOL CCtrlListView::GetUnicodeFormat() const -{ return ListView_GetUnicodeFormat(m_hwnd); -} -uint32_t CCtrlListView::GetView() const -{ return ListView_GetView(m_hwnd); -} -BOOL CCtrlListView::GetViewRect(RECT *prc) const -{ return ListView_GetViewRect(m_hwnd, prc); -} -void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const -{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -BOOL CCtrlListView::HasGroup(int dwGroupId) -{ return ListView_HasGroup(m_hwnd, dwGroupId); -} -int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const -{ return ListView_HitTest(m_hwnd, pinfo); -} -int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol) -{ return ListView_InsertColumn(m_hwnd, iCol, pcol); -} -int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp) -{ return ListView_InsertGroup(m_hwnd, index, pgrp); -} -void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert) -{ ListView_InsertGroupSorted(m_hwnd, structInsert); -} -int CCtrlListView::InsertItem(const LVITEM *pitem) -{ return ListView_InsertItem(m_hwnd, pitem); -} -BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim) -{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim); -} -BOOL CCtrlListView::IsGroupViewEnabled() -{ return ListView_IsGroupViewEnabled(m_hwnd); -} -UINT CCtrlListView::MapIDToIndex(UINT id) -{ return ListView_MapIDToIndex(m_hwnd, id); -} -UINT CCtrlListView::MapIndexToID(UINT index) -{ return ListView_MapIndexToID(m_hwnd, index); -} -BOOL CCtrlListView::RedrawItems(int iFirst, int iLast) -{ return ListView_RedrawItems(m_hwnd, iFirst, iLast); -} -void CCtrlListView::RemoveAllGroups() -{ ListView_RemoveAllGroups(m_hwnd); -} -int CCtrlListView::RemoveGroup(int iGroupId) -{ return ListView_RemoveGroup(m_hwnd, iGroupId); -} -BOOL CCtrlListView::Scroll(int dx, int dy) -{ return ListView_Scroll(m_hwnd, dx, dy); -} -BOOL CCtrlListView::SetBkColor(COLORREF clrBk) -{ return ListView_SetBkColor(m_hwnd, clrBk); -} -BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki) -{ return ListView_SetBkImage(m_hwnd, plvbki); -} -BOOL CCtrlListView::SetCallbackMask(UINT mask) -{ return ListView_SetCallbackMask(m_hwnd, mask); -} -void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck) -{ ListView_SetCheckState(m_hwnd, iIndex, fCheck); -} -BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol) -{ return ListView_SetColumn(m_hwnd, iCol, pcol); -} -BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray) -{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -BOOL CCtrlListView::SetColumnWidth(int iCol, int cx) -{ return ListView_SetColumnWidth(m_hwnd, iCol, cx); -} -void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle); -} -void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle); -} -int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp) -{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp); -} -void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) -{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics); -} -HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor) -{ return ListView_SetHotCursor(m_hwnd, hCursor); -} -INT CCtrlListView::SetHotItem(INT iIndex) -{ return ListView_SetHotItem(m_hwnd, iIndex); -} -void CCtrlListView::SetHoverTime(uint32_t dwHoverTime) -{ ListView_SetHoverTime(m_hwnd, dwHoverTime); -} -uint32_t CCtrlListView::SetIconSpacing(int cx, int cy) -{ return ListView_SetIconSpacing(m_hwnd, cx, cy); -} -HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList) -{ return ListView_SetImageList(m_hwnd, himl, iImageList); -} -BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip) -{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip); -} -BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim) -{ return ListView_SetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color) -{ return ListView_SetInsertMarkColor(m_hwnd, color); -} -BOOL CCtrlListView::SetItem(const LVITEM *pitem) -{ return ListView_SetItem(m_hwnd, pitem); -} -void CCtrlListView::SetItemCount(int cItems) -{ ListView_SetItemCount(m_hwnd, cItems); -} -void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags) -{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags); -} -BOOL CCtrlListView::SetItemPosition(int i, int x, int y) -{ return ListView_SetItemPosition(m_hwnd, i, x, y); -} -void CCtrlListView::SetItemPosition32(int iItem, int x, int y) -{ ListView_SetItemPosition32(m_hwnd, iItem, x, y); -} -void CCtrlListView::SetItemState(int i, UINT state, UINT mask) -{ ListView_SetItemState(m_hwnd, i, state, mask); -} -void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText) -{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText); -} -COLORREF CCtrlListView::SetOutlineColor(COLORREF color) -{ return ListView_SetOutlineColor(m_hwnd, color); -} -void CCtrlListView::SetSelectedColumn(int iCol) -{ ListView_SetSelectedColumn(m_hwnd, iCol); -} -INT CCtrlListView::SetSelectionMark(INT iIndex) -{ return ListView_SetSelectionMark(m_hwnd, iIndex); -} -BOOL CCtrlListView::SetTextBkColor(COLORREF clrText) -{ return ListView_SetTextBkColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTextColor(COLORREF clrText) -{ return ListView_SetTextColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo) -{ return ListView_SetTileInfo(m_hwnd, plvtinfo); -} -BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) -{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::SetToolTips(HWND ToolTip) -{ return ListView_SetToolTips(m_hwnd, ToolTip); -} -BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode) -{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode); -} -int CCtrlListView::SetView(uint32_t iView) -{ return ListView_SetView(m_hwnd, iView); -} -void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc) -{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv) -{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv); -} -BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort); -} -BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort); -} -INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const -{ return ListView_SubItemHitTest(m_hwnd, pInfo); -} -BOOL CCtrlListView::Update(int iItem) -{ return ListView_Update(m_hwnd, iItem); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlListView
+
+CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId)
+{}
+
+BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh)
+{
+ TEventInfo evt = { this, pnmh };
+
+ switch (pnmh->code) {
+ case NM_CLICK: OnClick(&evt); return TRUE;
+ case NM_DBLCLK: OnDoubleClick(&evt); return TRUE;
+ case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE;
+ case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE;
+ case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE;
+ case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE;
+ case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE;
+ case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE;
+ case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE;
+ case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE;
+ case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE;
+ case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE;
+ case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE;
+ case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE;
+ case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE;
+ case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE;
+ case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE;
+ case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE;
+ case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE;
+ case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE;
+ case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE;
+
+ case LVN_ITEMCHANGED:
+ if (!m_parentWnd || !m_parentWnd->IsInitialized())
+ return FALSE;
+
+ OnItemChanged(&evt);
+
+ // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes
+ if (evt.nmlv->uChanged & LVIF_STATE)
+ if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0)
+ NotifyChange();
+ return TRUE;
+
+ case LVN_ODSTATECHANGED:
+ NotifyChange();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param)
+{
+ int result = l1 - l2;
+ int newItem = HIWORD(param);
+ int oldItem = LOWORD(param);
+ if (newItem > oldItem)
+ return (l1 == oldItem && l2 <= newItem) ? 1 : result;
+
+ return (l2 == oldItem && l1 >= newItem) ? 1 : result;
+}
+
+int CCtrlListView::MoveItem(int idx, int direction)
+{
+ if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0))
+ return idx;
+
+ if (idx < 0)
+ idx = GetNextItem(-1, LVNI_FOCUSED);
+ SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction));
+ return idx + direction;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CCtrlListView::SetCurSel(int idx)
+{
+ SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
+}
+
+// additional api
+HIMAGELIST CCtrlListView::CreateImageList(int iImageList)
+{
+ HIMAGELIST hIml = GetImageList(iImageList);
+ if (hIml)
+ return hIml;
+
+ hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);
+ SetImageList(hIml, iImageList);
+ return hIml;
+}
+
+void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx)
+{
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
+ lvc.iImage = 0;
+ lvc.pszText = (LPWSTR)name;
+ lvc.cx = cx;
+ lvc.iSubItem = iSubItem;
+ InsertColumn(iSubItem, &lvc);
+}
+
+void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name)
+{
+ LVGROUP lvg = { 0 };
+ lvg.cbSize = sizeof(lvg);
+ lvg.mask = LVGF_HEADER | LVGF_GROUPID;
+ lvg.pszHeader = (LPWSTR)name;
+ lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader);
+ lvg.iGroupId = iGroupId;
+ InsertGroup(-1, &lvg);
+}
+
+int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId)
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE;
+ lvi.iSubItem = 0;
+ lvi.pszText = (LPWSTR)text;
+ lvi.iImage = iIcon;
+ lvi.lParam = lParam;
+ if (iGroupId >= 0) {
+ lvi.mask |= LVIF_GROUPID;
+ lvi.iGroupId = iGroupId;
+ }
+
+ return InsertItem(&lvi);
+}
+
+void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon)
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = iItem;
+ lvi.iSubItem = iSubItem;
+ lvi.pszText = (LPWSTR)text;
+ if (iIcon >= 0) {
+ lvi.mask |= LVIF_IMAGE;
+ lvi.iImage = iIcon;
+ }
+
+ SetItem(&lvi);
+}
+
+LPARAM CCtrlListView::GetItemData(int iItem) const
+{
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = iItem;
+ return GetItem(&lvi) ? lvi.lParam : -1;
+}
+
+void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const
+{
+ pos.pCtrl = this;
+
+ // position is empty, let's fill it using selection
+ if (pos.pt.x == 0 && pos.pt.y == 0) {
+ pos.iCurr = GetSelectionMark();
+ if (pos.iCurr != -1) {
+ RECT rc;
+ GetItemRect(pos.iCurr, &rc, TRUE);
+ pos.pt.x = rc.left + 8;
+ pos.pt.y = rc.top + 8;
+ ClientToScreen(m_hwnd, &pos.pt);
+ return;
+ }
+ }
+ // position is present, let's calculate current item
+ else {
+ LVHITTESTINFO hti;
+ hti.pt = pos.pt;
+ ScreenToClient(m_hwnd, &hti.pt);
+ if (SubItemHitTest(&hti) != -1) {
+ pos.iCurr = hti.iItem;
+ return;
+ }
+ }
+ CSuper::GetCaretPos(pos);
+}
+
+// classic api
+uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount)
+{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount);
+}
+void CCtrlListView::Arrange(UINT code)
+{ ListView_Arrange(m_hwnd, code);
+}
+void CCtrlListView::CancelEditLabel()
+{ ListView_CancelEditLabel(m_hwnd);
+}
+HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft)
+{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft);
+}
+void CCtrlListView::DeleteAllItems()
+{ ListView_DeleteAllItems(m_hwnd);
+}
+void CCtrlListView::DeleteColumn(int iCol)
+{ ListView_DeleteColumn(m_hwnd, iCol);
+}
+void CCtrlListView::DeleteItem(int iItem)
+{ ListView_DeleteItem(m_hwnd, iItem);
+}
+HWND CCtrlListView::EditLabel(int iItem)
+{ return ListView_EditLabel(m_hwnd, iItem);
+}
+int CCtrlListView::EnableGroupView(BOOL fEnable)
+{ return ListView_EnableGroupView(m_hwnd, fEnable);
+}
+BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK)
+{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK);
+}
+int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi)
+{ return ListView_FindItem(m_hwnd, iStart, plvfi);
+}
+COLORREF CCtrlListView::GetBkColor() const
+{ return ListView_GetBkColor(m_hwnd);
+}
+void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const
+{ ListView_GetBkImage(m_hwnd, plvbki);
+}
+UINT CCtrlListView::GetCallbackMask() const
+{ return ListView_GetCallbackMask(m_hwnd);
+}
+BOOL CCtrlListView::GetCheckState(UINT iIndex) const
+{ return ListView_GetCheckState(m_hwnd, iIndex);
+}
+void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const
+{ ListView_GetColumn(m_hwnd, iCol, pcol);
+}
+void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const
+{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray);
+}
+int CCtrlListView::GetColumnWidth(int iCol) const
+{ return ListView_GetColumnWidth(m_hwnd, iCol);
+}
+int CCtrlListView::GetCountPerPage() const
+{ return ListView_GetCountPerPage(m_hwnd);
+}
+HWND CCtrlListView::GetEditControl() const
+{ return ListView_GetEditControl(m_hwnd);
+}
+uint32_t CCtrlListView::GetExtendedListViewStyle() const
+{ return ListView_GetExtendedListViewStyle(m_hwnd);
+}
+void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const
+{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics);
+}
+HWND CCtrlListView::GetHeader() const
+{ return ListView_GetHeader(m_hwnd);
+}
+HCURSOR CCtrlListView::GetHotCursor() const
+{ return ListView_GetHotCursor(m_hwnd);
+}
+INT CCtrlListView::GetHotItem() const
+{ return ListView_GetHotItem(m_hwnd);
+}
+uint32_t CCtrlListView::GetHoverTime() const
+{ return ListView_GetHoverTime(m_hwnd);
+}
+HIMAGELIST CCtrlListView::GetImageList(int iImageList) const
+{ return ListView_GetImageList(m_hwnd, iImageList);
+}
+BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const
+{ return ListView_GetInsertMark(m_hwnd, plvim);
+}
+COLORREF CCtrlListView::GetInsertMarkColor() const
+{ return ListView_GetInsertMarkColor(m_hwnd);
+}
+int CCtrlListView::GetInsertMarkRect(LPRECT prc) const
+{ return ListView_GetInsertMarkRect(m_hwnd, prc);
+}
+BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const
+{ return ListView_GetISearchString(m_hwnd, lpsz);
+}
+bool CCtrlListView::GetItem(LPLVITEM pitem) const
+{ return ListView_GetItem(m_hwnd, pitem) == TRUE;
+}
+int CCtrlListView::GetItemCount() const
+{ return ListView_GetItemCount(m_hwnd);
+}
+void CCtrlListView::GetItemPosition(int i, POINT *ppt) const
+{ ListView_GetItemPosition(m_hwnd, i, ppt);
+}
+void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const
+{ ListView_GetItemRect(m_hwnd, i, prc, code);
+}
+uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const
+{ return ListView_GetItemSpacing(m_hwnd, fSmall);
+}
+UINT CCtrlListView::GetItemState(int i, UINT mask) const
+{ return ListView_GetItemState(m_hwnd, i, mask);
+}
+void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const
+{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax);
+}
+int CCtrlListView::GetNextItem(int iStart, UINT flags) const
+{ return ListView_GetNextItem(m_hwnd, iStart, flags);
+}
+BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const
+{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas);
+}
+BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const
+{ return ListView_GetOrigin(m_hwnd, lpptOrg);
+}
+COLORREF CCtrlListView::GetOutlineColor() const
+{ return ListView_GetOutlineColor(m_hwnd);
+}
+UINT CCtrlListView::GetSelectedColumn() const
+{ return ListView_GetSelectedColumn(m_hwnd);
+}
+UINT CCtrlListView::GetSelectedCount() const
+{ return ListView_GetSelectedCount(m_hwnd);
+}
+INT CCtrlListView::GetSelectionMark() const
+{ return ListView_GetSelectionMark(m_hwnd);
+}
+int CCtrlListView::GetStringWidth(LPCSTR psz) const
+{ return ListView_GetStringWidth(m_hwnd, psz);
+}
+BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const
+{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect);
+}
+COLORREF CCtrlListView::GetTextBkColor() const
+{ return ListView_GetTextBkColor(m_hwnd);
+}
+COLORREF CCtrlListView::GetTextColor() const
+{ return ListView_GetTextColor(m_hwnd);
+}
+void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const
+{ ListView_GetTileInfo(m_hwnd, plvtinfo);
+}
+void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const
+{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo);
+}
+HWND CCtrlListView::GetToolTips() const
+{ return ListView_GetToolTips(m_hwnd);
+}
+int CCtrlListView::GetTopIndex() const
+{ return ListView_GetTopIndex(m_hwnd);
+}
+BOOL CCtrlListView::GetUnicodeFormat() const
+{ return ListView_GetUnicodeFormat(m_hwnd);
+}
+uint32_t CCtrlListView::GetView() const
+{ return ListView_GetView(m_hwnd);
+}
+BOOL CCtrlListView::GetViewRect(RECT *prc) const
+{ return ListView_GetViewRect(m_hwnd, prc);
+}
+void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const
+{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc);
+}
+BOOL CCtrlListView::HasGroup(int dwGroupId)
+{ return ListView_HasGroup(m_hwnd, dwGroupId);
+}
+int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const
+{ return ListView_HitTest(m_hwnd, pinfo);
+}
+int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol)
+{ return ListView_InsertColumn(m_hwnd, iCol, pcol);
+}
+int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp)
+{ return ListView_InsertGroup(m_hwnd, index, pgrp);
+}
+void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert)
+{ ListView_InsertGroupSorted(m_hwnd, structInsert);
+}
+int CCtrlListView::InsertItem(const LVITEM *pitem)
+{ return ListView_InsertItem(m_hwnd, pitem);
+}
+BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim)
+{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim);
+}
+BOOL CCtrlListView::IsGroupViewEnabled()
+{ return ListView_IsGroupViewEnabled(m_hwnd);
+}
+UINT CCtrlListView::MapIDToIndex(UINT id)
+{ return ListView_MapIDToIndex(m_hwnd, id);
+}
+UINT CCtrlListView::MapIndexToID(UINT index)
+{ return ListView_MapIndexToID(m_hwnd, index);
+}
+BOOL CCtrlListView::RedrawItems(int iFirst, int iLast)
+{ return ListView_RedrawItems(m_hwnd, iFirst, iLast);
+}
+void CCtrlListView::RemoveAllGroups()
+{ ListView_RemoveAllGroups(m_hwnd);
+}
+int CCtrlListView::RemoveGroup(int iGroupId)
+{ return ListView_RemoveGroup(m_hwnd, iGroupId);
+}
+BOOL CCtrlListView::Scroll(int dx, int dy)
+{ return ListView_Scroll(m_hwnd, dx, dy);
+}
+BOOL CCtrlListView::SetBkColor(COLORREF clrBk)
+{ return ListView_SetBkColor(m_hwnd, clrBk);
+}
+BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki)
+{ return ListView_SetBkImage(m_hwnd, plvbki);
+}
+BOOL CCtrlListView::SetCallbackMask(UINT mask)
+{ return ListView_SetCallbackMask(m_hwnd, mask);
+}
+void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck)
+{ ListView_SetCheckState(m_hwnd, iIndex, fCheck);
+}
+BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol)
+{ return ListView_SetColumn(m_hwnd, iCol, pcol);
+}
+BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray)
+{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray);
+}
+BOOL CCtrlListView::SetColumnWidth(int iCol, int cx)
+{ return ListView_SetColumnWidth(m_hwnd, iCol, cx);
+}
+void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle)
+{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle);
+}
+void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle)
+{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle);
+}
+int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp)
+{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp);
+}
+void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics)
+{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics);
+}
+HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor)
+{ return ListView_SetHotCursor(m_hwnd, hCursor);
+}
+INT CCtrlListView::SetHotItem(INT iIndex)
+{ return ListView_SetHotItem(m_hwnd, iIndex);
+}
+void CCtrlListView::SetHoverTime(uint32_t dwHoverTime)
+{ ListView_SetHoverTime(m_hwnd, dwHoverTime);
+}
+uint32_t CCtrlListView::SetIconSpacing(int cx, int cy)
+{ return ListView_SetIconSpacing(m_hwnd, cx, cy);
+}
+HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList)
+{ return ListView_SetImageList(m_hwnd, himl, iImageList);
+}
+BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip)
+{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip);
+}
+BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim)
+{ return ListView_SetInsertMark(m_hwnd, plvim);
+}
+COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color)
+{ return ListView_SetInsertMarkColor(m_hwnd, color);
+}
+BOOL CCtrlListView::SetItem(const LVITEM *pitem)
+{ return ListView_SetItem(m_hwnd, pitem);
+}
+void CCtrlListView::SetItemCount(int cItems)
+{ ListView_SetItemCount(m_hwnd, cItems);
+}
+void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags)
+{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags);
+}
+BOOL CCtrlListView::SetItemPosition(int i, int x, int y)
+{ return ListView_SetItemPosition(m_hwnd, i, x, y);
+}
+void CCtrlListView::SetItemPosition32(int iItem, int x, int y)
+{ ListView_SetItemPosition32(m_hwnd, iItem, x, y);
+}
+void CCtrlListView::SetItemState(int i, UINT state, UINT mask)
+{ ListView_SetItemState(m_hwnd, i, state, mask);
+}
+void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText)
+{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText);
+}
+COLORREF CCtrlListView::SetOutlineColor(COLORREF color)
+{ return ListView_SetOutlineColor(m_hwnd, color);
+}
+void CCtrlListView::SetSelectedColumn(int iCol)
+{ ListView_SetSelectedColumn(m_hwnd, iCol);
+}
+INT CCtrlListView::SetSelectionMark(INT iIndex)
+{ return ListView_SetSelectionMark(m_hwnd, iIndex);
+}
+BOOL CCtrlListView::SetTextBkColor(COLORREF clrText)
+{ return ListView_SetTextBkColor(m_hwnd, clrText);
+}
+BOOL CCtrlListView::SetTextColor(COLORREF clrText)
+{ return ListView_SetTextColor(m_hwnd, clrText);
+}
+BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo)
+{ return ListView_SetTileInfo(m_hwnd, plvtinfo);
+}
+BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo)
+{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo);
+}
+HWND CCtrlListView::SetToolTips(HWND ToolTip)
+{ return ListView_SetToolTips(m_hwnd, ToolTip);
+}
+BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode)
+{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode);
+}
+int CCtrlListView::SetView(uint32_t iView)
+{ return ListView_SetView(m_hwnd, iView);
+}
+void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc)
+{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc);
+}
+int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv)
+{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv);
+}
+BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
+{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort);
+}
+BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
+{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort);
+}
+INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const
+{ return ListView_SubItemHitTest(m_hwnd, pInfo);
+}
+BOOL CCtrlListView::Update(int iItem)
+{ return ListView_Update(m_hwnd, iItem);
+}
diff --git a/src/mir_core/src/Windows/CCtrlMButton.cpp b/src/mir_core/src/Windows/CCtrlMButton.cpp index 8d9198e144..cc05a3a8e4 100644 --- a/src/mir_core/src/Windows/CCtrlMButton.cpp +++ b/src/mir_core/src/Windows/CCtrlMButton.cpp @@ -1,62 +1,62 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlMButton - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(hIcon), - m_toolTip(tooltip) -{} - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(::Skin_LoadIcon(iCoreIcon)), - m_toolTip(tooltip) -{} - -CCtrlMButton::~CCtrlMButton() -{ - ::IcoLib_ReleaseIcon(m_hIcon); -} - -void CCtrlMButton::OnInit() -{ - CCtrlButton::OnInit(); - - SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon); - SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0); - SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0); -} - -void CCtrlMButton::MakeFlat() -{ - SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0); -} - -void CCtrlMButton::MakePush() -{ - SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlMButton
+
+CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip)
+ : CCtrlButton(dlg, ctrlId),
+ m_hIcon(hIcon),
+ m_toolTip(tooltip)
+{}
+
+CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip)
+ : CCtrlButton(dlg, ctrlId),
+ m_hIcon(::Skin_LoadIcon(iCoreIcon)),
+ m_toolTip(tooltip)
+{}
+
+CCtrlMButton::~CCtrlMButton()
+{
+ ::IcoLib_ReleaseIcon(m_hIcon);
+}
+
+void CCtrlMButton::OnInit()
+{
+ CCtrlButton::OnInit();
+
+ SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon);
+ SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0);
+ SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0);
+}
+
+void CCtrlMButton::MakeFlat()
+{
+ SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0);
+}
+
+void CCtrlMButton::MakePush()
+{
+ SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0);
+}
diff --git a/src/mir_core/src/Windows/CCtrlPages.cpp b/src/mir_core/src/Windows/CCtrlPages.cpp index 512c32e142..c2a95553c5 100644 --- a/src/mir_core/src/Windows/CCtrlPages.cpp +++ b/src/mir_core/src/Windows/CCtrlPages.cpp @@ -1,411 +1,411 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static volatile long g_order = 1; - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlPages - -struct CCtrlPages::TPageInfo : public MZeroedObject -{ - TPageInfo() - { - m_iOrder = InterlockedIncrement(&g_order); - } - - ~TPageInfo() - { - if (m_hIcon) - DestroyIcon(m_hIcon); - } - - int m_iOrder; - ptrW m_ptszHeader; - HICON m_hIcon; - bool m_bChanged, m_bScheduledResize; - CDlgBase *m_pDlg; -}; - -CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId), - m_hIml(nullptr), - m_pActivePage(nullptr), - m_pages(4, NumericKeySortT) -{} - -void CCtrlPages::OnInit() -{ - CSuper::OnInit(); - Subclass(); - - for (auto &it : m_pages) - InsertPage(it); - m_pages.destroy(); - - ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT); - - TPageInfo *info = GetCurrPage(); - if (info) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - int tabCount; - - switch (msg) { - case WM_SIZE: - if (TPageInfo *pCurrInfo = GetCurrPage()) { - tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p == nullptr) - continue; - if (p == pCurrInfo) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - else p->m_bScheduledResize = true; - } - } - break; - - case PSM_CHANGED: - if (TPageInfo *info = GetCurrPage()) - info->m_bChanged = TRUE; - return TRUE; - - case PSM_FORCECHANGED: - tabCount = GetCount(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p) { - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - if (pshn.hdr.hwndFrom != nullptr) - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - } - break; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} - -void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg) -{ - TPageInfo *info = new TPageInfo; - info->m_pDlg = pDlg; - info->m_hIcon = hIcon; - info->m_ptszHeader = mir_wstrdup(ptszName); - - if (m_hwnd != nullptr) { - InsertPage(info); - - if (GetCount() == 1) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - } - m_pages.insert(info); -} - -void CCtrlPages::ActivatePage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - if (info == nullptr || info->m_pDlg == nullptr) - return; - - if (m_pActivePage != nullptr) - ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE); - - m_pActivePage = info->m_pDlg; - if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - - TabCtrl_SetCurSel(m_hwnd, iPage); - ShowPage(m_pActivePage); - ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0); -} - -void CCtrlPages::CheckRowCount() -{ - int iRowCount = TabCtrl_GetRowCount(m_hwnd); - if (m_numRows != iRowCount) { - m_numRows = iRowCount; - for (auto &p : m_pages) - p->m_bScheduledResize = true; - } -} - -int CCtrlPages::GetCount() -{ - return TabCtrl_GetItemCount(m_hwnd); -} - -CDlgBase* CCtrlPages::GetNthPage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - return (info == nullptr) ? nullptr : info->m_pDlg; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage() -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, iPage, &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -int CCtrlPages::GetDlgIndex(CDlgBase *pDlg) -{ - int tabCount = TabCtrl_GetItemCount(m_hwnd); - for (int i = 0; i < tabCount; i++) { - TCITEM tci; - tci.mask = TCIF_PARAM | TCIF_IMAGE; - TabCtrl_GetItem(m_hwnd, i, &tci); - TPageInfo *pPage = (TPageInfo *)tci.lParam; - if (pPage == nullptr) - continue; - - if (pPage->m_pDlg == pDlg) - return i; - } - - return -1; -} - -void CCtrlPages::InsertPage(TPageInfo *pPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)pPage; - tci.pszText = TranslateW_LP(pPage->m_ptszHeader); - if (pPage->m_hIcon) { - if (!m_hIml) { - m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - TabCtrl_SetImageList(m_hwnd, m_hIml); - } - - tci.mask |= TCIF_IMAGE; - tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon); - } - - TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci); - - CheckRowCount(); -} - -void CCtrlPages::RemovePage(int iPage) -{ - TPageInfo *p = GetItemPage(iPage); - if (p == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, iPage); - m_pages.remove(p); - delete p; - - CheckRowCount(); -} - -void CCtrlPages::ShowPage(CDlgBase *pDlg) -{ - if (pDlg->GetHwnd() == nullptr) { - pDlg->SetParent(m_hwnd); - pDlg->Create(); - - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE); - - EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - ShowWindow(pDlg->GetHwnd(), SW_SHOW); -} - -void CCtrlPages::SwapPages(int idx1, int idx2) -{ - TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2); - if (p1 == nullptr || p2 == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, idx1); - - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)p1; - tci.pszText = TranslateW_LP(p1->m_ptszHeader); - TabCtrl_InsertItem(m_hwnd, idx2, &tci); -} - -BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh) -{ - TPageInfo *info; - PSHNOTIFY pshn; - - switch (pnmh->code) { - case TCN_SELCHANGING: - if (info = GetCurrPage()) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) { - SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE); - return TRUE; - } - } - return TRUE; - - case TCN_SELCHANGE: - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - - if (info = GetCurrPage()) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - else m_pActivePage = nullptr; - return TRUE; - } - - return FALSE; -} - -void CCtrlPages::OnReset() -{ - CSuper::OnReset(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -bool CCtrlPages::OnApply() -{ - PSHNOTIFY pshn; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - if (m_pActivePage != nullptr) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) - return false; - } - - pshn.hdr.code = PSN_APPLY; - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) { - TabCtrl_SetCurSel(m_hwnd, i); - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - m_pActivePage = p->m_pDlg; - m_pActivePage->Show(); - return false; - } - } - - CSuper::OnApply(); - return true; -} - -void CCtrlPages::OnDestroy() -{ - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr; - if (pDlg->GetHwnd()) - pDlg->Close(); - delete p; - } - - TabCtrl_DeleteAllItems(m_hwnd); - - if (m_hIml) { - TabCtrl_SetImageList(m_hwnd, nullptr); - ImageList_Destroy(m_hIml); - } - - CSuper::OnDestroy(); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static volatile long g_order = 1;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlPages
+
+struct CCtrlPages::TPageInfo : public MZeroedObject
+{
+ TPageInfo()
+ {
+ m_iOrder = InterlockedIncrement(&g_order);
+ }
+
+ ~TPageInfo()
+ {
+ if (m_hIcon)
+ DestroyIcon(m_hIcon);
+ }
+
+ int m_iOrder;
+ ptrW m_ptszHeader;
+ HICON m_hIcon;
+ bool m_bChanged, m_bScheduledResize;
+ CDlgBase *m_pDlg;
+};
+
+CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId)
+ : CCtrlBase(dlg, ctrlId),
+ m_hIml(nullptr),
+ m_pActivePage(nullptr),
+ m_pages(4, NumericKeySortT)
+{}
+
+void CCtrlPages::OnInit()
+{
+ CSuper::OnInit();
+ Subclass();
+
+ for (auto &it : m_pages)
+ InsertPage(it);
+ m_pages.destroy();
+
+ ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT);
+
+ TPageInfo *info = GetCurrPage();
+ if (info) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom = m_pActivePage->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+}
+
+LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int tabCount;
+
+ switch (msg) {
+ case WM_SIZE:
+ if (TPageInfo *pCurrInfo = GetCurrPage()) {
+ tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p == nullptr)
+ continue;
+ if (p == pCurrInfo) {
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+ else p->m_bScheduledResize = true;
+ }
+ }
+ break;
+
+ case PSM_CHANGED:
+ if (TPageInfo *info = GetCurrPage())
+ info->m_bChanged = TRUE;
+ return TRUE;
+
+ case PSM_FORCECHANGED:
+ tabCount = GetCount();
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p) {
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ if (pshn.hdr.hwndFrom != nullptr)
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+ }
+ break;
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
+
+void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg)
+{
+ TPageInfo *info = new TPageInfo;
+ info->m_pDlg = pDlg;
+ info->m_hIcon = hIcon;
+ info->m_ptszHeader = mir_wstrdup(ptszName);
+
+ if (m_hwnd != nullptr) {
+ InsertPage(info);
+
+ if (GetCount() == 1) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+ }
+ }
+ m_pages.insert(info);
+}
+
+void CCtrlPages::ActivatePage(int iPage)
+{
+ TPageInfo *info = GetItemPage(iPage);
+ if (info == nullptr || info->m_pDlg == nullptr)
+ return;
+
+ if (m_pActivePage != nullptr)
+ ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE);
+
+ m_pActivePage = info->m_pDlg;
+ if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) {
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+
+ TabCtrl_SetCurSel(m_hwnd, iPage);
+ ShowPage(m_pActivePage);
+ ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0);
+}
+
+void CCtrlPages::CheckRowCount()
+{
+ int iRowCount = TabCtrl_GetRowCount(m_hwnd);
+ if (m_numRows != iRowCount) {
+ m_numRows = iRowCount;
+ for (auto &p : m_pages)
+ p->m_bScheduledResize = true;
+ }
+}
+
+int CCtrlPages::GetCount()
+{
+ return TabCtrl_GetItemCount(m_hwnd);
+}
+
+CDlgBase* CCtrlPages::GetNthPage(int iPage)
+{
+ TPageInfo *info = GetItemPage(iPage);
+ return (info == nullptr) ? nullptr : info->m_pDlg;
+}
+
+CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage()
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM;
+ if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci))
+ return nullptr;
+
+ return (TPageInfo*)tci.lParam;
+}
+
+CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage)
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM;
+ if (!TabCtrl_GetItem(m_hwnd, iPage, &tci))
+ return nullptr;
+
+ return (TPageInfo*)tci.lParam;
+}
+
+int CCtrlPages::GetDlgIndex(CDlgBase *pDlg)
+{
+ int tabCount = TabCtrl_GetItemCount(m_hwnd);
+ for (int i = 0; i < tabCount; i++) {
+ TCITEM tci;
+ tci.mask = TCIF_PARAM | TCIF_IMAGE;
+ TabCtrl_GetItem(m_hwnd, i, &tci);
+ TPageInfo *pPage = (TPageInfo *)tci.lParam;
+ if (pPage == nullptr)
+ continue;
+
+ if (pPage->m_pDlg == pDlg)
+ return i;
+ }
+
+ return -1;
+}
+
+void CCtrlPages::InsertPage(TPageInfo *pPage)
+{
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM | TCIF_TEXT;
+ tci.lParam = (LPARAM)pPage;
+ tci.pszText = TranslateW_LP(pPage->m_ptszHeader);
+ if (pPage->m_hIcon) {
+ if (!m_hIml) {
+ m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);
+ TabCtrl_SetImageList(m_hwnd, m_hIml);
+ }
+
+ tci.mask |= TCIF_IMAGE;
+ tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon);
+ }
+
+ TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci);
+
+ CheckRowCount();
+}
+
+void CCtrlPages::RemovePage(int iPage)
+{
+ TPageInfo *p = GetItemPage(iPage);
+ if (p == nullptr)
+ return;
+
+ TabCtrl_DeleteItem(m_hwnd, iPage);
+ m_pages.remove(p);
+ delete p;
+
+ CheckRowCount();
+}
+
+void CCtrlPages::ShowPage(CDlgBase *pDlg)
+{
+ if (pDlg->GetHwnd() == nullptr) {
+ pDlg->SetParent(m_hwnd);
+ pDlg->Create();
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ TabCtrl_AdjustRect(m_hwnd, FALSE, &rc);
+ SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE);
+
+ EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB);
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.hwndFrom = pDlg->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+ ShowWindow(pDlg->GetHwnd(), SW_SHOW);
+}
+
+void CCtrlPages::SwapPages(int idx1, int idx2)
+{
+ TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2);
+ if (p1 == nullptr || p2 == nullptr)
+ return;
+
+ TabCtrl_DeleteItem(m_hwnd, idx1);
+
+ TCITEM tci = { 0 };
+ tci.mask = TCIF_PARAM | TCIF_TEXT;
+ tci.lParam = (LPARAM)p1;
+ tci.pszText = TranslateW_LP(p1->m_ptszHeader);
+ TabCtrl_InsertItem(m_hwnd, idx2, &tci);
+}
+
+BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh)
+{
+ TPageInfo *info;
+ PSHNOTIFY pshn;
+
+ switch (pnmh->code) {
+ case TCN_SELCHANGING:
+ if (info = GetCurrPage()) {
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd();
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+ if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) {
+ SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ }
+ return TRUE;
+
+ case TCN_SELCHANGE:
+ if (m_pActivePage != nullptr)
+ m_pActivePage->Hide();
+
+ if (info = GetCurrPage()) {
+ m_pActivePage = info->m_pDlg;
+ ShowPage(m_pActivePage);
+ }
+ else m_pActivePage = nullptr;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void CCtrlPages::OnReset()
+{
+ CSuper::OnReset();
+
+ PSHNOTIFY pshn;
+ pshn.hdr.code = PSN_INFOCHANGED;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged)
+ continue;
+
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ }
+}
+
+bool CCtrlPages::OnApply()
+{
+ PSHNOTIFY pshn;
+ pshn.hdr.idFrom = 0;
+ pshn.lParam = 0;
+
+ if (m_pActivePage != nullptr) {
+ pshn.hdr.code = PSN_KILLACTIVE;
+ pshn.hdr.hwndFrom = m_pActivePage->GetHwnd();
+ if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn))
+ return false;
+ }
+
+ pshn.hdr.code = PSN_APPLY;
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged)
+ continue;
+
+ pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd();
+ SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+ if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) {
+ TabCtrl_SetCurSel(m_hwnd, i);
+ if (m_pActivePage != nullptr)
+ m_pActivePage->Hide();
+ m_pActivePage = p->m_pDlg;
+ m_pActivePage->Show();
+ return false;
+ }
+ }
+
+ CSuper::OnApply();
+ return true;
+}
+
+void CCtrlPages::OnDestroy()
+{
+ int tabCount = GetCount();
+ for (int i = 0; i < tabCount; i++) {
+ TPageInfo *p = GetItemPage(i);
+ CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr;
+ if (pDlg->GetHwnd())
+ pDlg->Close();
+ delete p;
+ }
+
+ TabCtrl_DeleteAllItems(m_hwnd);
+
+ if (m_hIml) {
+ TabCtrl_SetImageList(m_hwnd, nullptr);
+ ImageList_Destroy(m_hIml);
+ }
+
+ CSuper::OnDestroy();
+}
diff --git a/src/mir_core/src/Windows/CCtrlRichEdit.cpp b/src/mir_core/src/Windows/CCtrlRichEdit.cpp index 06bca26cb5..f41cad3f98 100644 --- a/src/mir_core/src/Windows/CCtrlRichEdit.cpp +++ b/src/mir_core/src/Windows/CCtrlRichEdit.cpp @@ -1,192 +1,192 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -#include <RichOle.h> - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlRichEdit class - -CCtrlRichEdit::CCtrlRichEdit(CDlgBase *dlg, int ctrlId) - : CCtrlEdit(dlg, ctrlId) -{} - -int CCtrlRichEdit::GetRichTextLength(int iCodePage) const -{ - GETTEXTLENGTHEX gtl; - gtl.codepage = iCodePage; - gtl.flags = GTL_PRECISE; - if (iCodePage == CP_ACP) - gtl.flags |= GTL_NUMBYTES; - else - gtl.flags |= GTL_NUMCHARS | GT_USECRLF; - - return (int)SendMessage(m_hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); -} - -int CCtrlRichEdit::SetRichText(const wchar_t *text) -{ - SETTEXTEX st; - st.flags = ST_DEFAULT; - st.codepage = 1200; - SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text); - - return GetRichTextLength(1200); -} - -int CCtrlRichEdit::SetRichTextRtf(const char *text) -{ - SETTEXTEX st; - st.flags = ST_DEFAULT; - st.codepage = CP_UTF8; - SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text); - - return GetRichTextLength(1200); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static DWORD CALLBACK MessageStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) -{ - static uint32_t dwRead; - char **ppText = (char **)dwCookie; - - if (*ppText == nullptr) { - *ppText = (char *)mir_alloc(cb + 2); - memcpy(*ppText, pbBuff, cb); - *pcb = cb; - dwRead = cb; - *(*ppText + cb) = '\0'; - } - else { - char *p = (char *)mir_realloc(*ppText, dwRead + cb + 2); - memcpy(p + dwRead, pbBuff, cb); - *ppText = p; - *pcb = cb; - dwRead += cb; - *(*ppText + dwRead) = '\0'; - } - return 0; -} - -char* CCtrlRichEdit::GetRichTextRtf(bool bText, bool bSelection) const -{ - char *pszText = nullptr; - uint32_t dwFlags = SF_USECODEPAGE | (CP_UTF8 << 16); - if (bText) - dwFlags |= SF_TEXT; - else - dwFlags |= SF_RTFNOOBJS | SFF_PLAINRTF; - if (bSelection) - dwFlags |= SFF_SELECTION; - - EDITSTREAM stream = { 0 }; - stream.pfnCallback = MessageStreamCallback; - stream.dwCookie = (DWORD_PTR)&pszText; // pass pointer to pointer - SendMessage(m_hwnd, EM_STREAMOUT, dwFlags, (LPARAM)&stream); - return pszText; // pszText contains the text -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////// - -struct CREOleCallback : public IRichEditOleCallback -{ - CREOleCallback() : refCount(0), nextStgId(0), pictStg(nullptr) {} - unsigned refCount; - IStorage *pictStg; - int nextStgId; - - STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR *ppvObj) - { - if (IsEqualIID(riid, IID_IRichEditOleCallback)) { - *ppvObj = this; - AddRef(); - return S_OK; - } - *ppvObj = nullptr; - return E_NOINTERFACE; - } - - STDMETHOD_(ULONG, AddRef)(THIS) - { - if (refCount == 0) - StgCreateDocfile(nullptr, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_DELETEONRELEASE, 0, &pictStg); - - return ++refCount; - } - - STDMETHOD_(ULONG, Release)(THIS) - { - if (--refCount == 0) { - if (pictStg) { - pictStg->Release(); - pictStg = nullptr; - } - } - return refCount; - } - - STDMETHOD(GetNewStorage)(LPSTORAGE *lplpstg) - { - wchar_t sztName[64]; - mir_snwprintf(sztName, L"s%u", nextStgId++); - if (pictStg == nullptr) - return STG_E_MEDIUMFULL; - return pictStg->CreateStorage(sztName, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, 0, lplpstg); - } - - STDMETHOD(ContextSensitiveHelp)(BOOL) - { return S_OK; } - STDMETHOD(GetInPlaceContext)(LPOLEINPLACEFRAME*, LPOLEINPLACEUIWINDOW*, LPOLEINPLACEFRAMEINFO) - { return E_INVALIDARG; } - STDMETHOD(ShowContainerUI)(BOOL) - { return S_OK; } - STDMETHOD(QueryInsertObject)(LPCLSID, LPSTORAGE, LONG) - { return S_OK; } - STDMETHOD(DeleteObject)(LPOLEOBJECT) - { return S_OK; } - STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT*, DWORD, BOOL, HGLOBAL) - { return S_OK; } - STDMETHOD(GetClipboardData)(CHARRANGE*, DWORD, LPDATAOBJECT*) - { return E_NOTIMPL; } - STDMETHOD(GetDragDropEffect)(BOOL, DWORD, LPDWORD) - { return S_OK; } - STDMETHOD(GetContextMenu)(uint16_t, LPOLEOBJECT, CHARRANGE*, HMENU*) - { return E_INVALIDARG; } -}; - -struct CREOleCallback2 : public CREOleCallback -{ - STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT *lpcfFormat, DWORD, BOOL, HGLOBAL) - { *lpcfFormat = CF_UNICODETEXT; - return S_OK; - } -}; - -CREOleCallback reOleCallback; -CREOleCallback2 reOleCallback2; - -void CCtrlRichEdit::SetReadOnly(bool bReadOnly) -{ - SendMsg(EM_SETOLECALLBACK, 0, (LPARAM)(bReadOnly ? &reOleCallback : &reOleCallback2)); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+#include <RichOle.h>
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlRichEdit class
+
+CCtrlRichEdit::CCtrlRichEdit(CDlgBase *dlg, int ctrlId)
+ : CCtrlEdit(dlg, ctrlId)
+{}
+
+int CCtrlRichEdit::GetRichTextLength(int iCodePage) const
+{
+ GETTEXTLENGTHEX gtl;
+ gtl.codepage = iCodePage;
+ gtl.flags = GTL_PRECISE;
+ if (iCodePage == CP_ACP)
+ gtl.flags |= GTL_NUMBYTES;
+ else
+ gtl.flags |= GTL_NUMCHARS | GT_USECRLF;
+
+ return (int)SendMessage(m_hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
+}
+
+int CCtrlRichEdit::SetRichText(const wchar_t *text)
+{
+ SETTEXTEX st;
+ st.flags = ST_DEFAULT;
+ st.codepage = 1200;
+ SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text);
+
+ return GetRichTextLength(1200);
+}
+
+int CCtrlRichEdit::SetRichTextRtf(const char *text)
+{
+ SETTEXTEX st;
+ st.flags = ST_DEFAULT;
+ st.codepage = CP_UTF8;
+ SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text);
+
+ return GetRichTextLength(1200);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static DWORD CALLBACK MessageStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
+{
+ static uint32_t dwRead;
+ char **ppText = (char **)dwCookie;
+
+ if (*ppText == nullptr) {
+ *ppText = (char *)mir_alloc(cb + 2);
+ memcpy(*ppText, pbBuff, cb);
+ *pcb = cb;
+ dwRead = cb;
+ *(*ppText + cb) = '\0';
+ }
+ else {
+ char *p = (char *)mir_realloc(*ppText, dwRead + cb + 2);
+ memcpy(p + dwRead, pbBuff, cb);
+ *ppText = p;
+ *pcb = cb;
+ dwRead += cb;
+ *(*ppText + dwRead) = '\0';
+ }
+ return 0;
+}
+
+char* CCtrlRichEdit::GetRichTextRtf(bool bText, bool bSelection) const
+{
+ char *pszText = nullptr;
+ uint32_t dwFlags = SF_USECODEPAGE | (CP_UTF8 << 16);
+ if (bText)
+ dwFlags |= SF_TEXT;
+ else
+ dwFlags |= SF_RTFNOOBJS | SFF_PLAINRTF;
+ if (bSelection)
+ dwFlags |= SFF_SELECTION;
+
+ EDITSTREAM stream = { 0 };
+ stream.pfnCallback = MessageStreamCallback;
+ stream.dwCookie = (DWORD_PTR)&pszText; // pass pointer to pointer
+ SendMessage(m_hwnd, EM_STREAMOUT, dwFlags, (LPARAM)&stream);
+ return pszText; // pszText contains the text
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct CREOleCallback : public IRichEditOleCallback
+{
+ CREOleCallback() : refCount(0), nextStgId(0), pictStg(nullptr) {}
+ unsigned refCount;
+ IStorage *pictStg;
+ int nextStgId;
+
+ STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR *ppvObj)
+ {
+ if (IsEqualIID(riid, IID_IRichEditOleCallback)) {
+ *ppvObj = this;
+ AddRef();
+ return S_OK;
+ }
+ *ppvObj = nullptr;
+ return E_NOINTERFACE;
+ }
+
+ STDMETHOD_(ULONG, AddRef)(THIS)
+ {
+ if (refCount == 0)
+ StgCreateDocfile(nullptr, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_DELETEONRELEASE, 0, &pictStg);
+
+ return ++refCount;
+ }
+
+ STDMETHOD_(ULONG, Release)(THIS)
+ {
+ if (--refCount == 0) {
+ if (pictStg) {
+ pictStg->Release();
+ pictStg = nullptr;
+ }
+ }
+ return refCount;
+ }
+
+ STDMETHOD(GetNewStorage)(LPSTORAGE *lplpstg)
+ {
+ wchar_t sztName[64];
+ mir_snwprintf(sztName, L"s%u", nextStgId++);
+ if (pictStg == nullptr)
+ return STG_E_MEDIUMFULL;
+ return pictStg->CreateStorage(sztName, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, 0, lplpstg);
+ }
+
+ STDMETHOD(ContextSensitiveHelp)(BOOL)
+ { return S_OK; }
+ STDMETHOD(GetInPlaceContext)(LPOLEINPLACEFRAME*, LPOLEINPLACEUIWINDOW*, LPOLEINPLACEFRAMEINFO)
+ { return E_INVALIDARG; }
+ STDMETHOD(ShowContainerUI)(BOOL)
+ { return S_OK; }
+ STDMETHOD(QueryInsertObject)(LPCLSID, LPSTORAGE, LONG)
+ { return S_OK; }
+ STDMETHOD(DeleteObject)(LPOLEOBJECT)
+ { return S_OK; }
+ STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT*, DWORD, BOOL, HGLOBAL)
+ { return S_OK; }
+ STDMETHOD(GetClipboardData)(CHARRANGE*, DWORD, LPDATAOBJECT*)
+ { return E_NOTIMPL; }
+ STDMETHOD(GetDragDropEffect)(BOOL, DWORD, LPDWORD)
+ { return S_OK; }
+ STDMETHOD(GetContextMenu)(uint16_t, LPOLEOBJECT, CHARRANGE*, HMENU*)
+ { return E_INVALIDARG; }
+};
+
+struct CREOleCallback2 : public CREOleCallback
+{
+ STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT *lpcfFormat, DWORD, BOOL, HGLOBAL)
+ { *lpcfFormat = CF_UNICODETEXT;
+ return S_OK;
+ }
+};
+
+CREOleCallback reOleCallback;
+CREOleCallback2 reOleCallback2;
+
+void CCtrlRichEdit::SetReadOnly(bool bReadOnly)
+{
+ SendMsg(EM_SETOLECALLBACK, 0, (LPARAM)(bReadOnly ? &reOleCallback : &reOleCallback2));
+}
diff --git a/src/mir_core/src/Windows/CCtrlSlider.cpp b/src/mir_core/src/Windows/CCtrlSlider.cpp index 69aeb24796..9938736e42 100644 --- a/src/mir_core/src/Windows/CCtrlSlider.cpp +++ b/src/mir_core/src/Windows/CCtrlSlider.cpp @@ -1,70 +1,70 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSlider class - -CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax) -{ - m_bNotifiable = true; -} - -BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == WM_HSCROLL) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSlider::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetPosition()); - return true; -} - -void CCtrlSlider::OnReset() -{ - SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int CCtrlSlider::GetPosition() const -{ - return SendMsg(TBM_GETPOS, 0, 0); -} - -void CCtrlSlider::SetPosition(int wPos) -{ - SendMsg(TBM_SETPOS, TRUE, wPos); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlSlider class
+
+CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) :
+ CCtrlData(dlg, ctrlId),
+ m_wMin(wMin),
+ m_wMax(wMax)
+{
+ m_bNotifiable = true;
+}
+
+BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode)
+{
+ if (idCode == WM_HSCROLL) {
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool CCtrlSlider::OnApply()
+{
+ CSuper::OnApply();
+
+ if (m_dbLink != nullptr)
+ SaveInt(GetPosition());
+ return true;
+}
+
+void CCtrlSlider::OnReset()
+{
+ SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax));
+
+ if (m_dbLink != nullptr)
+ SetPosition(LoadInt());
+}
+
+int CCtrlSlider::GetPosition() const
+{
+ return SendMsg(TBM_GETPOS, 0, 0);
+}
+
+void CCtrlSlider::SetPosition(int wPos)
+{
+ SendMsg(TBM_SETPOS, TRUE, wPos);
+}
diff --git a/src/mir_core/src/Windows/CCtrlSpin.cpp b/src/mir_core/src/Windows/CCtrlSpin.cpp index 54d43e933a..54dc5ffd07 100644 --- a/src/mir_core/src/Windows/CCtrlSpin.cpp +++ b/src/mir_core/src/Windows/CCtrlSpin.cpp @@ -1,81 +1,81 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSpin class - -CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax), - m_wCurr(0) -{} - -BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == UDN_DELTAPOS) { - auto *pEvent = (NMUPDOWN *)pnmh; - m_wCurr = pEvent->iPos + pEvent->iDelta; - - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSpin::OnApply() -{ - CSuper::OnApply(); - - m_wCurr = SendMsg(UDM_GETPOS, 0, 0); - if (m_dbLink != nullptr) - SaveInt(m_wCurr); - - HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0); - if (hwndBuddy) { - wchar_t buf[100]; - _itow(m_wCurr, buf, 10); - ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf)); - } - - return true; -} - -void CCtrlSpin::OnReset() -{ - SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int16_t CCtrlSpin::GetPosition() -{ - return m_wCurr; -} - -void CCtrlSpin::SetPosition(int16_t wPos) -{ - SendMsg(UDM_SETPOS, 0, m_wCurr = wPos); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlSpin class
+
+CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) :
+ CCtrlData(dlg, ctrlId),
+ m_wMin(wMin),
+ m_wMax(wMax),
+ m_wCurr(0)
+{}
+
+BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh)
+{
+ if (pnmh->code == UDN_DELTAPOS) {
+ auto *pEvent = (NMUPDOWN *)pnmh;
+ m_wCurr = pEvent->iPos + pEvent->iDelta;
+
+ NotifyChange();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool CCtrlSpin::OnApply()
+{
+ CSuper::OnApply();
+
+ m_wCurr = SendMsg(UDM_GETPOS, 0, 0);
+ if (m_dbLink != nullptr)
+ SaveInt(m_wCurr);
+
+ HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0);
+ if (hwndBuddy) {
+ wchar_t buf[100];
+ _itow(m_wCurr, buf, 10);
+ ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf));
+ }
+
+ return true;
+}
+
+void CCtrlSpin::OnReset()
+{
+ SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin));
+
+ if (m_dbLink != nullptr)
+ SetPosition(LoadInt());
+}
+
+int16_t CCtrlSpin::GetPosition()
+{
+ return m_wCurr;
+}
+
+void CCtrlSpin::SetPosition(int16_t wPos)
+{
+ SendMsg(UDM_SETPOS, 0, m_wCurr = wPos);
+}
diff --git a/src/mir_core/src/Windows/CCtrlTreeOpts.cpp b/src/mir_core/src/Windows/CCtrlTreeOpts.cpp index d15f3d4b1b..7935283285 100644 --- a/src/mir_core/src/Windows/CCtrlTreeOpts.cpp +++ b/src/mir_core/src/Windows/CCtrlTreeOpts.cpp @@ -2,7 +2,7 @@ Object UI extensions Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/src/mir_core/src/Windows/CCtrlTreeView.cpp b/src/mir_core/src/Windows/CCtrlTreeView.cpp index 6f435920f2..178b6b730e 100644 --- a/src/mir_core/src/Windows/CCtrlTreeView.cpp +++ b/src/mir_core/src/Windows/CCtrlTreeView.cpp @@ -2,7 +2,7 @@ Object UI extensions Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/src/mir_core/src/Windows/CDbLink.cpp b/src/mir_core/src/Windows/CDbLink.cpp index 2a0734cd3b..05925f871b 100644 --- a/src/mir_core/src/Windows/CDbLink.cpp +++ b/src/mir_core/src/Windows/CDbLink.cpp @@ -1,92 +1,92 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CDbLink class - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue) - : CDataLink(type) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_iDefault = iValue; - m_szDefault = nullptr; - dbv.type = DBVT_DELETED; -} - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue) - : CDataLink(type), - m_iDefault(0) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_szDefault = mir_wstrdup(szValue); - dbv.type = DBVT_DELETED; -} - -CDbLink::~CDbLink() -{ - mir_free(m_szModule); - mir_free(m_szSetting); - mir_free(m_szDefault); - if (dbv.type != DBVT_DELETED) - db_free(&dbv); -} - -uint32_t CDbLink::LoadInt() -{ - switch (m_type) { - case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault); - default: return m_iDefault; - } -} - -void CDbLink::SaveInt(uint32_t value) -{ - switch (m_type) { - case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break; - case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break; - case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break; - } -} - -wchar_t* CDbLink::LoadText() -{ - if (dbv.type != DBVT_DELETED) db_free(&dbv); - if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) { - if (dbv.type == DBVT_WCHAR) - return dbv.pwszVal; - return m_szDefault; - } - - dbv.type = DBVT_DELETED; - return m_szDefault; -} - -void CDbLink::SaveText(wchar_t *value) -{ - db_set_ws(0, m_szModule, m_szSetting, value); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDbLink class
+
+CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue)
+ : CDataLink(type)
+{
+ m_szModule = mir_strdup(szModule);
+ m_szSetting = mir_strdup(szSetting);
+ m_iDefault = iValue;
+ m_szDefault = nullptr;
+ dbv.type = DBVT_DELETED;
+}
+
+CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue)
+ : CDataLink(type),
+ m_iDefault(0)
+{
+ m_szModule = mir_strdup(szModule);
+ m_szSetting = mir_strdup(szSetting);
+ m_szDefault = mir_wstrdup(szValue);
+ dbv.type = DBVT_DELETED;
+}
+
+CDbLink::~CDbLink()
+{
+ mir_free(m_szModule);
+ mir_free(m_szSetting);
+ mir_free(m_szDefault);
+ if (dbv.type != DBVT_DELETED)
+ db_free(&dbv);
+}
+
+uint32_t CDbLink::LoadInt()
+{
+ switch (m_type) {
+ case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault);
+ case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault);
+ default: return m_iDefault;
+ }
+}
+
+void CDbLink::SaveInt(uint32_t value)
+{
+ switch (m_type) {
+ case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break;
+ case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break;
+ case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break;
+ }
+}
+
+wchar_t* CDbLink::LoadText()
+{
+ if (dbv.type != DBVT_DELETED) db_free(&dbv);
+ if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) {
+ if (dbv.type == DBVT_WCHAR)
+ return dbv.pwszVal;
+ return m_szDefault;
+ }
+
+ dbv.type = DBVT_DELETED;
+ return m_szDefault;
+}
+
+void CDbLink::SaveText(wchar_t *value)
+{
+ db_set_ws(0, m_szModule, m_szSetting, value);
+}
diff --git a/src/mir_core/src/Windows/CDlgBase.cpp b/src/mir_core/src/Windows/CDlgBase.cpp index 84066de414..45ae944e21 100644 --- a/src/mir_core/src/Windows/CDlgBase.cpp +++ b/src/mir_core/src/Windows/CDlgBase.cpp @@ -1,522 +1,522 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" -#include "diatheme.h" - -static mir_cs csDialogs; - -static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} -static LIST<CDlgBase> arDialogs(10, CompareDialogs); - -#pragma comment(lib, "uxtheme") - -///////////////////////////////////////////////////////////////////////////////////////// -// CDlgBase - -static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2) -{ - return c1->GetCtrlId() - c2->GetCtrlId(); -} - -static int CompareTimerId(const CTimer *t1, const CTimer *t2) -{ - return t1->GetEventId() - t2->GetEventId(); -} - -CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog) : - m_controls(1, CompareControlId), - m_timers(1, CompareTimerId), - m_pPlugin(pPlug), - m_bFixedSize(!g_bEnableDpiAware) -{ - m_idDialog = idDialog; - m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL; -} - -CDlgBase::~CDlgBase() -{ - m_bInitialized = false; // prevent double call of destructor - if (m_hwnd) - DestroyWindow(m_hwnd); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// events - -bool CDlgBase::OnInitDialog() -{ - return true; -} - -bool CDlgBase::OnClose() -{ - return true; -} - -bool CDlgBase::OnApply() -{ - return true; -} - -void CDlgBase::OnChange() -{} - -void CDlgBase::OnDestroy() -{} - -void CDlgBase::OnReset() -{} - -void CDlgBase::OnTimer(CTimer*) -{} - -///////////////////////////////////////////////////////////////////////////////////////// -// methods - -void CDlgBase::Close() -{ - ::SendMessage(m_hwnd, WM_CLOSE, 0, 0); -} - -void CDlgBase::Create() -{ - if (!m_bFixedSize) { - mir_ptr<DLGTEMPLATE> pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst())); - CreateDialogIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this); - } - else CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); -} - -int CDlgBase::DoModal() -{ - m_isModal = true; - - if (m_bFixedSize) - return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); - - mir_ptr<DLGTEMPLATE> pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst())); - return DialogBoxIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this); -} - -void CDlgBase::EndModal(INT_PTR nResult) -{ - ::EndDialog(m_hwnd, nResult); -} - -HINSTANCE CDlgBase::GetInst() const -{ - return m_pPlugin.getInst(); -} - -void CDlgBase::NotifyChange(void) -{ - if (!m_bInitialized) - return; - - OnChange(); - - if (m_hwndParent) - SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0); -} - -void CDlgBase::Resize() -{ - SendMessage(m_hwnd, WM_SIZE, 0, 0); -} - -void CDlgBase::SetCaption(const wchar_t *ptszCaption) -{ - if (m_hwnd && ptszCaption) - SetWindowText(m_hwnd, ptszCaption); -} - -void CDlgBase::SetDraw(bool bEnable) -{ - ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CDlgBase::Show(int nCmdShow) -{ - if (m_hwnd == nullptr) - Create(); - ShowWindow(m_hwnd, nCmdShow); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue); -} - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// virtual methods - -int CDlgBase::GlobalDlgResizer(HWND hwnd, LPARAM, UTILRESIZECONTROL *urc) -{ - CDlgBase *wnd = CDlgBase::Find(hwnd); - return (wnd == nullptr) ? 0 : wnd->Resizer(urc); -} - -void CDlgBase::OnResize() -{ - if (m_forceResizable || (GetWindowLongPtr(m_hwnd, GWL_STYLE) & WS_THICKFRAME)) - Utils_ResizeDialog(m_hwnd, m_pPlugin.getInst(), MAKEINTRESOURCEA(m_idDialog), GlobalDlgResizer); -} - -int CDlgBase::Resizer(UTILRESIZECONTROL*) -{ - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -BOOL CALLBACK CDlgBase::GlobalFieldEnum(HWND hwnd, LPARAM lParam) -{ - CDlgBase *pDlg = (CDlgBase*)lParam; - int id = GetWindowLongPtrW(hwnd, GWLP_ID); - if (id <= 0) - return TRUE; - - // already declared inside the class? skipping - CCtrlBase *ctrl = pDlg->FindControl(id); - if (ctrl != nullptr) - return TRUE; - - wchar_t wszClass[100]; - GetClassNameW(hwnd, wszClass, _countof(wszClass)); - if (!wcsicmp(wszClass, L"Static")) - new CCtrlLabel(pDlg, id); - if (!wcsicmp(wszClass, L"Edit")) - new CCtrlEdit(pDlg, id); - else if (!wcsicmp(wszClass, L"ComboBox")) - new CCtrlCombo(pDlg, id); - else if (!wcsicmp(wszClass, L"Button")) { - switch (GetWindowLongW(hwnd, GWL_STYLE) & (BS_CHECKBOX | BS_RADIOBUTTON | BS_AUTOCHECKBOX | BS_AUTORADIOBUTTON)) { - case BS_CHECKBOX: - case BS_AUTOCHECKBOX: - case BS_RADIOBUTTON: - case BS_AUTORADIOBUTTON: - new CCtrlCheck(pDlg, id); - break; - - default: - new CCtrlButton(pDlg, id); - } - } - else if (!wcsicmp(wszClass, L"RichEdit50W")) - new CCtrlRichEdit(pDlg, id); - else if (!wcsicmp(wszClass, L"msctls_updown32")) - new CCtrlSpin(pDlg, id); - - return TRUE; -} - -INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - m_bInitialized = m_bSucceeded = false; - TranslateDialog_LP(m_hwnd, &m_pPlugin); - - ::EnumChildWindows(m_hwnd, &GlobalFieldEnum, LPARAM(this)); - - NotifyControls(&CCtrlBase::OnInit); - if (!OnInitDialog()) - return FALSE; - - for (auto &it : m_controls) - if (it->m_bNotifiable) - it->OnChange(it); - - m_bInitialized = true; - return TRUE; - - case WM_CTLCOLOREDIT: - case WM_CTLCOLORSTATIC: - if (CCtrlBase *ctrl = FindControl(HWND(lParam))) { - if (ctrl->m_bUseSystemColors) { - SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); - return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); - } - } - break; - - case WM_GETMINMAXINFO: - if (m_iMinHeight != -1 && m_iMinWidth != -1) { - MINMAXINFO *lpmmi = (MINMAXINFO*)lParam; - lpmmi->ptMinTrackSize.y = m_iMinHeight; - lpmmi->ptMinTrackSize.x = m_iMinWidth; - return 0; - } - break; - - case WM_MEASUREITEM: - if (!Menu_MeasureItem(lParam)) { - MEASUREITEMSTRUCT *param = (MEASUREITEMSTRUCT *)lParam; - if (param && param->CtlID) - if (CCtrlBase *ctrl = FindControl(param->CtlID)) - return ctrl->OnMeasureItem(param); - } - return FALSE; - - case WM_DRAWITEM: - if (!Menu_DrawItem(lParam)) { - DRAWITEMSTRUCT *param = (DRAWITEMSTRUCT *)lParam; - if (param && param->CtlID) - if (CCtrlBase *ctrl = FindControl(param->CtlID)) - return ctrl->OnDrawItem(param); - } - return FALSE; - - case WM_DELETEITEM: - { - DELETEITEMSTRUCT *param = (DELETEITEMSTRUCT *)lParam; - if (param && param->CtlID) - if (CCtrlBase *ctrl = FindControl(param->CtlID)) - return ctrl->OnDeleteItem(param); - } - return FALSE; - - case WM_COMMAND: - { - HWND hwndCtrl = (HWND)lParam; - uint16_t idCtrl = LOWORD(wParam); - uint16_t idCode = HIWORD(wParam); - if (CCtrlBase *ctrl = FindControl(idCtrl)) { - BOOL result = ctrl->OnCommand(hwndCtrl, idCtrl, idCode); - if (result != FALSE) - return result; - } - - if (idCode == BN_CLICKED) { - // close dialog automatically if 'Cancel' button is pressed - if (idCtrl == IDCANCEL && (m_autoClose & CLOSE_ON_CANCEL)) { - m_bExiting = true; - PostMessage(m_hwnd, WM_CLOSE, 0, 0); - } - - // close dialog automatically if 'OK' button is pressed - if (idCtrl == IDOK && (m_autoClose & CLOSE_ON_OK)) { - // validate dialog data first - if (VerifyControls(&CCtrlBase::OnApply)) { - m_bExiting = true; - - // everything ok? good, let's close it - if (OnApply()) { - m_bSucceeded = true; - PostMessage(m_hwnd, WM_CLOSE, 0, 0); - } - else m_bExiting = false; - } - } - } - } - return FALSE; - - case WM_NOTIFY: - { - int idCtrl = wParam; - NMHDR *pnmh = (NMHDR *)lParam; - if (pnmh->idFrom == 0) { - switch (pnmh->code) { - case PSN_APPLY: - if (LPPSHNOTIFY(lParam)->lParam != 3) // IDC_APPLY - m_bExiting = true; - - if (!VerifyControls(&CCtrlBase::OnApply)) - m_bExiting = false; - else if (!OnApply()) - m_bExiting = false; - break; - - case PSN_RESET: - NotifyControls(&CCtrlBase::OnReset); - OnReset(); - break; - - case PSN_WIZFINISH: - m_OnFinishWizard(this); - break; - } - } - - if (CCtrlBase *ctrl = FindControl(pnmh->idFrom)) - return ctrl->OnNotify(idCtrl, pnmh); - } - return FALSE; - - case WM_HSCROLL: - if (auto *pCtrl = FindControl(HWND(lParam))) - pCtrl->OnCommand(HWND(lParam), pCtrl->m_idCtrl, WM_HSCROLL); - break; - - case PSM_CHANGED: - if (m_bInitialized) - OnChange(); - break; - - case WM_CONTEXTMENU: - if (CCtrlBase *ctrl = FindControl(HWND(wParam))) { - CContextMenuPos pos = {}; - if (lParam != -1) { - pos.pt.x = GET_X_LPARAM(lParam); - pos.pt.y = GET_Y_LPARAM(lParam); - } - ctrl->GetCaretPos(pos); - ctrl->OnBuildMenu(&pos); - } - break; - - case WM_SIZE: - OnResize(); - return TRUE; - - case WM_TIMER: - if (CTimer *timer = FindTimer(wParam)) - return timer->OnTimer(); - return FALSE; - - case WM_CLOSE: - if (OnClose()) { - m_bExiting = true; - if (m_isModal) - EndModal(m_bSucceeded ? IDOK : FALSE); - else - DestroyWindow(m_hwnd); - } - return TRUE; - - case WM_DESTROY: - m_bExiting = true; - OnDestroy(); - NotifyControls(&CCtrlBase::OnDestroy); - { - mir_cslock lck(csDialogs); - int idx = arDialogs.getIndex(this); - if (idx != -1) - arDialogs.remove(idx); - } - m_hwnd = nullptr; - if (m_bInitialized) { - if (m_isModal) - m_isModal = false; - else // modeless dialogs MUST be allocated with 'new' - delete this; - } - - return TRUE; - } - - return FALSE; -} - -INT_PTR CALLBACK CDlgBase::GlobalDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CDlgBase *wnd; - if (msg == WM_INITDIALOG) { - wnd = (CDlgBase*)lParam; - wnd->m_hwnd = hwnd; - - mir_cslock lck(csDialogs); - arDialogs.insert(wnd); - } - else wnd = CDlgBase::Find(hwnd); - - return (wnd == nullptr) ? FALSE : wnd->DlgProc(msg, wParam, lParam); -} - -void CDlgBase::ThemeDialogBackground(BOOL tabbed) -{ - EnableThemeDialogTexture(m_hwnd, (tabbed ? ETDT_ENABLE : ETDT_DISABLE) | ETDT_USETABTEXTURE); -} - -void CDlgBase::AddControl(CCtrlBase *ctrl) -{ - m_controls.insert(ctrl); -} - -void CDlgBase::RemoveControl(CCtrlBase *ctrl) -{ - m_controls.remove(ctrl); -} - -void CDlgBase::NotifyControls(void (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - (it->*fn)(); -} - -bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - if (!(it->*fn)()) - return false; - - return true; -} - -CCtrlBase* CDlgBase::FindControl(int idCtrl) -{ - CCtrlBase search(nullptr, idCtrl); - return m_controls.find(&search); -} - -CCtrlBase* CDlgBase::FindControl(HWND hwnd) -{ - for (auto &it : m_controls) - if (it->GetHwnd() == hwnd) - return it; - - return nullptr; -} - -void CDlgBase::AddTimer(CTimer *timer) -{ - m_timers.insert(timer); -} - -void CDlgBase::RemoveTimer(UINT_PTR idEvent) -{ - CTimer search(nullptr, idEvent); - m_timers.remove(&search); -} - -CTimer* CDlgBase::FindTimer(int idEvent) -{ - CTimer search(nullptr, idEvent); - return m_timers.find(&search); -} - -CDlgBase* CDlgBase::Find(HWND hwnd) -{ - PVOID bullshit[2]; // vfptr + hwnd - bullshit[1] = hwnd; - return arDialogs.find((CDlgBase*)&bullshit); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+#include "diatheme.h"
+
+static mir_cs csDialogs;
+
+static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2)
+{
+ return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd();
+}
+static LIST<CDlgBase> arDialogs(10, CompareDialogs);
+
+#pragma comment(lib, "uxtheme")
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDlgBase
+
+static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2)
+{
+ return c1->GetCtrlId() - c2->GetCtrlId();
+}
+
+static int CompareTimerId(const CTimer *t1, const CTimer *t2)
+{
+ return t1->GetEventId() - t2->GetEventId();
+}
+
+CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog) :
+ m_controls(1, CompareControlId),
+ m_timers(1, CompareTimerId),
+ m_pPlugin(pPlug),
+ m_bFixedSize(!g_bEnableDpiAware)
+{
+ m_idDialog = idDialog;
+ m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL;
+}
+
+CDlgBase::~CDlgBase()
+{
+ m_bInitialized = false; // prevent double call of destructor
+ if (m_hwnd)
+ DestroyWindow(m_hwnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// events
+
+bool CDlgBase::OnInitDialog()
+{
+ return true;
+}
+
+bool CDlgBase::OnClose()
+{
+ return true;
+}
+
+bool CDlgBase::OnApply()
+{
+ return true;
+}
+
+void CDlgBase::OnChange()
+{}
+
+void CDlgBase::OnDestroy()
+{}
+
+void CDlgBase::OnReset()
+{}
+
+void CDlgBase::OnTimer(CTimer*)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// methods
+
+void CDlgBase::Close()
+{
+ ::SendMessage(m_hwnd, WM_CLOSE, 0, 0);
+}
+
+void CDlgBase::Create()
+{
+ if (!m_bFixedSize) {
+ mir_ptr<DLGTEMPLATE> pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst()));
+ CreateDialogIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this);
+ }
+ else CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this);
+}
+
+int CDlgBase::DoModal()
+{
+ m_isModal = true;
+
+ if (m_bFixedSize)
+ return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this);
+
+ mir_ptr<DLGTEMPLATE> pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst()));
+ return DialogBoxIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this);
+}
+
+void CDlgBase::EndModal(INT_PTR nResult)
+{
+ ::EndDialog(m_hwnd, nResult);
+}
+
+HINSTANCE CDlgBase::GetInst() const
+{
+ return m_pPlugin.getInst();
+}
+
+void CDlgBase::NotifyChange(void)
+{
+ if (!m_bInitialized)
+ return;
+
+ OnChange();
+
+ if (m_hwndParent)
+ SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0);
+}
+
+void CDlgBase::Resize()
+{
+ SendMessage(m_hwnd, WM_SIZE, 0, 0);
+}
+
+void CDlgBase::SetCaption(const wchar_t *ptszCaption)
+{
+ if (m_hwnd && ptszCaption)
+ SetWindowText(m_hwnd, ptszCaption);
+}
+
+void CDlgBase::SetDraw(bool bEnable)
+{
+ ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0);
+}
+
+void CDlgBase::Show(int nCmdShow)
+{
+ if (m_hwnd == nullptr)
+ Create();
+ ShowWindow(m_hwnd, nCmdShow);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue)
+{
+ ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue);
+}
+
+void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue)
+{
+ ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// virtual methods
+
+int CDlgBase::GlobalDlgResizer(HWND hwnd, LPARAM, UTILRESIZECONTROL *urc)
+{
+ CDlgBase *wnd = CDlgBase::Find(hwnd);
+ return (wnd == nullptr) ? 0 : wnd->Resizer(urc);
+}
+
+void CDlgBase::OnResize()
+{
+ if (m_forceResizable || (GetWindowLongPtr(m_hwnd, GWL_STYLE) & WS_THICKFRAME))
+ Utils_ResizeDialog(m_hwnd, m_pPlugin.getInst(), MAKEINTRESOURCEA(m_idDialog), GlobalDlgResizer);
+}
+
+int CDlgBase::Resizer(UTILRESIZECONTROL*)
+{
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+BOOL CALLBACK CDlgBase::GlobalFieldEnum(HWND hwnd, LPARAM lParam)
+{
+ CDlgBase *pDlg = (CDlgBase*)lParam;
+ int id = GetWindowLongPtrW(hwnd, GWLP_ID);
+ if (id <= 0)
+ return TRUE;
+
+ // already declared inside the class? skipping
+ CCtrlBase *ctrl = pDlg->FindControl(id);
+ if (ctrl != nullptr)
+ return TRUE;
+
+ wchar_t wszClass[100];
+ GetClassNameW(hwnd, wszClass, _countof(wszClass));
+ if (!wcsicmp(wszClass, L"Static"))
+ new CCtrlLabel(pDlg, id);
+ if (!wcsicmp(wszClass, L"Edit"))
+ new CCtrlEdit(pDlg, id);
+ else if (!wcsicmp(wszClass, L"ComboBox"))
+ new CCtrlCombo(pDlg, id);
+ else if (!wcsicmp(wszClass, L"Button")) {
+ switch (GetWindowLongW(hwnd, GWL_STYLE) & (BS_CHECKBOX | BS_RADIOBUTTON | BS_AUTOCHECKBOX | BS_AUTORADIOBUTTON)) {
+ case BS_CHECKBOX:
+ case BS_AUTOCHECKBOX:
+ case BS_RADIOBUTTON:
+ case BS_AUTORADIOBUTTON:
+ new CCtrlCheck(pDlg, id);
+ break;
+
+ default:
+ new CCtrlButton(pDlg, id);
+ }
+ }
+ else if (!wcsicmp(wszClass, L"RichEdit50W"))
+ new CCtrlRichEdit(pDlg, id);
+ else if (!wcsicmp(wszClass, L"msctls_updown32"))
+ new CCtrlSpin(pDlg, id);
+
+ return TRUE;
+}
+
+INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ m_bInitialized = m_bSucceeded = false;
+ TranslateDialog_LP(m_hwnd, &m_pPlugin);
+
+ ::EnumChildWindows(m_hwnd, &GlobalFieldEnum, LPARAM(this));
+
+ NotifyControls(&CCtrlBase::OnInit);
+ if (!OnInitDialog())
+ return FALSE;
+
+ for (auto &it : m_controls)
+ if (it->m_bNotifiable)
+ it->OnChange(it);
+
+ m_bInitialized = true;
+ return TRUE;
+
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORSTATIC:
+ if (CCtrlBase *ctrl = FindControl(HWND(lParam))) {
+ if (ctrl->m_bUseSystemColors) {
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
+ }
+ }
+ break;
+
+ case WM_GETMINMAXINFO:
+ if (m_iMinHeight != -1 && m_iMinWidth != -1) {
+ MINMAXINFO *lpmmi = (MINMAXINFO*)lParam;
+ lpmmi->ptMinTrackSize.y = m_iMinHeight;
+ lpmmi->ptMinTrackSize.x = m_iMinWidth;
+ return 0;
+ }
+ break;
+
+ case WM_MEASUREITEM:
+ if (!Menu_MeasureItem(lParam)) {
+ MEASUREITEMSTRUCT *param = (MEASUREITEMSTRUCT *)lParam;
+ if (param && param->CtlID)
+ if (CCtrlBase *ctrl = FindControl(param->CtlID))
+ return ctrl->OnMeasureItem(param);
+ }
+ return FALSE;
+
+ case WM_DRAWITEM:
+ if (!Menu_DrawItem(lParam)) {
+ DRAWITEMSTRUCT *param = (DRAWITEMSTRUCT *)lParam;
+ if (param && param->CtlID)
+ if (CCtrlBase *ctrl = FindControl(param->CtlID))
+ return ctrl->OnDrawItem(param);
+ }
+ return FALSE;
+
+ case WM_DELETEITEM:
+ {
+ DELETEITEMSTRUCT *param = (DELETEITEMSTRUCT *)lParam;
+ if (param && param->CtlID)
+ if (CCtrlBase *ctrl = FindControl(param->CtlID))
+ return ctrl->OnDeleteItem(param);
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ {
+ HWND hwndCtrl = (HWND)lParam;
+ uint16_t idCtrl = LOWORD(wParam);
+ uint16_t idCode = HIWORD(wParam);
+ if (CCtrlBase *ctrl = FindControl(idCtrl)) {
+ BOOL result = ctrl->OnCommand(hwndCtrl, idCtrl, idCode);
+ if (result != FALSE)
+ return result;
+ }
+
+ if (idCode == BN_CLICKED) {
+ // close dialog automatically if 'Cancel' button is pressed
+ if (idCtrl == IDCANCEL && (m_autoClose & CLOSE_ON_CANCEL)) {
+ m_bExiting = true;
+ PostMessage(m_hwnd, WM_CLOSE, 0, 0);
+ }
+
+ // close dialog automatically if 'OK' button is pressed
+ if (idCtrl == IDOK && (m_autoClose & CLOSE_ON_OK)) {
+ // validate dialog data first
+ if (VerifyControls(&CCtrlBase::OnApply)) {
+ m_bExiting = true;
+
+ // everything ok? good, let's close it
+ if (OnApply()) {
+ m_bSucceeded = true;
+ PostMessage(m_hwnd, WM_CLOSE, 0, 0);
+ }
+ else m_bExiting = false;
+ }
+ }
+ }
+ }
+ return FALSE;
+
+ case WM_NOTIFY:
+ {
+ int idCtrl = wParam;
+ NMHDR *pnmh = (NMHDR *)lParam;
+ if (pnmh->idFrom == 0) {
+ switch (pnmh->code) {
+ case PSN_APPLY:
+ if (LPPSHNOTIFY(lParam)->lParam != 3) // IDC_APPLY
+ m_bExiting = true;
+
+ if (!VerifyControls(&CCtrlBase::OnApply))
+ m_bExiting = false;
+ else if (!OnApply())
+ m_bExiting = false;
+ break;
+
+ case PSN_RESET:
+ NotifyControls(&CCtrlBase::OnReset);
+ OnReset();
+ break;
+
+ case PSN_WIZFINISH:
+ m_OnFinishWizard(this);
+ break;
+ }
+ }
+
+ if (CCtrlBase *ctrl = FindControl(pnmh->idFrom))
+ return ctrl->OnNotify(idCtrl, pnmh);
+ }
+ return FALSE;
+
+ case WM_HSCROLL:
+ if (auto *pCtrl = FindControl(HWND(lParam)))
+ pCtrl->OnCommand(HWND(lParam), pCtrl->m_idCtrl, WM_HSCROLL);
+ break;
+
+ case PSM_CHANGED:
+ if (m_bInitialized)
+ OnChange();
+ break;
+
+ case WM_CONTEXTMENU:
+ if (CCtrlBase *ctrl = FindControl(HWND(wParam))) {
+ CContextMenuPos pos = {};
+ if (lParam != -1) {
+ pos.pt.x = GET_X_LPARAM(lParam);
+ pos.pt.y = GET_Y_LPARAM(lParam);
+ }
+ ctrl->GetCaretPos(pos);
+ ctrl->OnBuildMenu(&pos);
+ }
+ break;
+
+ case WM_SIZE:
+ OnResize();
+ return TRUE;
+
+ case WM_TIMER:
+ if (CTimer *timer = FindTimer(wParam))
+ return timer->OnTimer();
+ return FALSE;
+
+ case WM_CLOSE:
+ if (OnClose()) {
+ m_bExiting = true;
+ if (m_isModal)
+ EndModal(m_bSucceeded ? IDOK : FALSE);
+ else
+ DestroyWindow(m_hwnd);
+ }
+ return TRUE;
+
+ case WM_DESTROY:
+ m_bExiting = true;
+ OnDestroy();
+ NotifyControls(&CCtrlBase::OnDestroy);
+ {
+ mir_cslock lck(csDialogs);
+ int idx = arDialogs.getIndex(this);
+ if (idx != -1)
+ arDialogs.remove(idx);
+ }
+ m_hwnd = nullptr;
+ if (m_bInitialized) {
+ if (m_isModal)
+ m_isModal = false;
+ else // modeless dialogs MUST be allocated with 'new'
+ delete this;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+INT_PTR CALLBACK CDlgBase::GlobalDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CDlgBase *wnd;
+ if (msg == WM_INITDIALOG) {
+ wnd = (CDlgBase*)lParam;
+ wnd->m_hwnd = hwnd;
+
+ mir_cslock lck(csDialogs);
+ arDialogs.insert(wnd);
+ }
+ else wnd = CDlgBase::Find(hwnd);
+
+ return (wnd == nullptr) ? FALSE : wnd->DlgProc(msg, wParam, lParam);
+}
+
+void CDlgBase::ThemeDialogBackground(BOOL tabbed)
+{
+ EnableThemeDialogTexture(m_hwnd, (tabbed ? ETDT_ENABLE : ETDT_DISABLE) | ETDT_USETABTEXTURE);
+}
+
+void CDlgBase::AddControl(CCtrlBase *ctrl)
+{
+ m_controls.insert(ctrl);
+}
+
+void CDlgBase::RemoveControl(CCtrlBase *ctrl)
+{
+ m_controls.remove(ctrl);
+}
+
+void CDlgBase::NotifyControls(void (CCtrlBase::*fn)())
+{
+ for (auto &it : m_controls)
+ (it->*fn)();
+}
+
+bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)())
+{
+ for (auto &it : m_controls)
+ if (!(it->*fn)())
+ return false;
+
+ return true;
+}
+
+CCtrlBase* CDlgBase::FindControl(int idCtrl)
+{
+ CCtrlBase search(nullptr, idCtrl);
+ return m_controls.find(&search);
+}
+
+CCtrlBase* CDlgBase::FindControl(HWND hwnd)
+{
+ for (auto &it : m_controls)
+ if (it->GetHwnd() == hwnd)
+ return it;
+
+ return nullptr;
+}
+
+void CDlgBase::AddTimer(CTimer *timer)
+{
+ m_timers.insert(timer);
+}
+
+void CDlgBase::RemoveTimer(UINT_PTR idEvent)
+{
+ CTimer search(nullptr, idEvent);
+ m_timers.remove(&search);
+}
+
+CTimer* CDlgBase::FindTimer(int idEvent)
+{
+ CTimer search(nullptr, idEvent);
+ return m_timers.find(&search);
+}
+
+CDlgBase* CDlgBase::Find(HWND hwnd)
+{
+ PVOID bullshit[2]; // vfptr + hwnd
+ bullshit[1] = hwnd;
+ return arDialogs.find((CDlgBase*)&bullshit);
+}
diff --git a/src/mir_core/src/Windows/CProgress.cpp b/src/mir_core/src/Windows/CProgress.cpp index 991c6f239d..e2d5321f59 100644 --- a/src/mir_core/src/Windows/CProgress.cpp +++ b/src/mir_core/src/Windows/CProgress.cpp @@ -1,53 +1,53 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlProgress - -CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{ -} - -void CCtrlProgress::SetRange(uint16_t max, uint16_t min) -{ - SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max)); -} - -void CCtrlProgress::SetPosition(uint16_t value) -{ - SendMsg(PBM_SETPOS, value, 0); -} - -void CCtrlProgress::SetStep(uint16_t value) -{ - SendMsg(PBM_SETSTEP, value, 0); -} - -uint16_t CCtrlProgress::Move(uint16_t delta) -{ - return delta == 0 - ? SendMsg(PBM_STEPIT, 0, 0) - : SendMsg(PBM_DELTAPOS, delta, 0); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CCtrlProgress
+
+CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl)
+{
+}
+
+void CCtrlProgress::SetRange(uint16_t max, uint16_t min)
+{
+ SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max));
+}
+
+void CCtrlProgress::SetPosition(uint16_t value)
+{
+ SendMsg(PBM_SETPOS, value, 0);
+}
+
+void CCtrlProgress::SetStep(uint16_t value)
+{
+ SendMsg(PBM_SETSTEP, value, 0);
+}
+
+uint16_t CCtrlProgress::Move(uint16_t delta)
+{
+ return delta == 0
+ ? SendMsg(PBM_STEPIT, 0, 0)
+ : SendMsg(PBM_DELTAPOS, delta, 0);
+}
diff --git a/src/mir_core/src/Windows/CSplitter.cpp b/src/mir_core/src/Windows/CSplitter.cpp index e2ee6b6fc8..153beaf95b 100644 --- a/src/mir_core/src/Windows/CSplitter.cpp +++ b/src/mir_core/src/Windows/CSplitter.cpp @@ -1,83 +1,83 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CSplitter - -CSplitter::CSplitter(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_iPosition(0) -{ -} - -void CSplitter::OnInit() -{ - CSuper::OnInit(); - Subclass(); -} - -LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_NCHITTEST: - return HTCLIENT; - - case WM_SETCURSOR: - RECT rc; - GetClientRect(m_hwnd, &rc); - SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE); - return TRUE; - - case WM_LBUTTONDOWN: - SetCapture(m_hwnd); - return 0; - - case WM_MOUSEMOVE: - if (GetCapture() == m_hwnd) { - POINT pt = { 0, 0 }; - GetClientRect(m_hwnd, &rc); - if (rc.right > rc.bottom) { - pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.y; - } - else { - pt.x = LOWORD(GetMessagePos()) + rc.right / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.x; - } - - OnChange(this); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - } - return 0; - - case WM_LBUTTONUP: - ReleaseCapture(); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - return 0; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CSplitter
+
+CSplitter::CSplitter(CDlgBase *wnd, int idCtrl)
+ : CCtrlBase(wnd, idCtrl),
+ m_iPosition(0)
+{
+}
+
+void CSplitter::OnInit()
+{
+ CSuper::OnInit();
+ Subclass();
+}
+
+LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_NCHITTEST:
+ return HTCLIENT;
+
+ case WM_SETCURSOR:
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE);
+ return TRUE;
+
+ case WM_LBUTTONDOWN:
+ SetCapture(m_hwnd);
+ return 0;
+
+ case WM_MOUSEMOVE:
+ if (GetCapture() == m_hwnd) {
+ POINT pt = { 0, 0 };
+ GetClientRect(m_hwnd, &rc);
+ if (rc.right > rc.bottom) {
+ pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2;
+ ScreenToClient(m_parentWnd->GetHwnd(), &pt);
+ m_iPosition = pt.y;
+ }
+ else {
+ pt.x = LOWORD(GetMessagePos()) + rc.right / 2;
+ ScreenToClient(m_parentWnd->GetHwnd(), &pt);
+ m_iPosition = pt.x;
+ }
+
+ OnChange(this);
+ PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0);
+ }
+ return 0;
+
+ case WM_LBUTTONUP:
+ ReleaseCapture();
+ PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0);
+ return 0;
+ }
+
+ return CSuper::CustomWndProc(msg, wParam, lParam);
+}
diff --git a/src/mir_core/src/Windows/CTimer.cpp b/src/mir_core/src/Windows/CTimer.cpp index b346138707..4f1141f329 100644 --- a/src/mir_core/src/Windows/CTimer.cpp +++ b/src/mir_core/src/Windows/CTimer.cpp @@ -1,90 +1,90 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CTimer - -CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent) - : m_wnd(wnd), m_idEvent(idEvent) -{ - if (wnd) - wnd->AddTimer(this); -} - -CTimer::~CTimer() -{ - if (m_wnd) - m_wnd->RemoveTimer(m_idEvent); -} - -BOOL CTimer::OnTimer() -{ - OnEvent(this); - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CTimer::Start(int elapse) -{ - ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr); -} - -bool CTimer::Stop() -{ - return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct TStartParam -{ - CTimer *pTimer; - int period; -}; - -static INT_PTR CALLBACK stubStart(void *param) -{ - auto *p = (TStartParam *)param; - return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr); -} - -void CTimer::StartSafe(int elapse) -{ - TStartParam param = { this, elapse }; - CallFunctionSync(stubStart, ¶m); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static INT_PTR CALLBACK stubStop(void *param) -{ - auto *p = (CTimer*)param; - return ::KillTimer(p->GetHwnd(), p->GetEventId()); -} - -void CTimer::StopSafe() -{ - CallFunctionSync(stubStop, this); -} +/*
+
+Object UI extensions
+Copyright (c) 2008 Victor Pavlychko, George Hazan
+Copyright (C) 2012-23 Miranda NG team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CTimer
+
+CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent)
+ : m_wnd(wnd), m_idEvent(idEvent)
+{
+ if (wnd)
+ wnd->AddTimer(this);
+}
+
+CTimer::~CTimer()
+{
+ if (m_wnd)
+ m_wnd->RemoveTimer(m_idEvent);
+}
+
+BOOL CTimer::OnTimer()
+{
+ OnEvent(this);
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CTimer::Start(int elapse)
+{
+ ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr);
+}
+
+bool CTimer::Stop()
+{
+ return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct TStartParam
+{
+ CTimer *pTimer;
+ int period;
+};
+
+static INT_PTR CALLBACK stubStart(void *param)
+{
+ auto *p = (TStartParam *)param;
+ return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr);
+}
+
+void CTimer::StartSafe(int elapse)
+{
+ TStartParam param = { this, elapse };
+ CallFunctionSync(stubStart, ¶m);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR CALLBACK stubStop(void *param)
+{
+ auto *p = (CTimer*)param;
+ return ::KillTimer(p->GetHwnd(), p->GetEventId());
+}
+
+void CTimer::StopSafe()
+{
+ CallFunctionSync(stubStop, this);
+}
diff --git a/src/mir_core/src/Windows/cmdline.cpp b/src/mir_core/src/Windows/cmdline.cpp index a4a61b2c6e..0e43610e0c 100644 --- a/src/mir_core/src/Windows/cmdline.cpp +++ b/src/mir_core/src/Windows/cmdline.cpp @@ -1,77 +1,77 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -/* command line support */ - -struct CmdLineParam -{ - __inline CmdLineParam(const wchar_t *_name, const wchar_t *_value) : - name(mir_wstrdup(_name)), value(mir_wstrdup(_value)) - {} - - ptrW name, value; -}; - -static int CompareParams(const CmdLineParam *p1, const CmdLineParam *p2) -{ - return wcscmp(p1->name, p2->name); -} - -static OBJLIST<CmdLineParam> arParams(5, CompareParams); - -MIR_CORE_DLL(void) CmdLine_Parse(const wchar_t *ptszCmdLine) -{ - int nArgs = 0; - wchar_t **pArgs = CommandLineToArgvW(ptszCmdLine, &nArgs); - if (pArgs == nullptr) - return; - - for (int i=0; i < nArgs; i++) { - wchar_t *pOptionName = pArgs[i], *p; - - // not an option? skip it - if (*pOptionName != '/' && *pOptionName != '-') - continue; - - pOptionName++; - if ((p = wcspbrk(pOptionName, L"=:")) == nullptr) { // no more text in string - arParams.insert(new CmdLineParam(pOptionName, L"")); - break; - } - - // parameter with value - *p = 0; - arParams.insert(new CmdLineParam(pOptionName, p+1)); - } - - LocalFree(pArgs); -} - -MIR_CORE_DLL(const wchar_t*) CmdLine_GetOption(const wchar_t* ptszParameter) -{ - CmdLineParam tmp(ptszParameter, nullptr); - int idx = arParams.getIndex(&tmp); - return (idx == -1) ? nullptr : arParams[idx].value.get(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+/* command line support */
+
+struct CmdLineParam
+{
+ __inline CmdLineParam(const wchar_t *_name, const wchar_t *_value) :
+ name(mir_wstrdup(_name)), value(mir_wstrdup(_value))
+ {}
+
+ ptrW name, value;
+};
+
+static int CompareParams(const CmdLineParam *p1, const CmdLineParam *p2)
+{
+ return wcscmp(p1->name, p2->name);
+}
+
+static OBJLIST<CmdLineParam> arParams(5, CompareParams);
+
+MIR_CORE_DLL(void) CmdLine_Parse(const wchar_t *ptszCmdLine)
+{
+ int nArgs = 0;
+ wchar_t **pArgs = CommandLineToArgvW(ptszCmdLine, &nArgs);
+ if (pArgs == nullptr)
+ return;
+
+ for (int i=0; i < nArgs; i++) {
+ wchar_t *pOptionName = pArgs[i], *p;
+
+ // not an option? skip it
+ if (*pOptionName != '/' && *pOptionName != '-')
+ continue;
+
+ pOptionName++;
+ if ((p = wcspbrk(pOptionName, L"=:")) == nullptr) { // no more text in string
+ arParams.insert(new CmdLineParam(pOptionName, L""));
+ break;
+ }
+
+ // parameter with value
+ *p = 0;
+ arParams.insert(new CmdLineParam(pOptionName, p+1));
+ }
+
+ LocalFree(pArgs);
+}
+
+MIR_CORE_DLL(const wchar_t*) CmdLine_GetOption(const wchar_t* ptszParameter)
+{
+ CmdLineParam tmp(ptszParameter, nullptr);
+ int idx = arParams.getIndex(&tmp);
+ return (idx == -1) ? nullptr : arParams[idx].value.get();
+}
diff --git a/src/mir_core/src/Windows/colourpicker.cpp b/src/mir_core/src/Windows/colourpicker.cpp index 3272cc37ac..135f8c4096 100644 --- a/src/mir_core/src/Windows/colourpicker.cpp +++ b/src/mir_core/src/Windows/colourpicker.cpp @@ -1,105 +1,105 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -static LRESULT CALLBACK ColourPickerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) { - case WM_CREATE: - SetWindowLongPtr(hwnd, 0, 0); - SetWindowLongPtr(hwnd, sizeof(COLORREF), 0); - break; - - case CPM_SETDEFAULTCOLOUR: - SetWindowLongPtr(hwnd, sizeof(COLORREF), lParam); - break; - - case CPM_GETDEFAULTCOLOUR: - return GetWindowLongPtr(hwnd, sizeof(COLORREF)); - - case CPM_SETCOLOUR: - SetWindowLongPtr(hwnd, 0, lParam); - InvalidateRect(hwnd, nullptr, FALSE); - break; - - case CPM_GETCOLOUR: - return GetWindowLongPtr(hwnd, 0); - - case WM_LBUTTONUP: - { - COLORREF custColours[16] = { 0 }; - custColours[0] = GetWindowLongPtr(hwnd, sizeof(COLORREF)); - - CHOOSECOLOR cc = { 0 }; - cc.lStructSize = sizeof(CHOOSECOLOR); - cc.hwndOwner = hwnd; - cc.hInstance = (HWND)g_hInst; - cc.rgbResult = GetWindowLongPtr(hwnd, 0); - cc.lpCustColors = custColours; - cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; - if (ChooseColor(&cc)) { - SetWindowLongPtr(hwnd, 0, cc.rgbResult); - SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), CPN_COLOURCHANGED), (LPARAM)hwnd); - InvalidateRect(hwnd, nullptr, FALSE); - } - } - break; - - case WM_ENABLE: - InvalidateRect(hwnd, nullptr, FALSE); - break; - - case WM_NCPAINT: - case WM_PAINT: - PAINTSTRUCT ps; - HDC hdc1 = BeginPaint(hwnd, &ps); - - RECT rc; - GetClientRect(hwnd, &rc); - DrawEdge(hdc1, &rc, EDGE_ETCHED, BF_RECT); - InflateRect(&rc, -2, -2); - - HBRUSH hBrush = (IsWindowEnabled(hwnd)) ? CreateSolidBrush(GetWindowLongPtr(hwnd, 0)) : CreateHatchBrush(HS_BDIAGONAL, GetSysColor(COLOR_GRAYTEXT)); - SetBkColor(hdc1, GetSysColor(COLOR_BTNFACE)); - FillRect(hdc1, &rc, hBrush); - DeleteObject(hBrush); - - EndPaint(hwnd, &ps); - break; - } - return DefWindowProc(hwnd, message, wParam, lParam); -} - -void InitColourPicker(void) -{ - WNDCLASS wcl = { 0 }; - wcl.lpfnWndProc = ColourPickerWndProc; - wcl.cbWndExtra = sizeof(COLORREF) * 2; - wcl.hInstance = g_hInst; - wcl.lpszClassName = _T(WNDCLASS_COLOURPICKER); - wcl.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); - wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; - RegisterClass(&wcl); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+static LRESULT CALLBACK ColourPickerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_CREATE:
+ SetWindowLongPtr(hwnd, 0, 0);
+ SetWindowLongPtr(hwnd, sizeof(COLORREF), 0);
+ break;
+
+ case CPM_SETDEFAULTCOLOUR:
+ SetWindowLongPtr(hwnd, sizeof(COLORREF), lParam);
+ break;
+
+ case CPM_GETDEFAULTCOLOUR:
+ return GetWindowLongPtr(hwnd, sizeof(COLORREF));
+
+ case CPM_SETCOLOUR:
+ SetWindowLongPtr(hwnd, 0, lParam);
+ InvalidateRect(hwnd, nullptr, FALSE);
+ break;
+
+ case CPM_GETCOLOUR:
+ return GetWindowLongPtr(hwnd, 0);
+
+ case WM_LBUTTONUP:
+ {
+ COLORREF custColours[16] = { 0 };
+ custColours[0] = GetWindowLongPtr(hwnd, sizeof(COLORREF));
+
+ CHOOSECOLOR cc = { 0 };
+ cc.lStructSize = sizeof(CHOOSECOLOR);
+ cc.hwndOwner = hwnd;
+ cc.hInstance = (HWND)g_hInst;
+ cc.rgbResult = GetWindowLongPtr(hwnd, 0);
+ cc.lpCustColors = custColours;
+ cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT;
+ if (ChooseColor(&cc)) {
+ SetWindowLongPtr(hwnd, 0, cc.rgbResult);
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), CPN_COLOURCHANGED), (LPARAM)hwnd);
+ InvalidateRect(hwnd, nullptr, FALSE);
+ }
+ }
+ break;
+
+ case WM_ENABLE:
+ InvalidateRect(hwnd, nullptr, FALSE);
+ break;
+
+ case WM_NCPAINT:
+ case WM_PAINT:
+ PAINTSTRUCT ps;
+ HDC hdc1 = BeginPaint(hwnd, &ps);
+
+ RECT rc;
+ GetClientRect(hwnd, &rc);
+ DrawEdge(hdc1, &rc, EDGE_ETCHED, BF_RECT);
+ InflateRect(&rc, -2, -2);
+
+ HBRUSH hBrush = (IsWindowEnabled(hwnd)) ? CreateSolidBrush(GetWindowLongPtr(hwnd, 0)) : CreateHatchBrush(HS_BDIAGONAL, GetSysColor(COLOR_GRAYTEXT));
+ SetBkColor(hdc1, GetSysColor(COLOR_BTNFACE));
+ FillRect(hdc1, &rc, hBrush);
+ DeleteObject(hBrush);
+
+ EndPaint(hwnd, &ps);
+ break;
+ }
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+void InitColourPicker(void)
+{
+ WNDCLASS wcl = { 0 };
+ wcl.lpfnWndProc = ColourPickerWndProc;
+ wcl.cbWndExtra = sizeof(COLORREF) * 2;
+ wcl.hInstance = g_hInst;
+ wcl.lpszClassName = _T(WNDCLASS_COLOURPICKER);
+ wcl.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
+ wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
+ RegisterClass(&wcl);
+}
diff --git a/src/mir_core/src/Windows/diatheme.cpp b/src/mir_core/src/Windows/diatheme.cpp index 2c16131643..12e37ff578 100644 --- a/src/mir_core/src/Windows/diatheme.cpp +++ b/src/mir_core/src/Windows/diatheme.cpp @@ -1,170 +1,170 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "../stdafx.h" -#include "diatheme.h" - -// The following code was borrowed from Notepad2 sources and adapted for Miranda NG - -typedef HTHEME (WINAPI *OTD)(HWND hwnd,LPCWSTR pszClassList); -typedef HRESULT (WINAPI *GTSF)(HTHEME hTheme,int iFontId,LOGFONT *plf); -typedef HRESULT (WINAPI *CTD)(HTHEME hTheme); - -BOOL GetThemedDialogFont(LPWSTR lpFaceName, WORD *wSize) -{ - BOOL bSucceed = FALSE; - - HDC hDC = GetDC(nullptr); - int iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY); - ReleaseDC(nullptr, hDC); - - if (HMODULE hModUxTheme = GetModuleHandle(L"uxtheme.dll")) { - OTD _OpenThemeData = (OTD)GetProcAddress(hModUxTheme, "OpenThemeData"); - GTSF _GetThemeSysFont = (GTSF)GetProcAddress(hModUxTheme, "GetThemeSysFont"); - CTD _CloseThemeData = (CTD)GetProcAddress(hModUxTheme, "CloseThemeData"); - - if (_CloseThemeData && _GetThemeSysFont && _OpenThemeData) { - if (HTHEME hTheme = _OpenThemeData(NULL, L"WINDOWSTYLE;WINDOW")) { - LOGFONT lf; - if (S_OK == _GetThemeSysFont(hTheme,/*TMT_MSGBOXFONT*/805, &lf)) { - if (lf.lfHeight < 0) - lf.lfHeight = -lf.lfHeight; - *wSize = (WORD)MulDiv(lf.lfHeight, 72, iLogPixelsY); - if (*wSize == 0) - *wSize = 8; - wcsncpy_s(lpFaceName, LF_FACESIZE, lf.lfFaceName, _TRUNCATE); - bSucceed = TRUE; - } - _CloseThemeData(hTheme); - } - } - } - - if (!bSucceed) { - NONCLIENTMETRICS ncm; - ncm.cbSize = sizeof(NONCLIENTMETRICS); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0); - if (ncm.lfMessageFont.lfHeight < 0) - ncm.lfMessageFont.lfHeight = -ncm.lfMessageFont.lfHeight; - *wSize = (WORD)MulDiv(ncm.lfMessageFont.lfHeight, 72, iLogPixelsY); - if (*wSize == 0) - *wSize = 8; - - wcsncpy_s(lpFaceName, LF_FACESIZE, ncm.lfMessageFont.lfFaceName, _TRUNCATE); - } - - return TRUE; -} - -BOOL DialogTemplate_IsDialogEx(const DLGTEMPLATE *pTemplate) -{ - return ((DLGTEMPLATEEX *)pTemplate)->signature == 0xFFFF; -} - -BOOL DialogTemplate_HasFont(const DLGTEMPLATE *pTemplate) -{ - return (DS_SETFONT & (DialogTemplate_IsDialogEx(pTemplate) ? ((DLGTEMPLATEEX *)pTemplate)->style : pTemplate->style)); -} - -int DialogTemplate_FontAttrSize(BOOL bDialogEx) -{ - return (int)sizeof(WORD) * (bDialogEx ? 3 : 1); -} - -BYTE *DialogTemplate_GetFontSizeField(const DLGTEMPLATE *pTemplate) -{ - BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate); - - WORD *pw; - if (bDialogEx) - pw = (WORD *)((DLGTEMPLATEEX *)pTemplate + 1); - else - pw = (WORD *)(pTemplate + 1); - - if (*pw == (WORD)-1) - pw += 2; - else - while (*pw++); - - if (*pw == (WORD)-1) - pw += 2; - else - while (*pw++); - - while (*pw++); - - return (BYTE *)pw; -} - -DLGTEMPLATE* LoadThemedDialogTemplate(LPCTSTR lpDialogTemplateID, HINSTANCE hInstance) -{ - WCHAR wchFaceName[LF_FACESIZE]; - - HRSRC hRsrc = FindResource(hInstance, lpDialogTemplateID, RT_DIALOG); - if (hRsrc == nullptr) - return nullptr; - - HGLOBAL hRsrcMem = LoadResource(hInstance, hRsrc); - if (hRsrcMem == nullptr) - return nullptr; - - DLGTEMPLATE *pRsrcMem = (DLGTEMPLATE *)LockResource(hRsrcMem); - if (pRsrcMem == nullptr) - return nullptr; - - size_t dwTemplateSize = (UINT)SizeofResource(hInstance, hRsrc); - UnlockResource(hRsrcMem); - FreeResource(hRsrcMem); - - if (dwTemplateSize == 0) - return nullptr; - - auto *pTemplate = (DLGTEMPLATE *)mir_alloc(dwTemplateSize + LF_FACESIZE * 2); - memcpy(pTemplate, pRsrcMem, dwTemplateSize); - UnlockResource(hRsrcMem); - FreeResource(hRsrcMem); - - WORD wFontSize; - if (!GetThemedDialogFont(wchFaceName, &wFontSize)) - return(pTemplate); - - BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate); - BOOL bHasFont = DialogTemplate_HasFont(pTemplate); - size_t cbFontAttr = DialogTemplate_FontAttrSize(bDialogEx); - - if (bDialogEx) - ((DLGTEMPLATEEX *)pTemplate)->style |= DS_SHELLFONT; - else - pTemplate->style |= DS_SHELLFONT; - - size_t cbNew = cbFontAttr + ((mir_wstrlen(wchFaceName) + 1) * sizeof(WCHAR)); - BYTE *pbNew = (BYTE *)wchFaceName; - - BYTE *pb = DialogTemplate_GetFontSizeField(pTemplate); - size_t cbOld = (int)(bHasFont ? cbFontAttr + 2 * (mir_wstrlen((WCHAR *)(pb + cbFontAttr)) + 1) : 0); - - BYTE *pOldControls = (BYTE *)(((DWORD_PTR)pb + cbOld + 3) & ~(DWORD_PTR)3); - BYTE *pNewControls = (BYTE *)(((DWORD_PTR)pb + cbNew + 3) & ~(DWORD_PTR)3); - - WORD nCtrl = bDialogEx ? (WORD)((DLGTEMPLATEEX *)pTemplate)->cDlgItems : (WORD)pTemplate->cdit; - if (cbNew != cbOld && nCtrl > 0) - MoveMemory(pNewControls, pOldControls, dwTemplateSize - (pOldControls - (BYTE *)pTemplate)); - - *(WORD *)pb = wFontSize; - MoveMemory(pb + cbFontAttr, pbNew, cbNew - cbFontAttr); - return pTemplate; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "../stdafx.h"
+#include "diatheme.h"
+
+// The following code was borrowed from Notepad2 sources and adapted for Miranda NG
+
+typedef HTHEME (WINAPI *OTD)(HWND hwnd,LPCWSTR pszClassList);
+typedef HRESULT (WINAPI *GTSF)(HTHEME hTheme,int iFontId,LOGFONT *plf);
+typedef HRESULT (WINAPI *CTD)(HTHEME hTheme);
+
+BOOL GetThemedDialogFont(LPWSTR lpFaceName, WORD *wSize)
+{
+ BOOL bSucceed = FALSE;
+
+ HDC hDC = GetDC(nullptr);
+ int iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY);
+ ReleaseDC(nullptr, hDC);
+
+ if (HMODULE hModUxTheme = GetModuleHandle(L"uxtheme.dll")) {
+ OTD _OpenThemeData = (OTD)GetProcAddress(hModUxTheme, "OpenThemeData");
+ GTSF _GetThemeSysFont = (GTSF)GetProcAddress(hModUxTheme, "GetThemeSysFont");
+ CTD _CloseThemeData = (CTD)GetProcAddress(hModUxTheme, "CloseThemeData");
+
+ if (_CloseThemeData && _GetThemeSysFont && _OpenThemeData) {
+ if (HTHEME hTheme = _OpenThemeData(NULL, L"WINDOWSTYLE;WINDOW")) {
+ LOGFONT lf;
+ if (S_OK == _GetThemeSysFont(hTheme,/*TMT_MSGBOXFONT*/805, &lf)) {
+ if (lf.lfHeight < 0)
+ lf.lfHeight = -lf.lfHeight;
+ *wSize = (WORD)MulDiv(lf.lfHeight, 72, iLogPixelsY);
+ if (*wSize == 0)
+ *wSize = 8;
+ wcsncpy_s(lpFaceName, LF_FACESIZE, lf.lfFaceName, _TRUNCATE);
+ bSucceed = TRUE;
+ }
+ _CloseThemeData(hTheme);
+ }
+ }
+ }
+
+ if (!bSucceed) {
+ NONCLIENTMETRICS ncm;
+ ncm.cbSize = sizeof(NONCLIENTMETRICS);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);
+ if (ncm.lfMessageFont.lfHeight < 0)
+ ncm.lfMessageFont.lfHeight = -ncm.lfMessageFont.lfHeight;
+ *wSize = (WORD)MulDiv(ncm.lfMessageFont.lfHeight, 72, iLogPixelsY);
+ if (*wSize == 0)
+ *wSize = 8;
+
+ wcsncpy_s(lpFaceName, LF_FACESIZE, ncm.lfMessageFont.lfFaceName, _TRUNCATE);
+ }
+
+ return TRUE;
+}
+
+BOOL DialogTemplate_IsDialogEx(const DLGTEMPLATE *pTemplate)
+{
+ return ((DLGTEMPLATEEX *)pTemplate)->signature == 0xFFFF;
+}
+
+BOOL DialogTemplate_HasFont(const DLGTEMPLATE *pTemplate)
+{
+ return (DS_SETFONT & (DialogTemplate_IsDialogEx(pTemplate) ? ((DLGTEMPLATEEX *)pTemplate)->style : pTemplate->style));
+}
+
+int DialogTemplate_FontAttrSize(BOOL bDialogEx)
+{
+ return (int)sizeof(WORD) * (bDialogEx ? 3 : 1);
+}
+
+BYTE *DialogTemplate_GetFontSizeField(const DLGTEMPLATE *pTemplate)
+{
+ BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate);
+
+ WORD *pw;
+ if (bDialogEx)
+ pw = (WORD *)((DLGTEMPLATEEX *)pTemplate + 1);
+ else
+ pw = (WORD *)(pTemplate + 1);
+
+ if (*pw == (WORD)-1)
+ pw += 2;
+ else
+ while (*pw++);
+
+ if (*pw == (WORD)-1)
+ pw += 2;
+ else
+ while (*pw++);
+
+ while (*pw++);
+
+ return (BYTE *)pw;
+}
+
+DLGTEMPLATE* LoadThemedDialogTemplate(LPCTSTR lpDialogTemplateID, HINSTANCE hInstance)
+{
+ WCHAR wchFaceName[LF_FACESIZE];
+
+ HRSRC hRsrc = FindResource(hInstance, lpDialogTemplateID, RT_DIALOG);
+ if (hRsrc == nullptr)
+ return nullptr;
+
+ HGLOBAL hRsrcMem = LoadResource(hInstance, hRsrc);
+ if (hRsrcMem == nullptr)
+ return nullptr;
+
+ DLGTEMPLATE *pRsrcMem = (DLGTEMPLATE *)LockResource(hRsrcMem);
+ if (pRsrcMem == nullptr)
+ return nullptr;
+
+ size_t dwTemplateSize = (UINT)SizeofResource(hInstance, hRsrc);
+ UnlockResource(hRsrcMem);
+ FreeResource(hRsrcMem);
+
+ if (dwTemplateSize == 0)
+ return nullptr;
+
+ auto *pTemplate = (DLGTEMPLATE *)mir_alloc(dwTemplateSize + LF_FACESIZE * 2);
+ memcpy(pTemplate, pRsrcMem, dwTemplateSize);
+ UnlockResource(hRsrcMem);
+ FreeResource(hRsrcMem);
+
+ WORD wFontSize;
+ if (!GetThemedDialogFont(wchFaceName, &wFontSize))
+ return(pTemplate);
+
+ BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate);
+ BOOL bHasFont = DialogTemplate_HasFont(pTemplate);
+ size_t cbFontAttr = DialogTemplate_FontAttrSize(bDialogEx);
+
+ if (bDialogEx)
+ ((DLGTEMPLATEEX *)pTemplate)->style |= DS_SHELLFONT;
+ else
+ pTemplate->style |= DS_SHELLFONT;
+
+ size_t cbNew = cbFontAttr + ((mir_wstrlen(wchFaceName) + 1) * sizeof(WCHAR));
+ BYTE *pbNew = (BYTE *)wchFaceName;
+
+ BYTE *pb = DialogTemplate_GetFontSizeField(pTemplate);
+ size_t cbOld = (int)(bHasFont ? cbFontAttr + 2 * (mir_wstrlen((WCHAR *)(pb + cbFontAttr)) + 1) : 0);
+
+ BYTE *pOldControls = (BYTE *)(((DWORD_PTR)pb + cbOld + 3) & ~(DWORD_PTR)3);
+ BYTE *pNewControls = (BYTE *)(((DWORD_PTR)pb + cbNew + 3) & ~(DWORD_PTR)3);
+
+ WORD nCtrl = bDialogEx ? (WORD)((DLGTEMPLATEEX *)pTemplate)->cDlgItems : (WORD)pTemplate->cdit;
+ if (cbNew != cbOld && nCtrl > 0)
+ MoveMemory(pNewControls, pOldControls, dwTemplateSize - (pOldControls - (BYTE *)pTemplate));
+
+ *(WORD *)pb = wFontSize;
+ MoveMemory(pb + cbFontAttr, pbNew, cbNew - cbFontAttr);
+ return pTemplate;
+}
diff --git a/src/mir_core/src/Windows/fileutil.cpp b/src/mir_core/src/Windows/fileutil.cpp index 2522cc7cbe..a0dcee68f6 100644 --- a/src/mir_core/src/Windows/fileutil.cpp +++ b/src/mir_core/src/Windows/fileutil.cpp @@ -1,78 +1,78 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++() -{ - if (ptr != nullptr) { - if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) { - ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE; - ptr = nullptr; - } - } - return *this; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath) -{ - if (pwszPath != nullptr) - m_hFind = ::FindFirstFileW(pwszPath, &m_data); -} - -MFilePath::MFileIterator::~MFileIterator() -{ - if (m_hFind != INVALID_HANDLE_VALUE) - ::FindClose(m_hFind); -} - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin() -{ - if (m_hFind == INVALID_HANDLE_VALUE) - return MFilePath::MFileIterator::iterator(nullptr); - - return MFilePath::MFileIterator::iterator(this); -} - -bool MFilePath::MFileIterator::isDir() const -{ - if (m_hFind == INVALID_HANDLE_VALUE) - return false; - - return (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool MFilePath::isExist() const -{ - return _waccess(c_str(), 0) == 0; -} - -bool MFilePath::move(const wchar_t *pwszDest) -{ - return MoveFileW(c_str(), pwszDest) != 0; -} - -MFilePath::MFileIterator MFilePath::search() -{ - return MFileIterator(c_str()); -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "../stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++()
+{
+ if (ptr != nullptr) {
+ if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) {
+ ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE;
+ ptr = nullptr;
+ }
+ }
+ return *this;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath)
+{
+ if (pwszPath != nullptr)
+ m_hFind = ::FindFirstFileW(pwszPath, &m_data);
+}
+
+MFilePath::MFileIterator::~MFileIterator()
+{
+ if (m_hFind != INVALID_HANDLE_VALUE)
+ ::FindClose(m_hFind);
+}
+
+MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin()
+{
+ if (m_hFind == INVALID_HANDLE_VALUE)
+ return MFilePath::MFileIterator::iterator(nullptr);
+
+ return MFilePath::MFileIterator::iterator(this);
+}
+
+bool MFilePath::MFileIterator::isDir() const
+{
+ if (m_hFind == INVALID_HANDLE_VALUE)
+ return false;
+
+ return (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool MFilePath::isExist() const
+{
+ return _waccess(c_str(), 0) == 0;
+}
+
+bool MFilePath::move(const wchar_t *pwszDest)
+{
+ return MoveFileW(c_str(), pwszDest) != 0;
+}
+
+MFilePath::MFileIterator MFilePath::search()
+{
+ return MFileIterator(c_str());
+}
diff --git a/src/mir_core/src/Windows/hyperlink.cpp b/src/mir_core/src/Windows/hyperlink.cpp index 2f70d23103..e83f8ff569 100644 --- a/src/mir_core/src/Windows/hyperlink.cpp +++ b/src/mir_core/src/Windows/hyperlink.cpp @@ -1,277 +1,277 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -struct HyperlinkWndData -{ - HFONT hEnableFont, hDisableFont; - RECT rcText; - COLORREF enableColor, disableColor, focusColor; - uint8_t flags; /* see HLKF_* */ -}; - -/* flags */ -#define HLKF_HASENABLECOLOR 0x1 /* dat->enableColor is not system default */ -#define HLKF_HASDISABLECOLOR 0x2 /* dat->disableColor is not system default */ - -/* internal messages */ -#define HLK_MEASURETEXT (WM_USER+1) -#define HLK_INVALIDATE (WM_USER+2) - -static LRESULT CALLBACK HyperlinkWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - HyperlinkWndData *dat = (HyperlinkWndData*)GetWindowLongPtr(hwnd, 0); - - HDC hdc; - RECT rc; - POINT pt; - HFONT hFont; - LOGFONT lf; - HCURSOR hCursor; - COLORREF prevColor; - - switch (msg) { - case WM_NCCREATE: - dat = (struct HyperlinkWndData*)mir_calloc(sizeof(struct HyperlinkWndData)); - if (dat == nullptr) - return FALSE; /* fail creation */ - SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat); /* always succeeds */ - /* fall thru */ - - case WM_SYSCOLORCHANGE: - if (!(dat->flags&HLKF_HASENABLECOLOR)) { - if (GetSysColorBrush(COLOR_HOTLIGHT) == nullptr) dat->enableColor = RGB(0, 0, 255); - else dat->enableColor = GetSysColor(COLOR_HOTLIGHT); - dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2); - } - if (!(dat->flags&HLKF_HASDISABLECOLOR)) - dat->disableColor = GetSysColor(COLOR_GRAYTEXT); - break; - - case WM_SETFOCUS: - case WM_KILLFOCUS: - RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE); - break; - - case WM_MOUSEACTIVATE: - SetFocus(hwnd); - return MA_ACTIVATE; - - case WM_GETDLGCODE: - if (lParam) { - MSG *pMsg = (MSG *)lParam; - if (pMsg->message == WM_KEYDOWN) { - if (pMsg->wParam == VK_TAB) - return 0; - if (pMsg->wParam == VK_ESCAPE) - return 0; - } - else if (pMsg->message == WM_CHAR) { - if (pMsg->wParam == '\t') - return 0; - if (pMsg->wParam == 27) - return 0; - } - } - return DLGC_WANTMESSAGE; - - case WM_KEYDOWN: - switch (wParam) { - case VK_SPACE: - case VK_RETURN: - SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd); - break; - } - return 0; - - case WM_LBUTTONDOWN: - POINTSTOPOINT(pt, MAKEPOINTS(lParam)); - if (!PtInRect(&dat->rcText, pt)) break; - SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd); - return 0; - - case WM_SETFONT: - if ((HFONT)wParam == nullptr) { /* use default system color */ - dat->hEnableFont = dat->hDisableFont = nullptr; - return 0; - } - if (GetObject((HFONT)wParam, sizeof(lf), &lf)) { - lf.lfUnderline = 1; - hFont = CreateFontIndirect(&lf); - if (hFont != nullptr) { - dat->hEnableFont = hFont; - dat->hDisableFont = (HFONT)wParam; - if (LOWORD(lParam)) SendMessage(hwnd, HLK_INVALIDATE, 0, 0); - SendMessage(hwnd, HLK_MEASURETEXT, 0, 0); - } - } - return 0; - - case WM_ERASEBKGND: - return TRUE; - - case WM_ENABLE: - case HLK_INVALIDATE: - if (GetWindowRect(hwnd, &rc)) { - pt.x = rc.left; - pt.y = rc.top; - - HWND hwndParent = GetParent(hwnd); - if (hwndParent == nullptr) - hwndParent = hwnd; - if (!ScreenToClient(hwndParent, &pt)) - break; - - rc.right = pt.x + (rc.right - rc.left); - rc.bottom = pt.y + (rc.bottom - rc.top); - rc.left = pt.x; - rc.top = pt.y; - InvalidateRect(hwndParent, &rc, TRUE); - } - return 0; - - case WM_GETFONT: - return (LRESULT)dat->hDisableFont; - - case WM_CREATE: - case HLK_MEASURETEXT: - wchar_t szText[256]; - if (!GetWindowText(hwnd, szText, _countof(szText))) return 0; - lParam = (LPARAM)szText; - /* fall thru */ - - case WM_SETTEXT: - hdc = GetDC(hwnd); - if (hdc == nullptr) /* text change failed */ - return 0; - else { - BOOL fMeasured = FALSE; - HFONT hPrevFont = nullptr; - if (dat->hEnableFont != nullptr) hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont); - if (dat->hEnableFont == nullptr || hPrevFont != nullptr) { /* select failed? */ - SIZE textSize; - if (GetTextExtentPoint32(hdc, (wchar_t*)lParam, (int)mir_wstrlen((wchar_t*)lParam), &textSize)) { - if (GetClientRect(hwnd, &rc)) { - dat->rcText.top = 0; - dat->rcText.bottom = dat->rcText.top + textSize.cy; - LONG style = GetWindowLongPtr(hwnd, GWL_STYLE); - if (style & SS_CENTER) dat->rcText.left = (rc.right - textSize.cx) / 2; - else if (style & SS_RIGHT) dat->rcText.left = rc.right - textSize.cx; - else dat->rcText.left = 0; - dat->rcText.right = dat->rcText.left + textSize.cx; - fMeasured = TRUE; - } - } - } - if (dat->hEnableFont != nullptr && hPrevFont != nullptr) - SelectObject(hdc, hPrevFont); - ReleaseDC(hwnd, hdc); - if (!fMeasured) /* text change failed */ - return 0; - - SendMessage(hwnd, HLK_INVALIDATE, 0, 0); - } - break; - - case WM_SETCURSOR: - if (!GetCursorPos(&pt)) return FALSE; - if (!ScreenToClient(hwnd, &pt)) return FALSE; - if (PtInRect(&dat->rcText, pt)) { - hCursor = (HCURSOR)GetClassLongPtr(hwnd, GCLP_HCURSOR); - if (hCursor == nullptr) - hCursor = LoadCursor(nullptr, IDC_HAND); /* Win2000+ */ - } - else hCursor = LoadCursor(nullptr, IDC_ARROW); - SetCursor(hCursor); - return TRUE; - - case HLK_SETENABLECOLOUR: - prevColor = dat->enableColor; - dat->enableColor = (COLORREF)wParam; - dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2); - dat->flags |= HLKF_HASENABLECOLOR; - return (LRESULT)prevColor; - - case HLK_SETDISABLECOLOUR: - prevColor = dat->disableColor; - dat->disableColor = (COLORREF)wParam; - dat->flags |= HLKF_HASDISABLECOLOR; - return (LRESULT)prevColor; - - case WM_NCPAINT: - return 0; - - case WM_PAINT: - PAINTSTRUCT ps; - hdc = BeginPaint(hwnd, &ps); - if (hdc != nullptr) { - HFONT hPrevFont; - COLORREF textColor; - if (IsWindowEnabled(hwnd)) { - hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont); - textColor = (GetFocus() == hwnd) ? dat->focusColor : dat->enableColor; - } - else { - hPrevFont = (HFONT)SelectObject(hdc, dat->hDisableFont); - textColor = dat->disableColor; - } - if (GetClientRect(hwnd, &rc) && GetWindowText(hwnd, szText, _countof(szText))) { - BOOL fSmoothing; - UINT fSmoothingType; - SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fSmoothing, 0); - SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fSmoothingType, 0); - if (fSmoothing && fSmoothingType == FE_FONTSMOOTHINGCLEARTYPE) - DrawThemeParentBackground(hwnd, hdc, &rc); - SetBkMode(hdc, TRANSPARENT); - SetTextColor(hdc, textColor); - UINT alignFlag = (GetWindowLongPtr(hwnd, GWL_STYLE) & (SS_CENTER | SS_RIGHT | SS_LEFT)); - DrawText(hdc, szText, -1, &rc, alignFlag | DT_NOPREFIX | DT_SINGLELINE | DT_TOP); - } - if (hPrevFont != nullptr) SelectObject(hdc, hPrevFont); - EndPaint(hwnd, &ps); - } - return 0; - - case WM_NCDESTROY: - if (dat->hEnableFont != nullptr) DeleteObject(dat->hEnableFont); - mir_free(dat); - break; - } - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -void InitHyperlink(void) -{ - g_hCursorNS = LoadCursor(nullptr, IDC_SIZENS); - g_hCursorWE = LoadCursor(nullptr, IDC_SIZEWE); - - WNDCLASS wcl = { 0 }; - wcl.lpfnWndProc = HyperlinkWndProc; - wcl.cbWndExtra = sizeof(struct HyperlinkWndData*); - wcl.hInstance = g_hInst; - wcl.lpszClassName = WNDCLASS_HYPERLINK; - wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS | CS_PARENTDC; - RegisterClass(&wcl); /* automatically unregistered on exit */ -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+struct HyperlinkWndData
+{
+ HFONT hEnableFont, hDisableFont;
+ RECT rcText;
+ COLORREF enableColor, disableColor, focusColor;
+ uint8_t flags; /* see HLKF_* */
+};
+
+/* flags */
+#define HLKF_HASENABLECOLOR 0x1 /* dat->enableColor is not system default */
+#define HLKF_HASDISABLECOLOR 0x2 /* dat->disableColor is not system default */
+
+/* internal messages */
+#define HLK_MEASURETEXT (WM_USER+1)
+#define HLK_INVALIDATE (WM_USER+2)
+
+static LRESULT CALLBACK HyperlinkWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HyperlinkWndData *dat = (HyperlinkWndData*)GetWindowLongPtr(hwnd, 0);
+
+ HDC hdc;
+ RECT rc;
+ POINT pt;
+ HFONT hFont;
+ LOGFONT lf;
+ HCURSOR hCursor;
+ COLORREF prevColor;
+
+ switch (msg) {
+ case WM_NCCREATE:
+ dat = (struct HyperlinkWndData*)mir_calloc(sizeof(struct HyperlinkWndData));
+ if (dat == nullptr)
+ return FALSE; /* fail creation */
+ SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat); /* always succeeds */
+ /* fall thru */
+
+ case WM_SYSCOLORCHANGE:
+ if (!(dat->flags&HLKF_HASENABLECOLOR)) {
+ if (GetSysColorBrush(COLOR_HOTLIGHT) == nullptr) dat->enableColor = RGB(0, 0, 255);
+ else dat->enableColor = GetSysColor(COLOR_HOTLIGHT);
+ dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2);
+ }
+ if (!(dat->flags&HLKF_HASDISABLECOLOR))
+ dat->disableColor = GetSysColor(COLOR_GRAYTEXT);
+ break;
+
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+ RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE);
+ break;
+
+ case WM_MOUSEACTIVATE:
+ SetFocus(hwnd);
+ return MA_ACTIVATE;
+
+ case WM_GETDLGCODE:
+ if (lParam) {
+ MSG *pMsg = (MSG *)lParam;
+ if (pMsg->message == WM_KEYDOWN) {
+ if (pMsg->wParam == VK_TAB)
+ return 0;
+ if (pMsg->wParam == VK_ESCAPE)
+ return 0;
+ }
+ else if (pMsg->message == WM_CHAR) {
+ if (pMsg->wParam == '\t')
+ return 0;
+ if (pMsg->wParam == 27)
+ return 0;
+ }
+ }
+ return DLGC_WANTMESSAGE;
+
+ case WM_KEYDOWN:
+ switch (wParam) {
+ case VK_SPACE:
+ case VK_RETURN:
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd);
+ break;
+ }
+ return 0;
+
+ case WM_LBUTTONDOWN:
+ POINTSTOPOINT(pt, MAKEPOINTS(lParam));
+ if (!PtInRect(&dat->rcText, pt)) break;
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd);
+ return 0;
+
+ case WM_SETFONT:
+ if ((HFONT)wParam == nullptr) { /* use default system color */
+ dat->hEnableFont = dat->hDisableFont = nullptr;
+ return 0;
+ }
+ if (GetObject((HFONT)wParam, sizeof(lf), &lf)) {
+ lf.lfUnderline = 1;
+ hFont = CreateFontIndirect(&lf);
+ if (hFont != nullptr) {
+ dat->hEnableFont = hFont;
+ dat->hDisableFont = (HFONT)wParam;
+ if (LOWORD(lParam)) SendMessage(hwnd, HLK_INVALIDATE, 0, 0);
+ SendMessage(hwnd, HLK_MEASURETEXT, 0, 0);
+ }
+ }
+ return 0;
+
+ case WM_ERASEBKGND:
+ return TRUE;
+
+ case WM_ENABLE:
+ case HLK_INVALIDATE:
+ if (GetWindowRect(hwnd, &rc)) {
+ pt.x = rc.left;
+ pt.y = rc.top;
+
+ HWND hwndParent = GetParent(hwnd);
+ if (hwndParent == nullptr)
+ hwndParent = hwnd;
+ if (!ScreenToClient(hwndParent, &pt))
+ break;
+
+ rc.right = pt.x + (rc.right - rc.left);
+ rc.bottom = pt.y + (rc.bottom - rc.top);
+ rc.left = pt.x;
+ rc.top = pt.y;
+ InvalidateRect(hwndParent, &rc, TRUE);
+ }
+ return 0;
+
+ case WM_GETFONT:
+ return (LRESULT)dat->hDisableFont;
+
+ case WM_CREATE:
+ case HLK_MEASURETEXT:
+ wchar_t szText[256];
+ if (!GetWindowText(hwnd, szText, _countof(szText))) return 0;
+ lParam = (LPARAM)szText;
+ /* fall thru */
+
+ case WM_SETTEXT:
+ hdc = GetDC(hwnd);
+ if (hdc == nullptr) /* text change failed */
+ return 0;
+ else {
+ BOOL fMeasured = FALSE;
+ HFONT hPrevFont = nullptr;
+ if (dat->hEnableFont != nullptr) hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont);
+ if (dat->hEnableFont == nullptr || hPrevFont != nullptr) { /* select failed? */
+ SIZE textSize;
+ if (GetTextExtentPoint32(hdc, (wchar_t*)lParam, (int)mir_wstrlen((wchar_t*)lParam), &textSize)) {
+ if (GetClientRect(hwnd, &rc)) {
+ dat->rcText.top = 0;
+ dat->rcText.bottom = dat->rcText.top + textSize.cy;
+ LONG style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ if (style & SS_CENTER) dat->rcText.left = (rc.right - textSize.cx) / 2;
+ else if (style & SS_RIGHT) dat->rcText.left = rc.right - textSize.cx;
+ else dat->rcText.left = 0;
+ dat->rcText.right = dat->rcText.left + textSize.cx;
+ fMeasured = TRUE;
+ }
+ }
+ }
+ if (dat->hEnableFont != nullptr && hPrevFont != nullptr)
+ SelectObject(hdc, hPrevFont);
+ ReleaseDC(hwnd, hdc);
+ if (!fMeasured) /* text change failed */
+ return 0;
+
+ SendMessage(hwnd, HLK_INVALIDATE, 0, 0);
+ }
+ break;
+
+ case WM_SETCURSOR:
+ if (!GetCursorPos(&pt)) return FALSE;
+ if (!ScreenToClient(hwnd, &pt)) return FALSE;
+ if (PtInRect(&dat->rcText, pt)) {
+ hCursor = (HCURSOR)GetClassLongPtr(hwnd, GCLP_HCURSOR);
+ if (hCursor == nullptr)
+ hCursor = LoadCursor(nullptr, IDC_HAND); /* Win2000+ */
+ }
+ else hCursor = LoadCursor(nullptr, IDC_ARROW);
+ SetCursor(hCursor);
+ return TRUE;
+
+ case HLK_SETENABLECOLOUR:
+ prevColor = dat->enableColor;
+ dat->enableColor = (COLORREF)wParam;
+ dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2);
+ dat->flags |= HLKF_HASENABLECOLOR;
+ return (LRESULT)prevColor;
+
+ case HLK_SETDISABLECOLOUR:
+ prevColor = dat->disableColor;
+ dat->disableColor = (COLORREF)wParam;
+ dat->flags |= HLKF_HASDISABLECOLOR;
+ return (LRESULT)prevColor;
+
+ case WM_NCPAINT:
+ return 0;
+
+ case WM_PAINT:
+ PAINTSTRUCT ps;
+ hdc = BeginPaint(hwnd, &ps);
+ if (hdc != nullptr) {
+ HFONT hPrevFont;
+ COLORREF textColor;
+ if (IsWindowEnabled(hwnd)) {
+ hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont);
+ textColor = (GetFocus() == hwnd) ? dat->focusColor : dat->enableColor;
+ }
+ else {
+ hPrevFont = (HFONT)SelectObject(hdc, dat->hDisableFont);
+ textColor = dat->disableColor;
+ }
+ if (GetClientRect(hwnd, &rc) && GetWindowText(hwnd, szText, _countof(szText))) {
+ BOOL fSmoothing;
+ UINT fSmoothingType;
+ SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fSmoothing, 0);
+ SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fSmoothingType, 0);
+ if (fSmoothing && fSmoothingType == FE_FONTSMOOTHINGCLEARTYPE)
+ DrawThemeParentBackground(hwnd, hdc, &rc);
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextColor(hdc, textColor);
+ UINT alignFlag = (GetWindowLongPtr(hwnd, GWL_STYLE) & (SS_CENTER | SS_RIGHT | SS_LEFT));
+ DrawText(hdc, szText, -1, &rc, alignFlag | DT_NOPREFIX | DT_SINGLELINE | DT_TOP);
+ }
+ if (hPrevFont != nullptr) SelectObject(hdc, hPrevFont);
+ EndPaint(hwnd, &ps);
+ }
+ return 0;
+
+ case WM_NCDESTROY:
+ if (dat->hEnableFont != nullptr) DeleteObject(dat->hEnableFont);
+ mir_free(dat);
+ break;
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+void InitHyperlink(void)
+{
+ g_hCursorNS = LoadCursor(nullptr, IDC_SIZENS);
+ g_hCursorWE = LoadCursor(nullptr, IDC_SIZEWE);
+
+ WNDCLASS wcl = { 0 };
+ wcl.lpfnWndProc = HyperlinkWndProc;
+ wcl.cbWndExtra = sizeof(struct HyperlinkWndData*);
+ wcl.hInstance = g_hInst;
+ wcl.lpszClassName = WNDCLASS_HYPERLINK;
+ wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS | CS_PARENTDC;
+ RegisterClass(&wcl); /* automatically unregistered on exit */
+}
diff --git a/src/mir_core/src/Windows/icons.cpp b/src/mir_core/src/Windows/icons.cpp index a4555211de..122ecad5e5 100644 --- a/src/mir_core/src/Windows/icons.cpp +++ b/src/mir_core/src/Windows/icons.cpp @@ -1,74 +1,74 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -MIR_CORE_DLL(void) Icon_Register(HINSTANCE hInst, const char *szSection, IconItem *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin) -{ - wchar_t szFile[MAX_PATH]; - GetModuleFileName(hInst, szFile, MAX_PATH); - - SKINICONDESC sid = {}; - sid.defaultFile.w = szFile; - sid.section.a = (char*)szSection; - sid.flags = SIDF_PATH_UNICODE; - - for (unsigned i = 0; i < iCount; i++) { - char szSetting[100]; - if (prefix) { - mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName); - sid.pszName = szSetting; - } - else sid.pszName = pIcons[i].szName; - - sid.cx = sid.cy = pIcons[i].size; - sid.description.a = pIcons[i].szDescr; - sid.iDefaultIndex = -pIcons[i].defIconID; - pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin); - } -} - -MIR_CORE_DLL(void) Icon_RegisterT(HINSTANCE hInst, const wchar_t *szSection, IconItemT *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin) -{ - wchar_t szFile[MAX_PATH]; - GetModuleFileName(hInst, szFile, MAX_PATH); - - SKINICONDESC sid = {}; - sid.defaultFile.w = szFile; - sid.section.w = (wchar_t*)szSection; - sid.flags = SIDF_ALL_UNICODE; - - for (unsigned i = 0; i < iCount; i++) { - char szSetting[100]; - if (prefix) { - mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName); - sid.pszName = szSetting; - } - else sid.pszName = pIcons[i].szName; - - sid.cx = sid.cy = pIcons[i].size; - sid.description.w = pIcons[i].tszDescr; - sid.iDefaultIndex = -pIcons[i].defIconID; - pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin); - } -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+MIR_CORE_DLL(void) Icon_Register(HINSTANCE hInst, const char *szSection, IconItem *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin)
+{
+ wchar_t szFile[MAX_PATH];
+ GetModuleFileName(hInst, szFile, MAX_PATH);
+
+ SKINICONDESC sid = {};
+ sid.defaultFile.w = szFile;
+ sid.section.a = (char*)szSection;
+ sid.flags = SIDF_PATH_UNICODE;
+
+ for (unsigned i = 0; i < iCount; i++) {
+ char szSetting[100];
+ if (prefix) {
+ mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName);
+ sid.pszName = szSetting;
+ }
+ else sid.pszName = pIcons[i].szName;
+
+ sid.cx = sid.cy = pIcons[i].size;
+ sid.description.a = pIcons[i].szDescr;
+ sid.iDefaultIndex = -pIcons[i].defIconID;
+ pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin);
+ }
+}
+
+MIR_CORE_DLL(void) Icon_RegisterT(HINSTANCE hInst, const wchar_t *szSection, IconItemT *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin)
+{
+ wchar_t szFile[MAX_PATH];
+ GetModuleFileName(hInst, szFile, MAX_PATH);
+
+ SKINICONDESC sid = {};
+ sid.defaultFile.w = szFile;
+ sid.section.w = (wchar_t*)szSection;
+ sid.flags = SIDF_ALL_UNICODE;
+
+ for (unsigned i = 0; i < iCount; i++) {
+ char szSetting[100];
+ if (prefix) {
+ mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName);
+ sid.pszName = szSetting;
+ }
+ else sid.pszName = pIcons[i].szName;
+
+ sid.cx = sid.cy = pIcons[i].size;
+ sid.description.w = pIcons[i].tszDescr;
+ sid.iDefaultIndex = -pIcons[i].defIconID;
+ pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin);
+ }
+}
diff --git a/src/mir_core/src/Windows/langpack.cpp b/src/mir_core/src/Windows/langpack.cpp index 00b7d996e1..010b1311ca 100644 --- a/src/mir_core/src/Windows/langpack.cpp +++ b/src/mir_core/src/Windows/langpack.cpp @@ -1,765 +1,765 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -#include "../../../mir_app/src/langpack.h" - -#define LANGPACK_BUF_SIZE 4000 - -static int CompareMuuids(const MUUID *p1, const MUUID *p2) -{ - return memcmp(p1, p2, sizeof(MUUID)); -} - -static LIST<MUUID> lMuuids(10, CompareMuuids); -static MUUID *pCurrentMuuid = nullptr; -static HANDLE hevChanged = nullptr; - -static BOOL bModuleInitialized = FALSE; - -struct LangPackEntry -{ - uint32_t englishHash; - char *szLocal; - char *utfLocal; - wchar_t *wszLocal; - MUUID *pMuuid; - LangPackEntry* pNext; // for langpack items with the same hash value -}; - -static LANGPACK_INFO langPack; -static wchar_t g_tszRoot[MAX_PATH]; - -static LangPackEntry *g_pEntries; -static int g_entryCount, g_entriesAlloced; - -static int IsEmpty(const char *str) -{ - for (int i = 0; str[i]; i++) - if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') - return 0; - - return 1; -} - -static int ConvertBackslashes(char *str, UINT fileCp) -{ - int shift = 0; - char *pstr; - for (pstr = str; *pstr; pstr = CharNextExA(fileCp, pstr, 0)) { - if (*pstr == '\\') { - shift++; - switch (pstr[1]) { - case 'n': *pstr = '\n'; break; - case 't': *pstr = '\t'; break; - case 'r': *pstr = '\r'; break; - case 's': *pstr = ' '; break; - default: *pstr = pstr[1]; break; - } - memmove(pstr + 1, pstr + 2, strlen(pstr + 2) + 1); - } - } - return shift; -} - -#ifdef _DEBUG -//#pragma optimize("gt", on) -#endif - -// MurmurHash2 -MIR_CORE_DLL(unsigned int) mir_hash(const void * key, unsigned int len) -{ - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. - const unsigned int m = 0x5bd1e995; - const int r = 24; - - // Initialize the hash to a 'random' value - unsigned int h = len; - - // Mix 4 bytes at a time into the hash - const unsigned char *data = (const unsigned char*)key; - - while (len >= 4) { - unsigned int k = *(unsigned int*)data; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - // Handle the last few bytes of the input array - switch (len) { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; - h *= m; - } - - // Do a few final mixes of the hash to ensure the last few - // bytes are well-incorporated. - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} - -static unsigned int __fastcall hashstrW(const char *key) -{ - if (key == nullptr) return 0; - const unsigned int len = (unsigned int)wcslen((const wchar_t*)key); - char *buf = (char*)alloca(len + 1); - for (unsigned i = 0; i <= len; ++i) - buf[i] = key[i << 1]; - return mir_hash(buf, len); -} - -static const MUUID* GetMuid(HPLUGIN pPlugin) -{ - if (!pPlugin) - return nullptr; - - __try { - return &pPlugin->getInfo().uuid; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return nullptr; - } -} - -static int SortLangPackHashesProc(LangPackEntry *arg1, LangPackEntry *arg2) -{ - if (arg1->englishHash < arg2->englishHash) return -1; - if (arg1->englishHash > arg2->englishHash) return 1; - - return (arg1->pMuuid < arg2->pMuuid) ? -1 : 1; -} - -static void swapBytes(void *p, size_t iSize) -{ - char *head = (char*)p; // here - char *tail = head + iSize - 1; - - for (; tail > head; --tail, ++head) { - char temp = *head; - *head = *tail; - *tail = temp; - } -} - -static bool EnterMuuid(const char *p, MUUID &result) -{ - if (*p++ != '{') - return false; - - uint8_t *d = (uint8_t*)&result; - - for (int nBytes = 0; *p && nBytes < 24; p++) { - if (*p == '-') - continue; - - if (*p == '}') - break; - - if (!isxdigit(*p)) - return false; - - if (!isxdigit(p[1])) - return false; - - int c = 0; - if (sscanf(p, "%2x", &c) != 1) - return false; - - *d++ = (uint8_t)c; - nBytes++; - p++; - } - - if (*p != '}') - return false; - - swapBytes(&result.a, sizeof(result.a)); - swapBytes(&result.b, sizeof(result.b)); - swapBytes(&result.c, sizeof(result.c)); - return true; -} - -static void LoadLangPackFile(FILE *fp, char *line) -{ - while (!feof(fp)) { - if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr) - break; - - if (IsEmpty(line) || line[0] == ';' || line[0] == 0) - continue; - - rtrim(line); - - if (line[0] == '#') { - strlwr(line); - - if (!memcmp(line + 1, "include", 7)) { - wchar_t tszFileName[MAX_PATH]; - wchar_t *p = wcsrchr(langPack.tszFullPath, '\\'); - if (p) - *p = 0; - mir_snwprintf(tszFileName, L"%s\\%S", langPack.tszFullPath, ltrim(line + 9)); - if (p) - *p = '\\'; - - FILE *fpNew = _wfopen(tszFileName, L"r"); - if (fpNew) { - line[0] = 0; - fgets(line, LANGPACK_BUF_SIZE, fpNew); - - if (strlen(line) >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf') - fseek(fpNew, 3, SEEK_SET); - else - fseek(fpNew, 0, SEEK_SET); - - LoadLangPackFile(fpNew, line); - fclose(fpNew); - } - } - else if (!memcmp(line + 1, "muuid", 5)) { - MUUID t; - if (!EnterMuuid(line + 7, t)) - continue; - - MUUID *pNew = (MUUID*)mir_alloc(sizeof(MUUID)); - memcpy(pNew, &t, sizeof(t)); - lMuuids.insert(pNew); - pCurrentMuuid = pNew; - } - - continue; - } - - char cFirst = line[0]; - - ConvertBackslashes(line, CP_UTF8); - - size_t cbLen = strlen(line) - 1; - if (cFirst == '[' && line[cbLen] == ']') { - if (g_entryCount && g_pEntries[g_entryCount-1].wszLocal == nullptr) - g_entryCount--; - - char *pszLine = line + 1; - line[cbLen] = '\0'; - if (++g_entryCount > g_entriesAlloced) { - g_entriesAlloced += 128; - g_pEntries = (LangPackEntry*)mir_realloc(g_pEntries, sizeof(LangPackEntry)*g_entriesAlloced); - } - - LangPackEntry *E = &g_pEntries[g_entryCount - 1]; - E->englishHash = mir_hashstr(pszLine); - E->szLocal = E->utfLocal = nullptr; - E->wszLocal = nullptr; - E->pMuuid = pCurrentMuuid; - E->pNext = nullptr; - continue; - } - - if (!g_entryCount) - continue; - - LangPackEntry *E = &g_pEntries[g_entryCount - 1]; - int iNeeded = MultiByteToWideChar(CP_UTF8, 0, line, -1, nullptr, 0), iOldLen; - if (E->wszLocal == nullptr) { - iOldLen = 0; - E->wszLocal = (wchar_t *)mir_alloc((iNeeded + 1) * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal, iNeeded); - } - else { - iOldLen = (int)wcslen(E->wszLocal); - E->wszLocal = (wchar_t*)mir_realloc(E->wszLocal, (sizeof(wchar_t)* (iOldLen + iNeeded + 2))); - E->wszLocal[iOldLen++] = '\n'; - } - MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal + iOldLen, iNeeded); - } -} - -static int LoadLangDescr(LANGPACK_INFO &lpinfo, FILE *fp, char *line, int &startOfLine) -{ - char szLanguage[64]; szLanguage[0] = 0; - CMStringA szAuthors; - - lpinfo.codepage = CP_ACP; - lpinfo.flags = 0; - lpinfo.tszLanguage[0] = 0; - - fgets(line, LANGPACK_BUF_SIZE, fp); - size_t lineLen = strlen(line); - if (lineLen >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf') - memmove(line, line + 3, lineLen - 2); - - lrtrim(line); - if (mir_strcmp(line, "Miranda Language Pack Version 1")) - return 2; - - // headers - while (!feof(fp)) { - startOfLine = ftell(fp); - if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr) - break; - - lrtrim(line); - if (IsEmpty(line) || line[0] == ';' || line[0] == 0) - continue; - - if (line[0] == '[' || line[0] == '#') - break; - - char *pszColon = strchr(line, ':'); - if (pszColon == nullptr) - return 3; - - *pszColon++ = 0; - if (!mir_strcmp(line, "Language")) { - strncpy_s(szLanguage, pszColon, _TRUNCATE); - lrtrim(szLanguage); - } - else if (!mir_strcmp(line, "Last-Modified-Using")) { - lpinfo.szLastModifiedUsing = pszColon; - lpinfo.szLastModifiedUsing.Trim(); - } - else if (!mir_strcmp(line, "Authors")) { - if (!szAuthors.IsEmpty()) - szAuthors.AppendChar(' '); - szAuthors.Append(lrtrim(pszColon)); - } - else if (!mir_strcmp(line, "Locale")) { - char szBuf[20], *stopped; - - lrtrim(pszColon + 1); - USHORT langID = (USHORT)strtol(pszColon, &stopped, 16); - lpinfo.Locale = MAKELCID(langID, 0); - GetLocaleInfoA(lpinfo.Locale, LOCALE_IDEFAULTANSICODEPAGE, szBuf, 10); - szBuf[5] = 0; // codepages have max. 5 digits - lpinfo.codepage = atoi(szBuf); - } - } - - lpinfo.szAuthors = szAuthors; - - ptrW buf(mir_utf8decodeW(szLanguage)); - if (buf) - wcsncpy_s(lpinfo.tszLanguage, buf, _TRUNCATE); - else if (lpinfo.Locale != 0) - GetLocaleInfo(lpinfo.Locale, LOCALE_SENGLANGUAGE, lpinfo.tszLanguage, _countof(lpinfo.tszLanguage)); - - if (!lpinfo.tszLanguage[0]) { - wchar_t *p = wcschr(lpinfo.tszFileName, '_'); - wcsncpy_s(lpinfo.tszLanguage, ((p != nullptr) ? (p + 1) : lpinfo.tszFileName), _TRUNCATE); - p = wcsrchr(lpinfo.tszLanguage, '.'); - if (p != nullptr) *p = '\0'; - } - return 0; -} - -MIR_CORE_DLL(int) LoadLangPack(const wchar_t *ptszLangPack) -{ - if (ptszLangPack == nullptr || !mir_wstrcmpi(ptszLangPack, L"")) - return 1; - - // ensure that a lang's name is a full file name - wchar_t tszFullPath[MAX_PATH]; - if (!PathIsAbsoluteW(ptszLangPack)) - mir_snwprintf(tszFullPath, L"%s\\%s", g_tszRoot, ptszLangPack); - else - wcsncpy_s(tszFullPath, ptszLangPack, _TRUNCATE); - - // this lang is already loaded? nothing to do then - if (!mir_wstrcmp(tszFullPath, langPack.tszFullPath)) - return 0; - - // ok... loading a new langpack. remove the old one if needed - if (g_entryCount) - UnloadLangPackModule(); - - langPack.Locale = 0; - langPack.codepage = CP_ACP; - langPack.flags = 0; - - // exists & not a directory? - uint32_t dwAttrib = GetFileAttributes(tszFullPath); - if (dwAttrib == INVALID_FILE_ATTRIBUTES || (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) - return 3; - - // copy the full file name and extract a file name from it - wcsncpy_s(langPack.tszFullPath, tszFullPath, _TRUNCATE); - wchar_t *p = wcsrchr(langPack.tszFullPath, '\\'); - wcsncpy_s(langPack.tszFileName, (p == nullptr) ? tszFullPath : p + 1, _TRUNCATE); - CharLower(langPack.tszFileName); - - FILE *fp = _wfopen(tszFullPath, L"rt"); - if (fp == nullptr) - return 1; - - char line[LANGPACK_BUF_SIZE] = ""; - int startOfLine = 0; - if (LoadLangDescr(langPack, fp, line, startOfLine)) { - fclose(fp); - return 1; - } - - // body - fseek(fp, startOfLine, SEEK_SET); - - LoadLangPackFile(fp, line); - fclose(fp); - pCurrentMuuid = nullptr; - - qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc); - return 0; -} - -MIR_CORE_DLL(int) LoadLangPackDescr(const wchar_t *ptszLangPack, LANGPACK_INFO *lpInfo) -{ - if (lpInfo == nullptr) - return 1; - - wcsncpy_s(lpInfo->tszFullPath, ptszLangPack, _TRUNCATE); - wchar_t *p = wcsrchr(lpInfo->tszFullPath, '\\'); - wcsncpy_s(lpInfo->tszFileName, (p == nullptr) ? ptszLangPack : p+1, _TRUNCATE); - CharLower(lpInfo->tszFileName); - - FILE *fp = _wfopen(ptszLangPack, L"rt"); - if (fp == nullptr) - return 1; - - char line[LANGPACK_BUF_SIZE] = ""; - int startOfLine = 0; - int res = LoadLangDescr(*lpInfo, fp, line, startOfLine); - fclose(fp); - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int SortLangPackHashesProc2(LangPackEntry *arg1, LangPackEntry *arg2) -{ - if (arg1->englishHash < arg2->englishHash) return -1; - if (arg1->englishHash > arg2->englishHash) return 1; - return 0; -} - -char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W) -{ - if (g_entryCount == 0 || szEnglish == nullptr) - return (char*)szEnglish; - - LangPackEntry key, *entry; - key.englishHash = (W == 1) ? hashstrW(szEnglish) : mir_hashstr(szEnglish); - entry = (LangPackEntry*)bsearch(&key, g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc2); - if (entry == nullptr) - return (char*)szEnglish; - - // try to find the exact match, otherwise the first entry will be returned - if (pUuid) { - for (LangPackEntry *p = entry->pNext; p != nullptr; p = p->pNext) { - if (p->pMuuid && *p->pMuuid == *pUuid) { - entry = p; - break; - } - } - } - - switch (W) { - case 0: - if (entry->szLocal == nullptr && entry->wszLocal != nullptr) - entry->szLocal = mir_u2a_cp(entry->wszLocal, langPack.codepage); - return entry->szLocal; - - case 1: - return (char*)entry->wszLocal; - - case 2: - if (entry->utfLocal == nullptr && entry->wszLocal != nullptr) - entry->utfLocal = mir_utf8encodeW(entry->wszLocal); - return entry->utfLocal; - } - - return nullptr; -} - -MIR_CORE_DLL(int) Langpack_GetDefaultCodePage() -{ - return langPack.codepage; -} - -MIR_CORE_DLL(int) Langpack_GetDefaultLocale() -{ - return (langPack.Locale == 0) ? LOCALE_USER_DEFAULT : langPack.Locale; -} - -MIR_CORE_DLL(wchar_t*) Langpack_PcharToTchar(const char *pszStr) -{ - if (pszStr == nullptr) - return nullptr; - - int len = (int)strlen(pszStr); - wchar_t *result = (wchar_t*)alloca((len + 1)*sizeof(wchar_t)); - MultiByteToWideChar(Langpack_GetDefaultCodePage(), 0, pszStr, -1, result, len); - result[len] = 0; - return mir_wstrdup(TranslateW_LP(result)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(char*) TranslateA_LP(const char *str, HPLUGIN pPlugin) -{ - return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 0); -} - -MIR_CORE_DLL(char*) TranslateU_LP(const char *str, HPLUGIN pPlugin) -{ - return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 2); -} - -MIR_CORE_DLL(wchar_t*) TranslateW_LP(const wchar_t *str, HPLUGIN pPlugin) -{ - return (wchar_t*)LangPackTranslateString(GetMuid(pPlugin), (LPCSTR)str, 1); -} - -MIR_CORE_DLL(void) TranslateMenu_LP(HMENU hMenu, HPLUGIN pPlugin) -{ - const MUUID *uuid = &pPlugin->getInfo().uuid; - - MENUITEMINFO mii = { 0 }; - mii.cbSize = sizeof(mii); - - for (int i = GetMenuItemCount(hMenu) - 1; i >= 0; i--) { - wchar_t str[256]; - mii.fMask = MIIM_TYPE | MIIM_SUBMENU; - mii.dwTypeData = (wchar_t*)str; - mii.cch = _countof(str); - GetMenuItemInfo(hMenu, i, TRUE, &mii); - - if (mii.cch && mii.dwTypeData) { - wchar_t *result = (wchar_t*)LangPackTranslateString(uuid, (const char*)mii.dwTypeData, TRUE); - if (result != mii.dwTypeData) { - mii.dwTypeData = result; - mii.fMask = MIIM_TYPE; - SetMenuItemInfo(hMenu, i, TRUE, &mii); - } - } - - if (mii.hSubMenu != nullptr) - TranslateMenu_LP(mii.hSubMenu, pPlugin); - } -} - -static void TranslateWindow(const MUUID *pUuid, HWND hwnd) -{ - wchar_t title[2048]; - GetWindowText(hwnd, title, _countof(title)); - - wchar_t *result = (wchar_t*)LangPackTranslateString(pUuid, (const char*)title, TRUE); - if (result != title) - SetWindowText(hwnd, result); -} - -static BOOL CALLBACK TranslateDialogEnumProc(HWND hwnd, LPARAM lParam) -{ - HPLUGIN pPlugin = (HPLUGIN)lParam; - const MUUID *uuid = GetMuid(pPlugin); - - wchar_t szClass[32]; - GetClassName(hwnd, szClass, _countof(szClass)); - if (!mir_wstrcmpi(szClass, L"static") || !mir_wstrcmpi(szClass, L"hyperlink") || !mir_wstrcmpi(szClass, L"button") || !mir_wstrcmpi(szClass, L"MButtonClass") || !mir_wstrcmpi(szClass, L"MHeaderbarCtrl")) - TranslateWindow(uuid, hwnd); - else if (!mir_wstrcmpi(szClass, L"edit")) { - if (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY) - TranslateWindow(uuid, hwnd); - } - return TRUE; -} - -MIR_CORE_DLL(void) TranslateDialog_LP(HWND hDlg, HPLUGIN pPlugin) -{ - TranslateWindow(GetMuid(pPlugin), hDlg); - EnumChildWindows(hDlg, TranslateDialogEnumProc, (LPARAM)pPlugin); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) Langpack_SortDuplicates(void) -{ - if (g_entryCount == 0) - return; - - LangPackEntry *s = g_pEntries + 1, *d = s, *pLast = g_pEntries; - uint32_t dwSavedHash = g_pEntries->englishHash; - bool bSortNeeded = false; - - for (int i = 1; i < g_entryCount; i++, s++) { - if (s->englishHash != dwSavedHash) { - pLast = d; - if (s != d) - *d++ = *s; - else - d++; - dwSavedHash = s->englishHash; - } - else { - bSortNeeded = true; - LangPackEntry *p = (LangPackEntry*)mir_alloc(sizeof(LangPackEntry)); - *p = *s; - pLast->pNext = p; pLast = p; - } - } - - if (bSortNeeded) { - g_entryCount = (int)(d - g_pEntries); - qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void GetDefaultLang() -{ - // calculate the langpacks' root - PathToAbsoluteW(L"\\Languages", g_tszRoot); - if (_waccess(g_tszRoot, 0) != 0) // directory Languages exists - PathToAbsoluteW(L".", g_tszRoot); - - // look into mirandaboot.ini - wchar_t tszLangName[256]; - Profile_GetSetting(L"Language/DefaultLanguage", tszLangName); - if (tszLangName[0]) { - if (!mir_wstrcmpi(tszLangName, L"default")) { - db_set_ws(0, "Langpack", "Current", L"default"); - return; - } - if (!LoadLangPack(tszLangName)) { - db_set_ws(0, "Langpack", "Current", tszLangName); - return; - } - } - - // try to load langpack that matches UserDefaultUILanguage - wchar_t tszPath[MAX_PATH]; - if (GetLocaleInfo(MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT), LOCALE_SENGLANGUAGE, tszLangName, _countof(tszLangName))) { - mir_snwprintf(tszPath, L"langpack_%s.txt", wcslwr(tszLangName)); - if (!LoadLangPack(tszPath)) { - db_set_ws(0, "Langpack", "Current", tszPath); - return; - } - } - - // finally try to load first file - mir_snwprintf(tszPath, L"%s\\langpack_*.txt", g_tszRoot); - - WIN32_FIND_DATA fd; - HANDLE hFind = FindFirstFile(tszPath, &fd); - if (hFind != INVALID_HANDLE_VALUE) { - do { - /* search first langpack that could be loaded */ - if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - continue; - - if (!LoadLangPack(fd.cFileName)) { - db_set_ws(0, "Langpack", "Current", fd.cFileName); - break; - } - } while (FindNextFile(hFind, &fd)); - FindClose(hFind); - } - else db_set_ws(0, "Langpack", "Current", L"default"); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) ReloadLangpack(wchar_t *pszStr) -{ - if (pszStr == nullptr) - pszStr = NEWWSTR_ALLOCA(langPack.tszFileName); - - UnloadLangPackModule(); - LoadLangPack(pszStr); - Langpack_SortDuplicates(); - - NotifyEventHooks(hevChanged, 0, 0); -} - -static INT_PTR srvReloadLangpack(WPARAM, LPARAM lParam) -{ - ReloadLangpack((wchar_t*)lParam); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) LoadLangPackModule(void) -{ - bModuleInitialized = TRUE; - hevChanged = CreateHookableEvent(ME_LANGPACK_CHANGED); - CreateServiceFunction(MS_LANGPACK_RELOAD, srvReloadLangpack); - GetDefaultLang(); - return 0; -} - -void UnloadLangPackModule() -{ - if (!bModuleInitialized) return; - - for (auto &it : lMuuids) - mir_free(it); - lMuuids.destroy(); - - LangPackEntry *p = g_pEntries; - for (int i = 0; i < g_entryCount; i++, p++) { - if (p->pNext != nullptr) { - for (LangPackEntry *p1 = p->pNext; p1 != nullptr;) { - LangPackEntry *p2 = p1; p1 = p1->pNext; - mir_free(p2->szLocal); - mir_free(p2->wszLocal); - mir_free(p2); - } - } - - mir_free(p->szLocal); - mir_free(p->wszLocal); - } - - if (g_entryCount) { - mir_free(g_pEntries); - g_pEntries = nullptr; - g_entryCount = g_entriesAlloced = 0; - } - - langPack.tszFileName[0] = langPack.tszFullPath[0] = 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+#include "../../../mir_app/src/langpack.h"
+
+#define LANGPACK_BUF_SIZE 4000
+
+static int CompareMuuids(const MUUID *p1, const MUUID *p2)
+{
+ return memcmp(p1, p2, sizeof(MUUID));
+}
+
+static LIST<MUUID> lMuuids(10, CompareMuuids);
+static MUUID *pCurrentMuuid = nullptr;
+static HANDLE hevChanged = nullptr;
+
+static BOOL bModuleInitialized = FALSE;
+
+struct LangPackEntry
+{
+ uint32_t englishHash;
+ char *szLocal;
+ char *utfLocal;
+ wchar_t *wszLocal;
+ MUUID *pMuuid;
+ LangPackEntry* pNext; // for langpack items with the same hash value
+};
+
+static LANGPACK_INFO langPack;
+static wchar_t g_tszRoot[MAX_PATH];
+
+static LangPackEntry *g_pEntries;
+static int g_entryCount, g_entriesAlloced;
+
+static int IsEmpty(const char *str)
+{
+ for (int i = 0; str[i]; i++)
+ if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n')
+ return 0;
+
+ return 1;
+}
+
+static int ConvertBackslashes(char *str, UINT fileCp)
+{
+ int shift = 0;
+ char *pstr;
+ for (pstr = str; *pstr; pstr = CharNextExA(fileCp, pstr, 0)) {
+ if (*pstr == '\\') {
+ shift++;
+ switch (pstr[1]) {
+ case 'n': *pstr = '\n'; break;
+ case 't': *pstr = '\t'; break;
+ case 'r': *pstr = '\r'; break;
+ case 's': *pstr = ' '; break;
+ default: *pstr = pstr[1]; break;
+ }
+ memmove(pstr + 1, pstr + 2, strlen(pstr + 2) + 1);
+ }
+ }
+ return shift;
+}
+
+#ifdef _DEBUG
+//#pragma optimize("gt", on)
+#endif
+
+// MurmurHash2
+MIR_CORE_DLL(unsigned int) mir_hash(const void * key, unsigned int len)
+{
+ // 'm' and 'r' are mixing constants generated offline.
+ // They're not really 'magic', they just happen to work well.
+ const unsigned int m = 0x5bd1e995;
+ const int r = 24;
+
+ // Initialize the hash to a 'random' value
+ unsigned int h = len;
+
+ // Mix 4 bytes at a time into the hash
+ const unsigned char *data = (const unsigned char*)key;
+
+ while (len >= 4) {
+ unsigned int k = *(unsigned int*)data;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h *= m;
+ h ^= k;
+
+ data += 4;
+ len -= 4;
+ }
+
+ // Handle the last few bytes of the input array
+ switch (len) {
+ case 3: h ^= data[2] << 16;
+ case 2: h ^= data[1] << 8;
+ case 1: h ^= data[0];
+ h *= m;
+ }
+
+ // Do a few final mixes of the hash to ensure the last few
+ // bytes are well-incorporated.
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ return h;
+}
+
+static unsigned int __fastcall hashstrW(const char *key)
+{
+ if (key == nullptr) return 0;
+ const unsigned int len = (unsigned int)wcslen((const wchar_t*)key);
+ char *buf = (char*)alloca(len + 1);
+ for (unsigned i = 0; i <= len; ++i)
+ buf[i] = key[i << 1];
+ return mir_hash(buf, len);
+}
+
+static const MUUID* GetMuid(HPLUGIN pPlugin)
+{
+ if (!pPlugin)
+ return nullptr;
+
+ __try {
+ return &pPlugin->getInfo().uuid;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return nullptr;
+ }
+}
+
+static int SortLangPackHashesProc(LangPackEntry *arg1, LangPackEntry *arg2)
+{
+ if (arg1->englishHash < arg2->englishHash) return -1;
+ if (arg1->englishHash > arg2->englishHash) return 1;
+
+ return (arg1->pMuuid < arg2->pMuuid) ? -1 : 1;
+}
+
+static void swapBytes(void *p, size_t iSize)
+{
+ char *head = (char*)p; // here
+ char *tail = head + iSize - 1;
+
+ for (; tail > head; --tail, ++head) {
+ char temp = *head;
+ *head = *tail;
+ *tail = temp;
+ }
+}
+
+static bool EnterMuuid(const char *p, MUUID &result)
+{
+ if (*p++ != '{')
+ return false;
+
+ uint8_t *d = (uint8_t*)&result;
+
+ for (int nBytes = 0; *p && nBytes < 24; p++) {
+ if (*p == '-')
+ continue;
+
+ if (*p == '}')
+ break;
+
+ if (!isxdigit(*p))
+ return false;
+
+ if (!isxdigit(p[1]))
+ return false;
+
+ int c = 0;
+ if (sscanf(p, "%2x", &c) != 1)
+ return false;
+
+ *d++ = (uint8_t)c;
+ nBytes++;
+ p++;
+ }
+
+ if (*p != '}')
+ return false;
+
+ swapBytes(&result.a, sizeof(result.a));
+ swapBytes(&result.b, sizeof(result.b));
+ swapBytes(&result.c, sizeof(result.c));
+ return true;
+}
+
+static void LoadLangPackFile(FILE *fp, char *line)
+{
+ while (!feof(fp)) {
+ if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr)
+ break;
+
+ if (IsEmpty(line) || line[0] == ';' || line[0] == 0)
+ continue;
+
+ rtrim(line);
+
+ if (line[0] == '#') {
+ strlwr(line);
+
+ if (!memcmp(line + 1, "include", 7)) {
+ wchar_t tszFileName[MAX_PATH];
+ wchar_t *p = wcsrchr(langPack.tszFullPath, '\\');
+ if (p)
+ *p = 0;
+ mir_snwprintf(tszFileName, L"%s\\%S", langPack.tszFullPath, ltrim(line + 9));
+ if (p)
+ *p = '\\';
+
+ FILE *fpNew = _wfopen(tszFileName, L"r");
+ if (fpNew) {
+ line[0] = 0;
+ fgets(line, LANGPACK_BUF_SIZE, fpNew);
+
+ if (strlen(line) >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf')
+ fseek(fpNew, 3, SEEK_SET);
+ else
+ fseek(fpNew, 0, SEEK_SET);
+
+ LoadLangPackFile(fpNew, line);
+ fclose(fpNew);
+ }
+ }
+ else if (!memcmp(line + 1, "muuid", 5)) {
+ MUUID t;
+ if (!EnterMuuid(line + 7, t))
+ continue;
+
+ MUUID *pNew = (MUUID*)mir_alloc(sizeof(MUUID));
+ memcpy(pNew, &t, sizeof(t));
+ lMuuids.insert(pNew);
+ pCurrentMuuid = pNew;
+ }
+
+ continue;
+ }
+
+ char cFirst = line[0];
+
+ ConvertBackslashes(line, CP_UTF8);
+
+ size_t cbLen = strlen(line) - 1;
+ if (cFirst == '[' && line[cbLen] == ']') {
+ if (g_entryCount && g_pEntries[g_entryCount-1].wszLocal == nullptr)
+ g_entryCount--;
+
+ char *pszLine = line + 1;
+ line[cbLen] = '\0';
+ if (++g_entryCount > g_entriesAlloced) {
+ g_entriesAlloced += 128;
+ g_pEntries = (LangPackEntry*)mir_realloc(g_pEntries, sizeof(LangPackEntry)*g_entriesAlloced);
+ }
+
+ LangPackEntry *E = &g_pEntries[g_entryCount - 1];
+ E->englishHash = mir_hashstr(pszLine);
+ E->szLocal = E->utfLocal = nullptr;
+ E->wszLocal = nullptr;
+ E->pMuuid = pCurrentMuuid;
+ E->pNext = nullptr;
+ continue;
+ }
+
+ if (!g_entryCount)
+ continue;
+
+ LangPackEntry *E = &g_pEntries[g_entryCount - 1];
+ int iNeeded = MultiByteToWideChar(CP_UTF8, 0, line, -1, nullptr, 0), iOldLen;
+ if (E->wszLocal == nullptr) {
+ iOldLen = 0;
+ E->wszLocal = (wchar_t *)mir_alloc((iNeeded + 1) * sizeof(wchar_t));
+ MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal, iNeeded);
+ }
+ else {
+ iOldLen = (int)wcslen(E->wszLocal);
+ E->wszLocal = (wchar_t*)mir_realloc(E->wszLocal, (sizeof(wchar_t)* (iOldLen + iNeeded + 2)));
+ E->wszLocal[iOldLen++] = '\n';
+ }
+ MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal + iOldLen, iNeeded);
+ }
+}
+
+static int LoadLangDescr(LANGPACK_INFO &lpinfo, FILE *fp, char *line, int &startOfLine)
+{
+ char szLanguage[64]; szLanguage[0] = 0;
+ CMStringA szAuthors;
+
+ lpinfo.codepage = CP_ACP;
+ lpinfo.flags = 0;
+ lpinfo.tszLanguage[0] = 0;
+
+ fgets(line, LANGPACK_BUF_SIZE, fp);
+ size_t lineLen = strlen(line);
+ if (lineLen >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf')
+ memmove(line, line + 3, lineLen - 2);
+
+ lrtrim(line);
+ if (mir_strcmp(line, "Miranda Language Pack Version 1"))
+ return 2;
+
+ // headers
+ while (!feof(fp)) {
+ startOfLine = ftell(fp);
+ if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr)
+ break;
+
+ lrtrim(line);
+ if (IsEmpty(line) || line[0] == ';' || line[0] == 0)
+ continue;
+
+ if (line[0] == '[' || line[0] == '#')
+ break;
+
+ char *pszColon = strchr(line, ':');
+ if (pszColon == nullptr)
+ return 3;
+
+ *pszColon++ = 0;
+ if (!mir_strcmp(line, "Language")) {
+ strncpy_s(szLanguage, pszColon, _TRUNCATE);
+ lrtrim(szLanguage);
+ }
+ else if (!mir_strcmp(line, "Last-Modified-Using")) {
+ lpinfo.szLastModifiedUsing = pszColon;
+ lpinfo.szLastModifiedUsing.Trim();
+ }
+ else if (!mir_strcmp(line, "Authors")) {
+ if (!szAuthors.IsEmpty())
+ szAuthors.AppendChar(' ');
+ szAuthors.Append(lrtrim(pszColon));
+ }
+ else if (!mir_strcmp(line, "Locale")) {
+ char szBuf[20], *stopped;
+
+ lrtrim(pszColon + 1);
+ USHORT langID = (USHORT)strtol(pszColon, &stopped, 16);
+ lpinfo.Locale = MAKELCID(langID, 0);
+ GetLocaleInfoA(lpinfo.Locale, LOCALE_IDEFAULTANSICODEPAGE, szBuf, 10);
+ szBuf[5] = 0; // codepages have max. 5 digits
+ lpinfo.codepage = atoi(szBuf);
+ }
+ }
+
+ lpinfo.szAuthors = szAuthors;
+
+ ptrW buf(mir_utf8decodeW(szLanguage));
+ if (buf)
+ wcsncpy_s(lpinfo.tszLanguage, buf, _TRUNCATE);
+ else if (lpinfo.Locale != 0)
+ GetLocaleInfo(lpinfo.Locale, LOCALE_SENGLANGUAGE, lpinfo.tszLanguage, _countof(lpinfo.tszLanguage));
+
+ if (!lpinfo.tszLanguage[0]) {
+ wchar_t *p = wcschr(lpinfo.tszFileName, '_');
+ wcsncpy_s(lpinfo.tszLanguage, ((p != nullptr) ? (p + 1) : lpinfo.tszFileName), _TRUNCATE);
+ p = wcsrchr(lpinfo.tszLanguage, '.');
+ if (p != nullptr) *p = '\0';
+ }
+ return 0;
+}
+
+MIR_CORE_DLL(int) LoadLangPack(const wchar_t *ptszLangPack)
+{
+ if (ptszLangPack == nullptr || !mir_wstrcmpi(ptszLangPack, L""))
+ return 1;
+
+ // ensure that a lang's name is a full file name
+ wchar_t tszFullPath[MAX_PATH];
+ if (!PathIsAbsoluteW(ptszLangPack))
+ mir_snwprintf(tszFullPath, L"%s\\%s", g_tszRoot, ptszLangPack);
+ else
+ wcsncpy_s(tszFullPath, ptszLangPack, _TRUNCATE);
+
+ // this lang is already loaded? nothing to do then
+ if (!mir_wstrcmp(tszFullPath, langPack.tszFullPath))
+ return 0;
+
+ // ok... loading a new langpack. remove the old one if needed
+ if (g_entryCount)
+ UnloadLangPackModule();
+
+ langPack.Locale = 0;
+ langPack.codepage = CP_ACP;
+ langPack.flags = 0;
+
+ // exists & not a directory?
+ uint32_t dwAttrib = GetFileAttributes(tszFullPath);
+ if (dwAttrib == INVALID_FILE_ATTRIBUTES || (dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
+ return 3;
+
+ // copy the full file name and extract a file name from it
+ wcsncpy_s(langPack.tszFullPath, tszFullPath, _TRUNCATE);
+ wchar_t *p = wcsrchr(langPack.tszFullPath, '\\');
+ wcsncpy_s(langPack.tszFileName, (p == nullptr) ? tszFullPath : p + 1, _TRUNCATE);
+ CharLower(langPack.tszFileName);
+
+ FILE *fp = _wfopen(tszFullPath, L"rt");
+ if (fp == nullptr)
+ return 1;
+
+ char line[LANGPACK_BUF_SIZE] = "";
+ int startOfLine = 0;
+ if (LoadLangDescr(langPack, fp, line, startOfLine)) {
+ fclose(fp);
+ return 1;
+ }
+
+ // body
+ fseek(fp, startOfLine, SEEK_SET);
+
+ LoadLangPackFile(fp, line);
+ fclose(fp);
+ pCurrentMuuid = nullptr;
+
+ qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc);
+ return 0;
+}
+
+MIR_CORE_DLL(int) LoadLangPackDescr(const wchar_t *ptszLangPack, LANGPACK_INFO *lpInfo)
+{
+ if (lpInfo == nullptr)
+ return 1;
+
+ wcsncpy_s(lpInfo->tszFullPath, ptszLangPack, _TRUNCATE);
+ wchar_t *p = wcsrchr(lpInfo->tszFullPath, '\\');
+ wcsncpy_s(lpInfo->tszFileName, (p == nullptr) ? ptszLangPack : p+1, _TRUNCATE);
+ CharLower(lpInfo->tszFileName);
+
+ FILE *fp = _wfopen(ptszLangPack, L"rt");
+ if (fp == nullptr)
+ return 1;
+
+ char line[LANGPACK_BUF_SIZE] = "";
+ int startOfLine = 0;
+ int res = LoadLangDescr(*lpInfo, fp, line, startOfLine);
+ fclose(fp);
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int SortLangPackHashesProc2(LangPackEntry *arg1, LangPackEntry *arg2)
+{
+ if (arg1->englishHash < arg2->englishHash) return -1;
+ if (arg1->englishHash > arg2->englishHash) return 1;
+ return 0;
+}
+
+char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W)
+{
+ if (g_entryCount == 0 || szEnglish == nullptr)
+ return (char*)szEnglish;
+
+ LangPackEntry key, *entry;
+ key.englishHash = (W == 1) ? hashstrW(szEnglish) : mir_hashstr(szEnglish);
+ entry = (LangPackEntry*)bsearch(&key, g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc2);
+ if (entry == nullptr)
+ return (char*)szEnglish;
+
+ // try to find the exact match, otherwise the first entry will be returned
+ if (pUuid) {
+ for (LangPackEntry *p = entry->pNext; p != nullptr; p = p->pNext) {
+ if (p->pMuuid && *p->pMuuid == *pUuid) {
+ entry = p;
+ break;
+ }
+ }
+ }
+
+ switch (W) {
+ case 0:
+ if (entry->szLocal == nullptr && entry->wszLocal != nullptr)
+ entry->szLocal = mir_u2a_cp(entry->wszLocal, langPack.codepage);
+ return entry->szLocal;
+
+ case 1:
+ return (char*)entry->wszLocal;
+
+ case 2:
+ if (entry->utfLocal == nullptr && entry->wszLocal != nullptr)
+ entry->utfLocal = mir_utf8encodeW(entry->wszLocal);
+ return entry->utfLocal;
+ }
+
+ return nullptr;
+}
+
+MIR_CORE_DLL(int) Langpack_GetDefaultCodePage()
+{
+ return langPack.codepage;
+}
+
+MIR_CORE_DLL(int) Langpack_GetDefaultLocale()
+{
+ return (langPack.Locale == 0) ? LOCALE_USER_DEFAULT : langPack.Locale;
+}
+
+MIR_CORE_DLL(wchar_t*) Langpack_PcharToTchar(const char *pszStr)
+{
+ if (pszStr == nullptr)
+ return nullptr;
+
+ int len = (int)strlen(pszStr);
+ wchar_t *result = (wchar_t*)alloca((len + 1)*sizeof(wchar_t));
+ MultiByteToWideChar(Langpack_GetDefaultCodePage(), 0, pszStr, -1, result, len);
+ result[len] = 0;
+ return mir_wstrdup(TranslateW_LP(result));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(char*) TranslateA_LP(const char *str, HPLUGIN pPlugin)
+{
+ return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 0);
+}
+
+MIR_CORE_DLL(char*) TranslateU_LP(const char *str, HPLUGIN pPlugin)
+{
+ return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 2);
+}
+
+MIR_CORE_DLL(wchar_t*) TranslateW_LP(const wchar_t *str, HPLUGIN pPlugin)
+{
+ return (wchar_t*)LangPackTranslateString(GetMuid(pPlugin), (LPCSTR)str, 1);
+}
+
+MIR_CORE_DLL(void) TranslateMenu_LP(HMENU hMenu, HPLUGIN pPlugin)
+{
+ const MUUID *uuid = &pPlugin->getInfo().uuid;
+
+ MENUITEMINFO mii = { 0 };
+ mii.cbSize = sizeof(mii);
+
+ for (int i = GetMenuItemCount(hMenu) - 1; i >= 0; i--) {
+ wchar_t str[256];
+ mii.fMask = MIIM_TYPE | MIIM_SUBMENU;
+ mii.dwTypeData = (wchar_t*)str;
+ mii.cch = _countof(str);
+ GetMenuItemInfo(hMenu, i, TRUE, &mii);
+
+ if (mii.cch && mii.dwTypeData) {
+ wchar_t *result = (wchar_t*)LangPackTranslateString(uuid, (const char*)mii.dwTypeData, TRUE);
+ if (result != mii.dwTypeData) {
+ mii.dwTypeData = result;
+ mii.fMask = MIIM_TYPE;
+ SetMenuItemInfo(hMenu, i, TRUE, &mii);
+ }
+ }
+
+ if (mii.hSubMenu != nullptr)
+ TranslateMenu_LP(mii.hSubMenu, pPlugin);
+ }
+}
+
+static void TranslateWindow(const MUUID *pUuid, HWND hwnd)
+{
+ wchar_t title[2048];
+ GetWindowText(hwnd, title, _countof(title));
+
+ wchar_t *result = (wchar_t*)LangPackTranslateString(pUuid, (const char*)title, TRUE);
+ if (result != title)
+ SetWindowText(hwnd, result);
+}
+
+static BOOL CALLBACK TranslateDialogEnumProc(HWND hwnd, LPARAM lParam)
+{
+ HPLUGIN pPlugin = (HPLUGIN)lParam;
+ const MUUID *uuid = GetMuid(pPlugin);
+
+ wchar_t szClass[32];
+ GetClassName(hwnd, szClass, _countof(szClass));
+ if (!mir_wstrcmpi(szClass, L"static") || !mir_wstrcmpi(szClass, L"hyperlink") || !mir_wstrcmpi(szClass, L"button") || !mir_wstrcmpi(szClass, L"MButtonClass") || !mir_wstrcmpi(szClass, L"MHeaderbarCtrl"))
+ TranslateWindow(uuid, hwnd);
+ else if (!mir_wstrcmpi(szClass, L"edit")) {
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY)
+ TranslateWindow(uuid, hwnd);
+ }
+ return TRUE;
+}
+
+MIR_CORE_DLL(void) TranslateDialog_LP(HWND hDlg, HPLUGIN pPlugin)
+{
+ TranslateWindow(GetMuid(pPlugin), hDlg);
+ EnumChildWindows(hDlg, TranslateDialogEnumProc, (LPARAM)pPlugin);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) Langpack_SortDuplicates(void)
+{
+ if (g_entryCount == 0)
+ return;
+
+ LangPackEntry *s = g_pEntries + 1, *d = s, *pLast = g_pEntries;
+ uint32_t dwSavedHash = g_pEntries->englishHash;
+ bool bSortNeeded = false;
+
+ for (int i = 1; i < g_entryCount; i++, s++) {
+ if (s->englishHash != dwSavedHash) {
+ pLast = d;
+ if (s != d)
+ *d++ = *s;
+ else
+ d++;
+ dwSavedHash = s->englishHash;
+ }
+ else {
+ bSortNeeded = true;
+ LangPackEntry *p = (LangPackEntry*)mir_alloc(sizeof(LangPackEntry));
+ *p = *s;
+ pLast->pNext = p; pLast = p;
+ }
+ }
+
+ if (bSortNeeded) {
+ g_entryCount = (int)(d - g_pEntries);
+ qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void GetDefaultLang()
+{
+ // calculate the langpacks' root
+ PathToAbsoluteW(L"\\Languages", g_tszRoot);
+ if (_waccess(g_tszRoot, 0) != 0) // directory Languages exists
+ PathToAbsoluteW(L".", g_tszRoot);
+
+ // look into mirandaboot.ini
+ wchar_t tszLangName[256];
+ Profile_GetSetting(L"Language/DefaultLanguage", tszLangName);
+ if (tszLangName[0]) {
+ if (!mir_wstrcmpi(tszLangName, L"default")) {
+ db_set_ws(0, "Langpack", "Current", L"default");
+ return;
+ }
+ if (!LoadLangPack(tszLangName)) {
+ db_set_ws(0, "Langpack", "Current", tszLangName);
+ return;
+ }
+ }
+
+ // try to load langpack that matches UserDefaultUILanguage
+ wchar_t tszPath[MAX_PATH];
+ if (GetLocaleInfo(MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT), LOCALE_SENGLANGUAGE, tszLangName, _countof(tszLangName))) {
+ mir_snwprintf(tszPath, L"langpack_%s.txt", wcslwr(tszLangName));
+ if (!LoadLangPack(tszPath)) {
+ db_set_ws(0, "Langpack", "Current", tszPath);
+ return;
+ }
+ }
+
+ // finally try to load first file
+ mir_snwprintf(tszPath, L"%s\\langpack_*.txt", g_tszRoot);
+
+ WIN32_FIND_DATA fd;
+ HANDLE hFind = FindFirstFile(tszPath, &fd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ /* search first langpack that could be loaded */
+ if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+
+ if (!LoadLangPack(fd.cFileName)) {
+ db_set_ws(0, "Langpack", "Current", fd.cFileName);
+ break;
+ }
+ } while (FindNextFile(hFind, &fd));
+ FindClose(hFind);
+ }
+ else db_set_ws(0, "Langpack", "Current", L"default");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) ReloadLangpack(wchar_t *pszStr)
+{
+ if (pszStr == nullptr)
+ pszStr = NEWWSTR_ALLOCA(langPack.tszFileName);
+
+ UnloadLangPackModule();
+ LoadLangPack(pszStr);
+ Langpack_SortDuplicates();
+
+ NotifyEventHooks(hevChanged, 0, 0);
+}
+
+static INT_PTR srvReloadLangpack(WPARAM, LPARAM lParam)
+{
+ ReloadLangpack((wchar_t*)lParam);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) LoadLangPackModule(void)
+{
+ bModuleInitialized = TRUE;
+ hevChanged = CreateHookableEvent(ME_LANGPACK_CHANGED);
+ CreateServiceFunction(MS_LANGPACK_RELOAD, srvReloadLangpack);
+ GetDefaultLang();
+ return 0;
+}
+
+void UnloadLangPackModule()
+{
+ if (!bModuleInitialized) return;
+
+ for (auto &it : lMuuids)
+ mir_free(it);
+ lMuuids.destroy();
+
+ LangPackEntry *p = g_pEntries;
+ for (int i = 0; i < g_entryCount; i++, p++) {
+ if (p->pNext != nullptr) {
+ for (LangPackEntry *p1 = p->pNext; p1 != nullptr;) {
+ LangPackEntry *p2 = p1; p1 = p1->pNext;
+ mir_free(p2->szLocal);
+ mir_free(p2->wszLocal);
+ mir_free(p2);
+ }
+ }
+
+ mir_free(p->szLocal);
+ mir_free(p->wszLocal);
+ }
+
+ if (g_entryCount) {
+ mir_free(g_pEntries);
+ g_pEntries = nullptr;
+ g_entryCount = g_entriesAlloced = 0;
+ }
+
+ langPack.tszFileName[0] = langPack.tszFullPath[0] = 0;
+}
diff --git a/src/mir_core/src/Windows/locks.cpp b/src/mir_core/src/Windows/locks.cpp index 88f4639e80..dca0e98966 100644 --- a/src/mir_core/src/Windows/locks.cpp +++ b/src/mir_core/src/Windows/locks.cpp @@ -1,46 +1,46 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -mir_cs::mir_cs() -{ - ::InitializeCriticalSection(&m_cs); -} - -mir_cs::~mir_cs() -{ - ::DeleteCriticalSection(&m_cs); -} - -void mir_cs::Lock() -{ - while (::TryEnterCriticalSection(&m_cs) == 0) - SleepEx(50, TRUE); -} - -void mir_cs::Unlock() -{ - ::LeaveCriticalSection(&m_cs); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+mir_cs::mir_cs()
+{
+ ::InitializeCriticalSection(&m_cs);
+}
+
+mir_cs::~mir_cs()
+{
+ ::DeleteCriticalSection(&m_cs);
+}
+
+void mir_cs::Lock()
+{
+ while (::TryEnterCriticalSection(&m_cs) == 0)
+ SleepEx(50, TRUE);
+}
+
+void mir_cs::Unlock()
+{
+ ::LeaveCriticalSection(&m_cs);
+}
diff --git a/src/mir_core/src/Windows/miranda.cpp b/src/mir_core/src/Windows/miranda.cpp index d13e69ff1f..ce52b20b0b 100644 --- a/src/mir_core/src/Windows/miranda.cpp +++ b/src/mir_core/src/Windows/miranda.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_core/src/Windows/openurl.cpp b/src/mir_core/src/Windows/openurl.cpp index 3a9a3d2024..4bb8310e75 100644 --- a/src/mir_core/src/Windows/openurl.cpp +++ b/src/mir_core/src/Windows/openurl.cpp @@ -1,76 +1,76 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" -#include <ctype.h> - -struct TOpenUrlInfo -{ - TOpenUrlInfo(wchar_t *_url, int _bNew) : - szUrl(_url), - newWindow(_bNew) - {} - - ptrW szUrl; - int newWindow; -}; - -static void __cdecl OpenURLThread(TOpenUrlInfo *hUrlInfo) -{ - // wack a protocol on it - CMStringW tszUrl; - if ((isalpha(hUrlInfo->szUrl[0]) && hUrlInfo->szUrl[1] == ':') || hUrlInfo->szUrl[0] == '\\') - tszUrl.Format(L"file:///%s", hUrlInfo->szUrl.get()); - else { - int i; - for (i = 0; iswalpha(hUrlInfo->szUrl[i]); i++); - if (hUrlInfo->szUrl[i] == ':') - tszUrl = hUrlInfo->szUrl; - else if (!wcsnicmp(hUrlInfo->szUrl, L"ftp.", 4)) - tszUrl.Format(L"ftp://%s", hUrlInfo->szUrl.get()); - else - tszUrl.Format(L"http://%s", hUrlInfo->szUrl.get()); - } - - // check user defined browser for opening urls - ptrW tszBrowser(db_get_wsa(0, "Miranda", "OpenUrlBrowser")); - if (tszBrowser) - ShellExecute(nullptr, L"open", tszBrowser, tszUrl, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT); - else - ShellExecute(nullptr, L"open", tszUrl, nullptr, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT); - - delete hUrlInfo; -} - -MIR_CORE_DLL(void) Utils_OpenUrl(const char *pszUrl, bool bOpenInNewWindow) -{ - if (pszUrl) - mir_forkThread<TOpenUrlInfo>(OpenURLThread, new TOpenUrlInfo(mir_a2u(pszUrl), bOpenInNewWindow)); -} - -MIR_CORE_DLL(void) Utils_OpenUrlW(const wchar_t *pszUrl, bool bOpenInNewWindow) -{ - if (pszUrl) - mir_forkThread<TOpenUrlInfo>(OpenURLThread, new TOpenUrlInfo(mir_wstrdup(pszUrl), bOpenInNewWindow)); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+#include <ctype.h>
+
+struct TOpenUrlInfo
+{
+ TOpenUrlInfo(wchar_t *_url, int _bNew) :
+ szUrl(_url),
+ newWindow(_bNew)
+ {}
+
+ ptrW szUrl;
+ int newWindow;
+};
+
+static void __cdecl OpenURLThread(TOpenUrlInfo *hUrlInfo)
+{
+ // wack a protocol on it
+ CMStringW tszUrl;
+ if ((isalpha(hUrlInfo->szUrl[0]) && hUrlInfo->szUrl[1] == ':') || hUrlInfo->szUrl[0] == '\\')
+ tszUrl.Format(L"file:///%s", hUrlInfo->szUrl.get());
+ else {
+ int i;
+ for (i = 0; iswalpha(hUrlInfo->szUrl[i]); i++);
+ if (hUrlInfo->szUrl[i] == ':')
+ tszUrl = hUrlInfo->szUrl;
+ else if (!wcsnicmp(hUrlInfo->szUrl, L"ftp.", 4))
+ tszUrl.Format(L"ftp://%s", hUrlInfo->szUrl.get());
+ else
+ tszUrl.Format(L"http://%s", hUrlInfo->szUrl.get());
+ }
+
+ // check user defined browser for opening urls
+ ptrW tszBrowser(db_get_wsa(0, "Miranda", "OpenUrlBrowser"));
+ if (tszBrowser)
+ ShellExecute(nullptr, L"open", tszBrowser, tszUrl, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT);
+ else
+ ShellExecute(nullptr, L"open", tszUrl, nullptr, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT);
+
+ delete hUrlInfo;
+}
+
+MIR_CORE_DLL(void) Utils_OpenUrl(const char *pszUrl, bool bOpenInNewWindow)
+{
+ if (pszUrl)
+ mir_forkThread<TOpenUrlInfo>(OpenURLThread, new TOpenUrlInfo(mir_a2u(pszUrl), bOpenInNewWindow));
+}
+
+MIR_CORE_DLL(void) Utils_OpenUrlW(const wchar_t *pszUrl, bool bOpenInNewWindow)
+{
+ if (pszUrl)
+ mir_forkThread<TOpenUrlInfo>(OpenURLThread, new TOpenUrlInfo(mir_wstrdup(pszUrl), bOpenInNewWindow));
+}
diff --git a/src/mir_core/src/Windows/path.cpp b/src/mir_core/src/Windows/path.cpp index 5623ad416f..eb14c6b0de 100644 --- a/src/mir_core/src/Windows/path.cpp +++ b/src/mir_core/src/Windows/path.cpp @@ -1,246 +1,246 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -static char szMirandaPath[MAX_PATH]; -static wchar_t szMirandaPathW[MAX_PATH]; - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) PathIsAbsolute(const char *path) -{ - if (path && strlen(path) > 2) - if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\')) - return 1; - return 0; -} - -MIR_CORE_DLL(int) PathToRelative(const char *pSrc, char *pOut, const char *pBase) -{ - if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) { - *pOut = 0; - return 0; - } - - if (!PathIsAbsolute(pSrc)) - strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - else { - if (pBase == nullptr) - pBase = szMirandaPath; - - size_t cbBaseLen = strlen(pBase); - if (!strnicmp(pSrc, pBase, cbBaseLen)) - strncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE); - else - strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - } - - return (int)strlen(pOut); -} - -MIR_CORE_DLL(int) PathToAbsolute(const char *pSrc, char *pOut, const char *base) -{ - if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) { - *pOut = 0; - return 0; - } - - char buf[MAX_PATH]; - if (pSrc[0] < ' ') - strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - - if (PathIsAbsolute(pSrc)) - return GetFullPathNameA(pSrc, MAX_PATH, pOut, nullptr); - - if (base == nullptr) - base = szMirandaPath; - - if (pSrc[0] == '\\') - pSrc++; - mir_snprintf(buf, "%s%s", base, pSrc); - return GetFullPathNameA(buf, _countof(buf), pOut, nullptr); -} - -MIR_CORE_DLL(int) CreatePathToFile(const char *szFilePath) -{ - if (szFilePath == nullptr) - return ERROR_INVALID_PARAMETER; - - char *buf = NEWSTR_ALLOCA(szFilePath); - char *p = strrchr(buf, '\\'); - if (p == nullptr) - return 0; - - *p = '\0'; - return CreateDirectoryTree(buf); -} - -MIR_CORE_DLL(int) CreateDirectoryTree(const char *szDir) -{ - if (szDir == nullptr) - return 1; - - uint32_t dwAttributes = GetFileAttributesA(szDir); - if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) - return 0; - - char szTestDir[MAX_PATH]; - mir_strncpy(szTestDir, szDir, _countof(szTestDir)); - char *pszLastBackslash = strrchr(szTestDir, '\\'); - if (pszLastBackslash == nullptr) - return 0; - - *pszLastBackslash = '\0'; - CreateDirectoryTree(szTestDir); - *pszLastBackslash = '\\'; - return (CreateDirectoryA(szTestDir, nullptr) == 0) ? GetLastError() : 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) PathIsAbsoluteW(const wchar_t *path) -{ - if (path && wcslen(path) > 2) - if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\')) - return 1; - return 0; -} - -MIR_CORE_DLL(int) PathToRelativeW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *pBase) -{ - if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH) - return 0; - - if (!PathIsAbsoluteW(pSrc)) - wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - else { - if (pBase == nullptr) - pBase = szMirandaPathW; - - size_t cbBaseLen = wcslen(pBase); - if (!wcsnicmp(pSrc, pBase, cbBaseLen)) - wcsncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE); - else - wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - } - return (int)wcslen(pOut); -} - -MIR_CORE_DLL(int) PathToAbsoluteW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *base) -{ - if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH) { - *pOut = 0; - return 0; - } - - wchar_t buf[MAX_PATH]; - if (pSrc[0] < ' ') - return mir_snwprintf(pOut, MAX_PATH, L"%s", pSrc); - - if (PathIsAbsoluteW(pSrc)) - return GetFullPathName(pSrc, MAX_PATH, pOut, nullptr); - - if (base == nullptr) - base = szMirandaPathW; - - if (pSrc[0] == '\\') - pSrc++; - - mir_snwprintf(buf, MAX_PATH, L"%s%s", base, pSrc); - return GetFullPathName(buf, MAX_PATH, pOut, nullptr); -} - -MIR_CORE_DLL(int) CreatePathToFileW(const wchar_t *wszFilePath) -{ - if (wszFilePath == nullptr) - return ERROR_INVALID_PARAMETER; - - wchar_t *buf = NEWWSTR_ALLOCA(wszFilePath); - wchar_t *p = wcsrchr(buf, '\\'); - if (p == nullptr) - return 0; - - *p = '\0'; - return CreateDirectoryTreeW(buf); -} - -MIR_CORE_DLL(int) CreateDirectoryTreeW(const wchar_t *szDir) -{ - if (szDir == nullptr) - return 1; - - uint32_t dwAttributes = GetFileAttributesW(szDir); - if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) - return 0; - - wchar_t szTestDir[MAX_PATH]; - mir_wstrncpy(szTestDir, szDir, _countof(szTestDir)); - wchar_t *pszLastBackslash = wcsrchr(szTestDir, '\\'); - if (pszLastBackslash == nullptr) - return 0; - - *pszLastBackslash = '\0'; - CreateDirectoryTreeW(szTestDir); - *pszLastBackslash = '\\'; - return (CreateDirectoryW(szTestDir, nullptr) == 0) ? GetLastError() : 0; -} - -MIR_CORE_DLL(int) DeleteDirectoryTreeW(const wchar_t *pwszDir, bool bAllowUndo) -{ - if (pwszDir == nullptr) - return ERROR_BAD_ARGUMENTS; - - CMStringW wszPath(pwszDir); - wszPath.AppendChar(0); - - SHFILEOPSTRUCTW file_op = { - nullptr, - FO_DELETE, - wszPath, - L"", - FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION, - false, - nullptr, - L"" }; - - if (bAllowUndo) - file_op.fFlags |= FOF_ALLOWUNDO; - - return SHFileOperationW(&file_op); -} - -int InitPathUtils(void) -{ - GetModuleFileNameA(nullptr, szMirandaPath, _countof(szMirandaPath)); - char *p = strrchr(szMirandaPath, '\\'); - if (p) - p[1] = 0; - - GetModuleFileNameW(nullptr, szMirandaPathW, _countof(szMirandaPathW)); - wchar_t *tp = wcsrchr(szMirandaPathW, '\\'); - if (tp) - tp[1] = 0; - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+static char szMirandaPath[MAX_PATH];
+static wchar_t szMirandaPathW[MAX_PATH];
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) PathIsAbsolute(const char *path)
+{
+ if (path && strlen(path) > 2)
+ if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\'))
+ return 1;
+ return 0;
+}
+
+MIR_CORE_DLL(int) PathToRelative(const char *pSrc, char *pOut, const char *pBase)
+{
+ if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) {
+ *pOut = 0;
+ return 0;
+ }
+
+ if (!PathIsAbsolute(pSrc))
+ strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE);
+ else {
+ if (pBase == nullptr)
+ pBase = szMirandaPath;
+
+ size_t cbBaseLen = strlen(pBase);
+ if (!strnicmp(pSrc, pBase, cbBaseLen))
+ strncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE);
+ else
+ strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE);
+ }
+
+ return (int)strlen(pOut);
+}
+
+MIR_CORE_DLL(int) PathToAbsolute(const char *pSrc, char *pOut, const char *base)
+{
+ if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) {
+ *pOut = 0;
+ return 0;
+ }
+
+ char buf[MAX_PATH];
+ if (pSrc[0] < ' ')
+ strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE);
+
+ if (PathIsAbsolute(pSrc))
+ return GetFullPathNameA(pSrc, MAX_PATH, pOut, nullptr);
+
+ if (base == nullptr)
+ base = szMirandaPath;
+
+ if (pSrc[0] == '\\')
+ pSrc++;
+ mir_snprintf(buf, "%s%s", base, pSrc);
+ return GetFullPathNameA(buf, _countof(buf), pOut, nullptr);
+}
+
+MIR_CORE_DLL(int) CreatePathToFile(const char *szFilePath)
+{
+ if (szFilePath == nullptr)
+ return ERROR_INVALID_PARAMETER;
+
+ char *buf = NEWSTR_ALLOCA(szFilePath);
+ char *p = strrchr(buf, '\\');
+ if (p == nullptr)
+ return 0;
+
+ *p = '\0';
+ return CreateDirectoryTree(buf);
+}
+
+MIR_CORE_DLL(int) CreateDirectoryTree(const char *szDir)
+{
+ if (szDir == nullptr)
+ return 1;
+
+ uint32_t dwAttributes = GetFileAttributesA(szDir);
+ if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ return 0;
+
+ char szTestDir[MAX_PATH];
+ mir_strncpy(szTestDir, szDir, _countof(szTestDir));
+ char *pszLastBackslash = strrchr(szTestDir, '\\');
+ if (pszLastBackslash == nullptr)
+ return 0;
+
+ *pszLastBackslash = '\0';
+ CreateDirectoryTree(szTestDir);
+ *pszLastBackslash = '\\';
+ return (CreateDirectoryA(szTestDir, nullptr) == 0) ? GetLastError() : 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) PathIsAbsoluteW(const wchar_t *path)
+{
+ if (path && wcslen(path) > 2)
+ if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\'))
+ return 1;
+ return 0;
+}
+
+MIR_CORE_DLL(int) PathToRelativeW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *pBase)
+{
+ if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH)
+ return 0;
+
+ if (!PathIsAbsoluteW(pSrc))
+ wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE);
+ else {
+ if (pBase == nullptr)
+ pBase = szMirandaPathW;
+
+ size_t cbBaseLen = wcslen(pBase);
+ if (!wcsnicmp(pSrc, pBase, cbBaseLen))
+ wcsncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE);
+ else
+ wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE);
+ }
+ return (int)wcslen(pOut);
+}
+
+MIR_CORE_DLL(int) PathToAbsoluteW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *base)
+{
+ if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH) {
+ *pOut = 0;
+ return 0;
+ }
+
+ wchar_t buf[MAX_PATH];
+ if (pSrc[0] < ' ')
+ return mir_snwprintf(pOut, MAX_PATH, L"%s", pSrc);
+
+ if (PathIsAbsoluteW(pSrc))
+ return GetFullPathName(pSrc, MAX_PATH, pOut, nullptr);
+
+ if (base == nullptr)
+ base = szMirandaPathW;
+
+ if (pSrc[0] == '\\')
+ pSrc++;
+
+ mir_snwprintf(buf, MAX_PATH, L"%s%s", base, pSrc);
+ return GetFullPathName(buf, MAX_PATH, pOut, nullptr);
+}
+
+MIR_CORE_DLL(int) CreatePathToFileW(const wchar_t *wszFilePath)
+{
+ if (wszFilePath == nullptr)
+ return ERROR_INVALID_PARAMETER;
+
+ wchar_t *buf = NEWWSTR_ALLOCA(wszFilePath);
+ wchar_t *p = wcsrchr(buf, '\\');
+ if (p == nullptr)
+ return 0;
+
+ *p = '\0';
+ return CreateDirectoryTreeW(buf);
+}
+
+MIR_CORE_DLL(int) CreateDirectoryTreeW(const wchar_t *szDir)
+{
+ if (szDir == nullptr)
+ return 1;
+
+ uint32_t dwAttributes = GetFileAttributesW(szDir);
+ if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ return 0;
+
+ wchar_t szTestDir[MAX_PATH];
+ mir_wstrncpy(szTestDir, szDir, _countof(szTestDir));
+ wchar_t *pszLastBackslash = wcsrchr(szTestDir, '\\');
+ if (pszLastBackslash == nullptr)
+ return 0;
+
+ *pszLastBackslash = '\0';
+ CreateDirectoryTreeW(szTestDir);
+ *pszLastBackslash = '\\';
+ return (CreateDirectoryW(szTestDir, nullptr) == 0) ? GetLastError() : 0;
+}
+
+MIR_CORE_DLL(int) DeleteDirectoryTreeW(const wchar_t *pwszDir, bool bAllowUndo)
+{
+ if (pwszDir == nullptr)
+ return ERROR_BAD_ARGUMENTS;
+
+ CMStringW wszPath(pwszDir);
+ wszPath.AppendChar(0);
+
+ SHFILEOPSTRUCTW file_op = {
+ nullptr,
+ FO_DELETE,
+ wszPath,
+ L"",
+ FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION,
+ false,
+ nullptr,
+ L"" };
+
+ if (bAllowUndo)
+ file_op.fFlags |= FOF_ALLOWUNDO;
+
+ return SHFileOperationW(&file_op);
+}
+
+int InitPathUtils(void)
+{
+ GetModuleFileNameA(nullptr, szMirandaPath, _countof(szMirandaPath));
+ char *p = strrchr(szMirandaPath, '\\');
+ if (p)
+ p[1] = 0;
+
+ GetModuleFileNameW(nullptr, szMirandaPathW, _countof(szMirandaPathW));
+ wchar_t *tp = wcsrchr(szMirandaPathW, '\\');
+ if (tp)
+ tp[1] = 0;
+ return 0;
+}
diff --git a/src/mir_core/src/Windows/resizer.cpp b/src/mir_core/src/Windows/resizer.cpp index 3b5de46fcc..519659a27f 100644 --- a/src/mir_core/src/Windows/resizer.cpp +++ b/src/mir_core/src/Windows/resizer.cpp @@ -1,151 +1,151 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -#pragma pack(2) - -struct START_OF_DLGITEMTEMPLATEEX -{ - uint32_t helpID; - uint32_t exStyle; - uint32_t style; - short x, y, cx, cy; - uint32_t id; -}; - -struct START_OF_DLGTEMPLATEEX -{ - uint16_t dlgVer; - uint16_t signature; - uint32_t helpID; - uint32_t exStyle; - uint32_t style; - uint16_t cDlgItems; - short x, y, cx, cy; -}; - -MIR_CORE_DLL(int) Utils_ResizeDialog(HWND hwndDlg, HINSTANCE hInstance, LPCSTR lpTemplate, DIALOGRESIZERPROC pfnResizer, LPARAM lParam) -{ - DLGTEMPLATE *pTemplate = (DLGTEMPLATE*)LockResource(LoadResource(hInstance, FindResourceA(hInstance, lpTemplate, MAKEINTRESOURCEA(5)))); - START_OF_DLGTEMPLATEEX *pTemplateEx = (START_OF_DLGTEMPLATEEX*)pTemplate; - int extendedDlg = pTemplateEx->signature == 0xFFFF; - if (extendedDlg && pTemplateEx->dlgVer != 1) - return 1; - - PWORD pWord = (extendedDlg) ? (PWORD)(pTemplateEx + 1) : (PWORD)(pTemplate + 1); - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class - while (*pWord++); // skip title - if (extendedDlg) { - if (pTemplateEx->style & DS_SETFONT) { - pWord += 3; // font size, weight, italic - while (*pWord++); // font name - } - } - else { - if (pTemplate->style & DS_SETFONT) { - pWord++; // font size - while (*pWord++); // font name - } - } - - UTILRESIZECONTROL urc; - urc.cbSize = sizeof(UTILRESIZECONTROL); - - RECT rc; - rc.left = 0; rc.top = 0; - if (extendedDlg) { - rc.right = pTemplateEx->cx; - rc.bottom = pTemplateEx->cy; - } - else { - rc.right = pTemplate->cx; - rc.bottom = pTemplate->cy; - } - - MapDialogRect(hwndDlg, &rc); - urc.dlgOriginalSize.cx = rc.right; urc.dlgOriginalSize.cy = rc.bottom; - GetClientRect(hwndDlg, &rc); - urc.dlgNewSize.cx = rc.right; urc.dlgNewSize.cy = rc.bottom; - - int itemCount = (extendedDlg) ? pTemplateEx->cDlgItems : pTemplate->cdit; - - HDWP hDwp = BeginDeferWindowPos(itemCount); - for (int i = 0; i < itemCount; i++) { - if ((UINT_PTR)pWord & 2) pWord++; //dword align - - if (extendedDlg) { - START_OF_DLGITEMTEMPLATEEX *pItemEx = (START_OF_DLGITEMTEMPLATEEX*)pWord; - pWord = (PWORD)(pItemEx + 1); - - urc.wId = pItemEx->id; - urc.rcItem.left = pItemEx->x; urc.rcItem.top = pItemEx->y; - urc.rcItem.right = urc.rcItem.left + pItemEx->cx; urc.rcItem.bottom = urc.rcItem.top + pItemEx->cy; - } - else { - DLGITEMTEMPLATE *pItem = (DLGITEMTEMPLATE*)pWord; - pWord = (PWORD)(pItem + 1); - - urc.wId = pItem->id; - urc.rcItem.left = pItem->x; urc.rcItem.top = pItem->y; - urc.rcItem.right = urc.rcItem.left + pItem->cx; urc.rcItem.bottom = urc.rcItem.top + pItem->cy; - } - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class - pWord += 1 + (1 + *pWord) / 2; //creation data - - if (urc.wId == 65535) // using this breaks the dwp, so just ignore it - continue; - - MapDialogRect(hwndDlg, &urc.rcItem); - int procResult = (pfnResizer)(hwndDlg, lParam, &urc); - if (procResult & RD_ANCHORX_RIGHT) { - urc.rcItem.left += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; - urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; - } - else if (procResult & RD_ANCHORX_WIDTH) - urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; - else if (procResult & RD_ANCHORX_CENTRE) { - urc.rcItem.left += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2; - urc.rcItem.right += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2; - } - if (procResult & RD_ANCHORY_BOTTOM) { - urc.rcItem.top += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; - urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; - } - else if (procResult & RD_ANCHORY_HEIGHT) - urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; - else if (procResult & RD_ANCHORY_CENTRE) { - urc.rcItem.top += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2; - urc.rcItem.bottom += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2; - } - - HWND hCtrl = GetDlgItem(hwndDlg, urc.wId); - if (hCtrl != nullptr && urc.wId != UINT(-1)) - hDwp = DeferWindowPos(hDwp, hCtrl, nullptr, urc.rcItem.left, urc.rcItem.top, urc.rcItem.right - urc.rcItem.left, urc.rcItem.bottom - urc.rcItem.top, SWP_NOZORDER); - } - EndDeferWindowPos(hDwp); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+#pragma pack(2)
+
+struct START_OF_DLGITEMTEMPLATEEX
+{
+ uint32_t helpID;
+ uint32_t exStyle;
+ uint32_t style;
+ short x, y, cx, cy;
+ uint32_t id;
+};
+
+struct START_OF_DLGTEMPLATEEX
+{
+ uint16_t dlgVer;
+ uint16_t signature;
+ uint32_t helpID;
+ uint32_t exStyle;
+ uint32_t style;
+ uint16_t cDlgItems;
+ short x, y, cx, cy;
+};
+
+MIR_CORE_DLL(int) Utils_ResizeDialog(HWND hwndDlg, HINSTANCE hInstance, LPCSTR lpTemplate, DIALOGRESIZERPROC pfnResizer, LPARAM lParam)
+{
+ DLGTEMPLATE *pTemplate = (DLGTEMPLATE*)LockResource(LoadResource(hInstance, FindResourceA(hInstance, lpTemplate, MAKEINTRESOURCEA(5))));
+ START_OF_DLGTEMPLATEEX *pTemplateEx = (START_OF_DLGTEMPLATEEX*)pTemplate;
+ int extendedDlg = pTemplateEx->signature == 0xFFFF;
+ if (extendedDlg && pTemplateEx->dlgVer != 1)
+ return 1;
+
+ PWORD pWord = (extendedDlg) ? (PWORD)(pTemplateEx + 1) : (PWORD)(pTemplate + 1);
+ if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu
+ if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class
+ while (*pWord++); // skip title
+ if (extendedDlg) {
+ if (pTemplateEx->style & DS_SETFONT) {
+ pWord += 3; // font size, weight, italic
+ while (*pWord++); // font name
+ }
+ }
+ else {
+ if (pTemplate->style & DS_SETFONT) {
+ pWord++; // font size
+ while (*pWord++); // font name
+ }
+ }
+
+ UTILRESIZECONTROL urc;
+ urc.cbSize = sizeof(UTILRESIZECONTROL);
+
+ RECT rc;
+ rc.left = 0; rc.top = 0;
+ if (extendedDlg) {
+ rc.right = pTemplateEx->cx;
+ rc.bottom = pTemplateEx->cy;
+ }
+ else {
+ rc.right = pTemplate->cx;
+ rc.bottom = pTemplate->cy;
+ }
+
+ MapDialogRect(hwndDlg, &rc);
+ urc.dlgOriginalSize.cx = rc.right; urc.dlgOriginalSize.cy = rc.bottom;
+ GetClientRect(hwndDlg, &rc);
+ urc.dlgNewSize.cx = rc.right; urc.dlgNewSize.cy = rc.bottom;
+
+ int itemCount = (extendedDlg) ? pTemplateEx->cDlgItems : pTemplate->cdit;
+
+ HDWP hDwp = BeginDeferWindowPos(itemCount);
+ for (int i = 0; i < itemCount; i++) {
+ if ((UINT_PTR)pWord & 2) pWord++; //dword align
+
+ if (extendedDlg) {
+ START_OF_DLGITEMTEMPLATEEX *pItemEx = (START_OF_DLGITEMTEMPLATEEX*)pWord;
+ pWord = (PWORD)(pItemEx + 1);
+
+ urc.wId = pItemEx->id;
+ urc.rcItem.left = pItemEx->x; urc.rcItem.top = pItemEx->y;
+ urc.rcItem.right = urc.rcItem.left + pItemEx->cx; urc.rcItem.bottom = urc.rcItem.top + pItemEx->cy;
+ }
+ else {
+ DLGITEMTEMPLATE *pItem = (DLGITEMTEMPLATE*)pWord;
+ pWord = (PWORD)(pItem + 1);
+
+ urc.wId = pItem->id;
+ urc.rcItem.left = pItem->x; urc.rcItem.top = pItem->y;
+ urc.rcItem.right = urc.rcItem.left + pItem->cx; urc.rcItem.bottom = urc.rcItem.top + pItem->cy;
+ }
+ if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu
+ if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class
+ pWord += 1 + (1 + *pWord) / 2; //creation data
+
+ if (urc.wId == 65535) // using this breaks the dwp, so just ignore it
+ continue;
+
+ MapDialogRect(hwndDlg, &urc.rcItem);
+ int procResult = (pfnResizer)(hwndDlg, lParam, &urc);
+ if (procResult & RD_ANCHORX_RIGHT) {
+ urc.rcItem.left += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx;
+ urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx;
+ }
+ else if (procResult & RD_ANCHORX_WIDTH)
+ urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx;
+ else if (procResult & RD_ANCHORX_CENTRE) {
+ urc.rcItem.left += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2;
+ urc.rcItem.right += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2;
+ }
+ if (procResult & RD_ANCHORY_BOTTOM) {
+ urc.rcItem.top += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy;
+ urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy;
+ }
+ else if (procResult & RD_ANCHORY_HEIGHT)
+ urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy;
+ else if (procResult & RD_ANCHORY_CENTRE) {
+ urc.rcItem.top += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2;
+ urc.rcItem.bottom += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2;
+ }
+
+ HWND hCtrl = GetDlgItem(hwndDlg, urc.wId);
+ if (hCtrl != nullptr && urc.wId != UINT(-1))
+ hDwp = DeferWindowPos(hDwp, hCtrl, nullptr, urc.rcItem.left, urc.rcItem.top, urc.rcItem.right - urc.rcItem.left, urc.rcItem.bottom - urc.rcItem.top, SWP_NOZORDER);
+ }
+ EndDeferWindowPos(hDwp);
+ return 0;
+}
diff --git a/src/mir_core/src/Windows/subclass.cpp b/src/mir_core/src/Windows/subclass.cpp index ecc56206af..8ca02d78e3 100644 --- a/src/mir_core/src/Windows/subclass.cpp +++ b/src/mir_core/src/Windows/subclass.cpp @@ -1,201 +1,201 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "../stdafx.h" - -struct MSubclassData -{ - HWND m_hWnd; - - int m_iHooks; - WNDPROC *m_hooks; - WNDPROC m_origWndProc; - - ~MSubclassData() - { - free(m_hooks); - } -}; - -static LIST<MSubclassData> arSubclass(10, HandleKeySortT); - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK MSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hwnd); - if (p != nullptr) { - if (p->m_iHooks) - return p->m_hooks[p->m_iHooks-1](hwnd, uMsg, wParam, lParam); - - return p->m_origWndProc(hwnd, uMsg, wParam, lParam); - } - - return DefWindowProc(hwnd, uMsg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// This is for wine: it return wrong WNDPROC for edit control in some cases. -#ifdef WIN64 -#define STD_WND_PROC_ADDR_MASK 0x7FFF00000 -#else -#define STD_WND_PROC_ADDR_MASK 0xFFFF0000 -#endif - -MIR_CORE_DLL(void) mir_subclassWindow(HWND hWnd, WNDPROC wndProc) -{ - if (hWnd == nullptr) - return; - - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) { - p = new MSubclassData; - p->m_hWnd = hWnd; - p->m_origWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc); - if (((SIZE_T)p->m_origWndProc & STD_WND_PROC_ADDR_MASK) == STD_WND_PROC_ADDR_MASK) { /* XXX: fix me. Wine fix. */ - p->m_origWndProc = (WNDPROC)GetClassLongPtr(hWnd, GCLP_WNDPROC); - if (((SIZE_T)p->m_origWndProc & 0x7FFF0000) == 0x7FFF0000) /* Delay crash. */ - p->m_origWndProc = DefWindowProc; - } - p->m_iHooks = 0; - p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC)); - arSubclass.insert(p); - } - else { - for (int i=0; i < p->m_iHooks; i++) - if (p->m_hooks[i] == wndProc) - return; - - void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC)); - if (tmp == nullptr) - return; - - p->m_hooks = (WNDPROC *)tmp; - } - - p->m_hooks[p->m_iHooks++] = wndProc; -} - -MIR_CORE_DLL(void) mir_subclassWindowFull(HWND hWnd, WNDPROC wndProc, WNDPROC oldWndProc) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) { - p = new MSubclassData; - p->m_hWnd = hWnd; - p->m_origWndProc = oldWndProc; - p->m_iHooks = 0; - p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC)); - arSubclass.insert(p); - - SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc); - } - else { - for (int i=0; i < p->m_iHooks; i++) - if (p->m_hooks[i] == wndProc) - return; - - void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC)); - if (tmp == nullptr) - return; - - p->m_hooks = (WNDPROC *)tmp; - } - - p->m_hooks[p->m_iHooks++] = wndProc; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void removeHook(MSubclassData *p, int idx) -{ - // untie hook from a window to prevent calling mir_callNextSubclass from saveProc - for (int i = idx + 1; i < p->m_iHooks; i++) - p->m_hooks[i-1] = p->m_hooks[i]; - p->m_iHooks--; -} - -static WNDPROC finalizeSubclassing(HWND hWnd, MSubclassData *p) -{ - WNDPROC saveProc = p->m_origWndProc; - arSubclass.remove(p); - delete p; - - SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)saveProc); - return saveProc; -} - -MIR_CORE_DLL(void) mir_unsubclassWindow(HWND hWnd, WNDPROC wndProc) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) - return; - - for (int i = 0; i < p->m_iHooks; i++) { - if (p->m_hooks[i] == wndProc) { - removeHook(p, i); - i--; - } - } - - if (p->m_iHooks == 0) - finalizeSubclassing(hWnd, p); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(LRESULT) mir_callNextSubclass(HWND hWnd, WNDPROC wndProc, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) - return DefWindowProc(hWnd, uMsg, wParam, lParam); - - for (int i = p->m_iHooks - 1; i >= 0; i--) { - if (p->m_hooks[i] != wndProc) - continue; - - // next hook exists, call it - if (i != 0) - return p->m_hooks[i-1](hWnd, uMsg, wParam, lParam); - - // last hook called, ping the default window procedure - if (uMsg != WM_NCDESTROY) - return p->m_origWndProc(hWnd, uMsg, wParam, lParam); - - WNDPROC saveProc = finalizeSubclassing(hWnd, p); - return saveProc(hWnd, uMsg, wParam, lParam); - } - - // invalid / closed hook - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) KillModuleSubclassing(HMODULE hInst) -{ - for (auto &it : arSubclass.rev_iter()) { - for (int j = 0; j < it->m_iHooks; j++) { - if (GetInstByAddress(it->m_hooks[j]) == hInst) { - removeHook(it, j); - j--; - } - } - - if (it->m_iHooks == 0) - finalizeSubclassing(it->m_hWnd, it); - } -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "../stdafx.h"
+
+struct MSubclassData
+{
+ HWND m_hWnd;
+
+ int m_iHooks;
+ WNDPROC *m_hooks;
+ WNDPROC m_origWndProc;
+
+ ~MSubclassData()
+ {
+ free(m_hooks);
+ }
+};
+
+static LIST<MSubclassData> arSubclass(10, HandleKeySortT);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK MSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ MSubclassData *p = arSubclass.find((MSubclassData*)&hwnd);
+ if (p != nullptr) {
+ if (p->m_iHooks)
+ return p->m_hooks[p->m_iHooks-1](hwnd, uMsg, wParam, lParam);
+
+ return p->m_origWndProc(hwnd, uMsg, wParam, lParam);
+ }
+
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// This is for wine: it return wrong WNDPROC for edit control in some cases.
+#ifdef WIN64
+#define STD_WND_PROC_ADDR_MASK 0x7FFF00000
+#else
+#define STD_WND_PROC_ADDR_MASK 0xFFFF0000
+#endif
+
+MIR_CORE_DLL(void) mir_subclassWindow(HWND hWnd, WNDPROC wndProc)
+{
+ if (hWnd == nullptr)
+ return;
+
+ MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd);
+ if (p == nullptr) {
+ p = new MSubclassData;
+ p->m_hWnd = hWnd;
+ p->m_origWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc);
+ if (((SIZE_T)p->m_origWndProc & STD_WND_PROC_ADDR_MASK) == STD_WND_PROC_ADDR_MASK) { /* XXX: fix me. Wine fix. */
+ p->m_origWndProc = (WNDPROC)GetClassLongPtr(hWnd, GCLP_WNDPROC);
+ if (((SIZE_T)p->m_origWndProc & 0x7FFF0000) == 0x7FFF0000) /* Delay crash. */
+ p->m_origWndProc = DefWindowProc;
+ }
+ p->m_iHooks = 0;
+ p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC));
+ arSubclass.insert(p);
+ }
+ else {
+ for (int i=0; i < p->m_iHooks; i++)
+ if (p->m_hooks[i] == wndProc)
+ return;
+
+ void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC));
+ if (tmp == nullptr)
+ return;
+
+ p->m_hooks = (WNDPROC *)tmp;
+ }
+
+ p->m_hooks[p->m_iHooks++] = wndProc;
+}
+
+MIR_CORE_DLL(void) mir_subclassWindowFull(HWND hWnd, WNDPROC wndProc, WNDPROC oldWndProc)
+{
+ MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd);
+ if (p == nullptr) {
+ p = new MSubclassData;
+ p->m_hWnd = hWnd;
+ p->m_origWndProc = oldWndProc;
+ p->m_iHooks = 0;
+ p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC));
+ arSubclass.insert(p);
+
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc);
+ }
+ else {
+ for (int i=0; i < p->m_iHooks; i++)
+ if (p->m_hooks[i] == wndProc)
+ return;
+
+ void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC));
+ if (tmp == nullptr)
+ return;
+
+ p->m_hooks = (WNDPROC *)tmp;
+ }
+
+ p->m_hooks[p->m_iHooks++] = wndProc;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void removeHook(MSubclassData *p, int idx)
+{
+ // untie hook from a window to prevent calling mir_callNextSubclass from saveProc
+ for (int i = idx + 1; i < p->m_iHooks; i++)
+ p->m_hooks[i-1] = p->m_hooks[i];
+ p->m_iHooks--;
+}
+
+static WNDPROC finalizeSubclassing(HWND hWnd, MSubclassData *p)
+{
+ WNDPROC saveProc = p->m_origWndProc;
+ arSubclass.remove(p);
+ delete p;
+
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)saveProc);
+ return saveProc;
+}
+
+MIR_CORE_DLL(void) mir_unsubclassWindow(HWND hWnd, WNDPROC wndProc)
+{
+ MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd);
+ if (p == nullptr)
+ return;
+
+ for (int i = 0; i < p->m_iHooks; i++) {
+ if (p->m_hooks[i] == wndProc) {
+ removeHook(p, i);
+ i--;
+ }
+ }
+
+ if (p->m_iHooks == 0)
+ finalizeSubclassing(hWnd, p);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(LRESULT) mir_callNextSubclass(HWND hWnd, WNDPROC wndProc, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd);
+ if (p == nullptr)
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+
+ for (int i = p->m_iHooks - 1; i >= 0; i--) {
+ if (p->m_hooks[i] != wndProc)
+ continue;
+
+ // next hook exists, call it
+ if (i != 0)
+ return p->m_hooks[i-1](hWnd, uMsg, wParam, lParam);
+
+ // last hook called, ping the default window procedure
+ if (uMsg != WM_NCDESTROY)
+ return p->m_origWndProc(hWnd, uMsg, wParam, lParam);
+
+ WNDPROC saveProc = finalizeSubclassing(hWnd, p);
+ return saveProc(hWnd, uMsg, wParam, lParam);
+ }
+
+ // invalid / closed hook
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) KillModuleSubclassing(HMODULE hInst)
+{
+ for (auto &it : arSubclass.rev_iter()) {
+ for (int j = 0; j < it->m_iHooks; j++) {
+ if (GetInstByAddress(it->m_hooks[j]) == hInst) {
+ removeHook(it, j);
+ j--;
+ }
+ }
+
+ if (it->m_iHooks == 0)
+ finalizeSubclassing(it->m_hWnd, it);
+ }
+}
diff --git a/src/mir_core/src/Windows/threads.cpp b/src/mir_core/src/Windows/threads.cpp index 6af62d3633..c33a80faed 100644 --- a/src/mir_core/src/Windows/threads.cpp +++ b/src/mir_core/src/Windows/threads.cpp @@ -1,400 +1,400 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -#include <m_netlib.h> - -static mir_cs csThreads; - -///////////////////////////////////////////////////////////////////////////////////////// -// APC and mutex functions - -static void __stdcall DummyAPCFunc(ULONG_PTR) -{ - /* called in the context of thread that cleared it's APC queue */ - return; -} - -static int MirandaWaitForMutex(HANDLE hEvent) -{ - // will get WAIT_IO_COMPLETE for QueueUserAPC() which isnt a result - for (;;) { - uint32_t rc = MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE); - if (rc == WAIT_OBJECT_0 + 1) { - MSG msg; - while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { - if (msg.hwnd != nullptr && IsDialogMessage(msg.hwnd, &msg)) /* Wine fix. */ - continue; - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - else if (rc == WAIT_OBJECT_0) { // got object - return 1; - } - else if (rc == WAIT_ABANDONED_0 || rc == WAIT_FAILED) - return 0; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// exception handling - -static uint32_t __cdecl sttDefaultFilter(uint32_t, EXCEPTION_POINTERS*) -{ - return EXCEPTION_EXECUTE_HANDLER; -} - -pfnExceptionFilter pMirandaExceptFilter = sttDefaultFilter; - -MIR_CORE_DLL(pfnExceptionFilter) GetExceptionFilter() -{ - return pMirandaExceptFilter; -} - -MIR_CORE_DLL(pfnExceptionFilter) SetExceptionFilter(pfnExceptionFilter _mirandaExceptFilter) -{ - pfnExceptionFilter oldOne = pMirandaExceptFilter; - if (_mirandaExceptFilter != nullptr) - pMirandaExceptFilter = _mirandaExceptFilter; - return oldOne; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// thread support functions - -struct THREAD_WAIT_ENTRY -{ - uint32_t dwThreadId; // valid if hThread isn't signalled - HANDLE hThread; - HINSTANCE hOwner; - void *pObject, *pEntryPoint; -}; - -static LIST<THREAD_WAIT_ENTRY> threads(10, NumericKeySortT); - -struct FORK_ARG -{ - HANDLE hEvent, hThread; - union - { - pThreadFunc threadcode; - pThreadFuncEx threadcodeex; - }; - void *arg, *owner; -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// forkthread - starts a new thread - -DWORD WINAPI forkthread_r(void *arg) -{ - FORK_ARG *fa = (FORK_ARG*)arg; - pThreadFunc callercode = fa->threadcode; - void *cookie = fa->arg; - HANDLE hThread = fa->hThread; - Thread_Push((HINSTANCE)callercode); - SetEvent(fa->hEvent); - - callercode(cookie); - - CloseHandle(hThread); - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - Thread_Pop(); - return 0; -} - -MIR_CORE_DLL(HANDLE) mir_forkthread(void(__cdecl *threadcode)(void*), void *arg) -{ - FORK_ARG fa; - fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - fa.threadcode = threadcode; - fa.arg = arg; - - DWORD threadID; - fa.hThread = CreateThread(nullptr, 0, forkthread_r, &fa, 0, &threadID); - if (fa.hThread != nullptr) - WaitForSingleObject(fa.hEvent, INFINITE); - - CloseHandle(fa.hEvent); - return fa.hThread; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// forkthreadex - starts a new thread with the extended info and returns the thread id - -DWORD WINAPI forkthreadex_r(void *arg) -{ - struct FORK_ARG *fa = (struct FORK_ARG *)arg; - pThreadFuncEx threadcode = fa->threadcodeex; - pThreadFuncOwner threadcodeex = (pThreadFuncOwner)fa->threadcodeex; - void *cookie = fa->arg; - void *owner = fa->owner; - unsigned long rc = 0; - - Thread_Push((HINSTANCE)threadcode, fa->owner); - SetEvent(fa->hEvent); - if (owner) - rc = threadcodeex(owner, cookie); - else - rc = threadcode(cookie); - - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - Thread_Pop(); - return rc; -} - -MIR_CORE_DLL(HANDLE) mir_forkthreadex(pThreadFuncEx aFunc, void* arg, unsigned *pThreadID) -{ - struct FORK_ARG fa = {}; - fa.threadcodeex = aFunc; - fa.arg = arg; - fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - - DWORD threadID = 0; - HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID); - if (hThread != nullptr) - WaitForSingleObject(fa.hEvent, INFINITE); - - if (pThreadID != nullptr) - *pThreadID = threadID; - - CloseHandle(fa.hEvent); - return hThread; -} - -MIR_CORE_DLL(HANDLE) mir_forkthreadowner(pThreadFuncOwner aFunc, void *owner, void *arg, unsigned *pThreadID) -{ - struct FORK_ARG fa = {}; - fa.threadcodeex = (pThreadFuncEx)aFunc; - fa.arg = arg; - fa.owner = owner; - fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - - DWORD threadID = 0; - HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID); - if (hThread != nullptr) - WaitForSingleObject(fa.hEvent, INFINITE); - - if (pThreadID != nullptr) - *pThreadID = threadID; - - CloseHandle(fa.hEvent); - return hThread; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void __cdecl KillObjectThreadsWorker(void* owner) -{ - HANDLE *threadPool = (HANDLE*)alloca(threads.getCount() * sizeof(HANDLE)); - int threadCount = 0; - { - mir_cslock lck(csThreads); - - for (auto &it : threads) - if (it->pObject == owner) - threadPool[threadCount++] = it->hThread; - } - - // is there anything to kill? - if (threadCount == 0) - return; - - // wait'em all - if (WaitForMultipleObjects(threadCount, threadPool, TRUE, 5000) != WAIT_TIMEOUT) - return; - - // forcibly kill all remaining threads after 5 secs - mir_cslock lck(csThreads); - auto T = threads.rev_iter(); - for (auto &it : T) { - if (it->pObject == owner) { - char szModuleName[MAX_PATH]; - GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName)); - Netlib_Logf(nullptr, "Killing object thread %s:%08x", szModuleName, it->dwThreadId); - TerminateThread(it->hThread, 9999); - CloseHandle(it->hThread); - mir_free(it); - threads.removeItem(&it); - } - } -} - -MIR_CORE_DLL(void) KillObjectThreads(void* owner) -{ - if (owner == nullptr) - return; - - uint32_t dwTicks = GetTickCount() + 6000; - HANDLE hThread = mir_forkthread(KillObjectThreadsWorker, owner); - while (GetTickCount() < dwTicks) { - int res = MsgWaitForMultipleObjectsEx(1, &hThread, 50, QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_ALERTABLE); - if (res == WAIT_OBJECT_0 || res == WAIT_FAILED) - break; - - MSG msg; - while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void CALLBACK KillAllThreads(HWND, UINT, UINT_PTR, DWORD) -{ - mir_cslock lck(csThreads); - for (auto &p : threads) { - char szModuleName[MAX_PATH]; - GetModuleFileNameA(p->hOwner, szModuleName, sizeof(szModuleName)); - Netlib_Logf(nullptr, "Killing thread %s:%08x (%p)", szModuleName, p->dwThreadId, p->pEntryPoint); - TerminateThread(p->hThread, 9999); - CloseHandle(p->hThread); - mir_free(p); - } - - threads.destroy(); - - SetEvent(hThreadQueueEmpty); -} - -MIR_CORE_DLL(void) Thread_Wait(void) -{ - // acquire the list and wake up any alertable threads - { - mir_cslock lck(csThreads); - for (auto &p : threads) - QueueUserAPC(DummyAPCFunc, p->hThread, 0); - } - - // give all unclosed threads 5 seconds to close - SetTimer(nullptr, 0, 5000, KillAllThreads); - - // wait til the thread list is empty - MirandaWaitForMutex(hThreadQueueEmpty); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -typedef LONG (WINAPI *pNtQIT)(HANDLE, LONG, PVOID, ULONG, PULONG); -#define ThreadQuerySetWin32StartAddress 9 - -static void* GetCurrentThreadEntryPoint() -{ - pNtQIT NtQueryInformationThread = (pNtQIT)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationThread"); - if (NtQueryInformationThread == nullptr) - return nullptr; - - HANDLE hDupHandle, hCurrentProcess = GetCurrentProcess(); - if (!DuplicateHandle(hCurrentProcess, GetCurrentThread(), hCurrentProcess, &hDupHandle, THREAD_QUERY_INFORMATION, FALSE, 0)) { - SetLastError(ERROR_ACCESS_DENIED); - return nullptr; - } - - DWORD_PTR dwStartAddress; - LONG ntStatus = NtQueryInformationThread(hDupHandle, ThreadQuerySetWin32StartAddress, &dwStartAddress, sizeof(DWORD_PTR), nullptr); - CloseHandle(hDupHandle); - - return (ntStatus != ERROR_SUCCESS) ? nullptr : (void*)dwStartAddress; -} - -MIR_CORE_DLL(INT_PTR) Thread_Push(HINSTANCE hInst, void* pOwner) -{ - ResetEvent(hThreadQueueEmpty); // thread list is not empty - - mir_cslock lck(csThreads); - - THREAD_WAIT_ENTRY *p = (THREAD_WAIT_ENTRY*)mir_calloc(sizeof(THREAD_WAIT_ENTRY)); - DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &p->hThread, 0, FALSE, DUPLICATE_SAME_ACCESS); - p->dwThreadId = GetCurrentThreadId(); - p->pObject = pOwner; - p->pEntryPoint = hInst; - - // try to find the precise match - CMPluginBase &pPlugin = GetPluginByInstance(hInst); - if (pPlugin.getInst() == hInst) - p->hOwner = hInst; - else - GetInstByAddress((hInst != nullptr) ? (PVOID)hInst : GetCurrentThreadEntryPoint()); - - threads.insert(p); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(INT_PTR) Thread_Pop() -{ - uint32_t dwThreadId = GetCurrentThreadId(); - - mir_cslock lck(csThreads); - THREAD_WAIT_ENTRY *p = threads.find((THREAD_WAIT_ENTRY*)&dwThreadId); - if (p == nullptr) - return 1; - - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - CloseHandle(p->hThread); - threads.remove(p); - mir_free(p); - - if (!threads.getCount()) { - threads.destroy(); - SetEvent(hThreadQueueEmpty); // thread list is empty now - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -const uint32_t MS_VC_EXCEPTION=0x406D1388; - -#pragma pack(push,8) -typedef struct tagTHREADNAME_INFO -{ - uint32_t dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - uint32_t dwThreadID; // Thread ID (-1=caller thread). - uint32_t dwFlags; // Reserved for future use, must be zero. -} THREADNAME_INFO; -#pragma pack(pop) - -MIR_CORE_DLL(void) Thread_SetName(const char *szThreadName) -{ - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = szThreadName; - info.dwThreadID = GetCurrentThreadId(); - info.dwFlags = 0; - - __try { - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); - } - __except (EXCEPTION_EXECUTE_HANDLER) - {} -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+#include <m_netlib.h>
+
+static mir_cs csThreads;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// APC and mutex functions
+
+static void __stdcall DummyAPCFunc(ULONG_PTR)
+{
+ /* called in the context of thread that cleared it's APC queue */
+ return;
+}
+
+static int MirandaWaitForMutex(HANDLE hEvent)
+{
+ // will get WAIT_IO_COMPLETE for QueueUserAPC() which isnt a result
+ for (;;) {
+ uint32_t rc = MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
+ if (rc == WAIT_OBJECT_0 + 1) {
+ MSG msg;
+ while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
+ if (msg.hwnd != nullptr && IsDialogMessage(msg.hwnd, &msg)) /* Wine fix. */
+ continue;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+ else if (rc == WAIT_OBJECT_0) { // got object
+ return 1;
+ }
+ else if (rc == WAIT_ABANDONED_0 || rc == WAIT_FAILED)
+ return 0;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// exception handling
+
+static uint32_t __cdecl sttDefaultFilter(uint32_t, EXCEPTION_POINTERS*)
+{
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+pfnExceptionFilter pMirandaExceptFilter = sttDefaultFilter;
+
+MIR_CORE_DLL(pfnExceptionFilter) GetExceptionFilter()
+{
+ return pMirandaExceptFilter;
+}
+
+MIR_CORE_DLL(pfnExceptionFilter) SetExceptionFilter(pfnExceptionFilter _mirandaExceptFilter)
+{
+ pfnExceptionFilter oldOne = pMirandaExceptFilter;
+ if (_mirandaExceptFilter != nullptr)
+ pMirandaExceptFilter = _mirandaExceptFilter;
+ return oldOne;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// thread support functions
+
+struct THREAD_WAIT_ENTRY
+{
+ uint32_t dwThreadId; // valid if hThread isn't signalled
+ HANDLE hThread;
+ HINSTANCE hOwner;
+ void *pObject, *pEntryPoint;
+};
+
+static LIST<THREAD_WAIT_ENTRY> threads(10, NumericKeySortT);
+
+struct FORK_ARG
+{
+ HANDLE hEvent, hThread;
+ union
+ {
+ pThreadFunc threadcode;
+ pThreadFuncEx threadcodeex;
+ };
+ void *arg, *owner;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// forkthread - starts a new thread
+
+DWORD WINAPI forkthread_r(void *arg)
+{
+ FORK_ARG *fa = (FORK_ARG*)arg;
+ pThreadFunc callercode = fa->threadcode;
+ void *cookie = fa->arg;
+ HANDLE hThread = fa->hThread;
+ Thread_Push((HINSTANCE)callercode);
+ SetEvent(fa->hEvent);
+
+ callercode(cookie);
+
+ CloseHandle(hThread);
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ Thread_Pop();
+ return 0;
+}
+
+MIR_CORE_DLL(HANDLE) mir_forkthread(void(__cdecl *threadcode)(void*), void *arg)
+{
+ FORK_ARG fa;
+ fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+ fa.threadcode = threadcode;
+ fa.arg = arg;
+
+ DWORD threadID;
+ fa.hThread = CreateThread(nullptr, 0, forkthread_r, &fa, 0, &threadID);
+ if (fa.hThread != nullptr)
+ WaitForSingleObject(fa.hEvent, INFINITE);
+
+ CloseHandle(fa.hEvent);
+ return fa.hThread;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// forkthreadex - starts a new thread with the extended info and returns the thread id
+
+DWORD WINAPI forkthreadex_r(void *arg)
+{
+ struct FORK_ARG *fa = (struct FORK_ARG *)arg;
+ pThreadFuncEx threadcode = fa->threadcodeex;
+ pThreadFuncOwner threadcodeex = (pThreadFuncOwner)fa->threadcodeex;
+ void *cookie = fa->arg;
+ void *owner = fa->owner;
+ unsigned long rc = 0;
+
+ Thread_Push((HINSTANCE)threadcode, fa->owner);
+ SetEvent(fa->hEvent);
+ if (owner)
+ rc = threadcodeex(owner, cookie);
+ else
+ rc = threadcode(cookie);
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ Thread_Pop();
+ return rc;
+}
+
+MIR_CORE_DLL(HANDLE) mir_forkthreadex(pThreadFuncEx aFunc, void* arg, unsigned *pThreadID)
+{
+ struct FORK_ARG fa = {};
+ fa.threadcodeex = aFunc;
+ fa.arg = arg;
+ fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+
+ DWORD threadID = 0;
+ HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID);
+ if (hThread != nullptr)
+ WaitForSingleObject(fa.hEvent, INFINITE);
+
+ if (pThreadID != nullptr)
+ *pThreadID = threadID;
+
+ CloseHandle(fa.hEvent);
+ return hThread;
+}
+
+MIR_CORE_DLL(HANDLE) mir_forkthreadowner(pThreadFuncOwner aFunc, void *owner, void *arg, unsigned *pThreadID)
+{
+ struct FORK_ARG fa = {};
+ fa.threadcodeex = (pThreadFuncEx)aFunc;
+ fa.arg = arg;
+ fa.owner = owner;
+ fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+
+ DWORD threadID = 0;
+ HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID);
+ if (hThread != nullptr)
+ WaitForSingleObject(fa.hEvent, INFINITE);
+
+ if (pThreadID != nullptr)
+ *pThreadID = threadID;
+
+ CloseHandle(fa.hEvent);
+ return hThread;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void __cdecl KillObjectThreadsWorker(void* owner)
+{
+ HANDLE *threadPool = (HANDLE*)alloca(threads.getCount() * sizeof(HANDLE));
+ int threadCount = 0;
+ {
+ mir_cslock lck(csThreads);
+
+ for (auto &it : threads)
+ if (it->pObject == owner)
+ threadPool[threadCount++] = it->hThread;
+ }
+
+ // is there anything to kill?
+ if (threadCount == 0)
+ return;
+
+ // wait'em all
+ if (WaitForMultipleObjects(threadCount, threadPool, TRUE, 5000) != WAIT_TIMEOUT)
+ return;
+
+ // forcibly kill all remaining threads after 5 secs
+ mir_cslock lck(csThreads);
+ auto T = threads.rev_iter();
+ for (auto &it : T) {
+ if (it->pObject == owner) {
+ char szModuleName[MAX_PATH];
+ GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName));
+ Netlib_Logf(nullptr, "Killing object thread %s:%08x", szModuleName, it->dwThreadId);
+ TerminateThread(it->hThread, 9999);
+ CloseHandle(it->hThread);
+ mir_free(it);
+ threads.removeItem(&it);
+ }
+ }
+}
+
+MIR_CORE_DLL(void) KillObjectThreads(void* owner)
+{
+ if (owner == nullptr)
+ return;
+
+ uint32_t dwTicks = GetTickCount() + 6000;
+ HANDLE hThread = mir_forkthread(KillObjectThreadsWorker, owner);
+ while (GetTickCount() < dwTicks) {
+ int res = MsgWaitForMultipleObjectsEx(1, &hThread, 50, QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_ALERTABLE);
+ if (res == WAIT_OBJECT_0 || res == WAIT_FAILED)
+ break;
+
+ MSG msg;
+ while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void CALLBACK KillAllThreads(HWND, UINT, UINT_PTR, DWORD)
+{
+ mir_cslock lck(csThreads);
+ for (auto &p : threads) {
+ char szModuleName[MAX_PATH];
+ GetModuleFileNameA(p->hOwner, szModuleName, sizeof(szModuleName));
+ Netlib_Logf(nullptr, "Killing thread %s:%08x (%p)", szModuleName, p->dwThreadId, p->pEntryPoint);
+ TerminateThread(p->hThread, 9999);
+ CloseHandle(p->hThread);
+ mir_free(p);
+ }
+
+ threads.destroy();
+
+ SetEvent(hThreadQueueEmpty);
+}
+
+MIR_CORE_DLL(void) Thread_Wait(void)
+{
+ // acquire the list and wake up any alertable threads
+ {
+ mir_cslock lck(csThreads);
+ for (auto &p : threads)
+ QueueUserAPC(DummyAPCFunc, p->hThread, 0);
+ }
+
+ // give all unclosed threads 5 seconds to close
+ SetTimer(nullptr, 0, 5000, KillAllThreads);
+
+ // wait til the thread list is empty
+ MirandaWaitForMutex(hThreadQueueEmpty);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+typedef LONG (WINAPI *pNtQIT)(HANDLE, LONG, PVOID, ULONG, PULONG);
+#define ThreadQuerySetWin32StartAddress 9
+
+static void* GetCurrentThreadEntryPoint()
+{
+ pNtQIT NtQueryInformationThread = (pNtQIT)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationThread");
+ if (NtQueryInformationThread == nullptr)
+ return nullptr;
+
+ HANDLE hDupHandle, hCurrentProcess = GetCurrentProcess();
+ if (!DuplicateHandle(hCurrentProcess, GetCurrentThread(), hCurrentProcess, &hDupHandle, THREAD_QUERY_INFORMATION, FALSE, 0)) {
+ SetLastError(ERROR_ACCESS_DENIED);
+ return nullptr;
+ }
+
+ DWORD_PTR dwStartAddress;
+ LONG ntStatus = NtQueryInformationThread(hDupHandle, ThreadQuerySetWin32StartAddress, &dwStartAddress, sizeof(DWORD_PTR), nullptr);
+ CloseHandle(hDupHandle);
+
+ return (ntStatus != ERROR_SUCCESS) ? nullptr : (void*)dwStartAddress;
+}
+
+MIR_CORE_DLL(INT_PTR) Thread_Push(HINSTANCE hInst, void* pOwner)
+{
+ ResetEvent(hThreadQueueEmpty); // thread list is not empty
+
+ mir_cslock lck(csThreads);
+
+ THREAD_WAIT_ENTRY *p = (THREAD_WAIT_ENTRY*)mir_calloc(sizeof(THREAD_WAIT_ENTRY));
+ DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &p->hThread, 0, FALSE, DUPLICATE_SAME_ACCESS);
+ p->dwThreadId = GetCurrentThreadId();
+ p->pObject = pOwner;
+ p->pEntryPoint = hInst;
+
+ // try to find the precise match
+ CMPluginBase &pPlugin = GetPluginByInstance(hInst);
+ if (pPlugin.getInst() == hInst)
+ p->hOwner = hInst;
+ else
+ GetInstByAddress((hInst != nullptr) ? (PVOID)hInst : GetCurrentThreadEntryPoint());
+
+ threads.insert(p);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(INT_PTR) Thread_Pop()
+{
+ uint32_t dwThreadId = GetCurrentThreadId();
+
+ mir_cslock lck(csThreads);
+ THREAD_WAIT_ENTRY *p = threads.find((THREAD_WAIT_ENTRY*)&dwThreadId);
+ if (p == nullptr)
+ return 1;
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ CloseHandle(p->hThread);
+ threads.remove(p);
+ mir_free(p);
+
+ if (!threads.getCount()) {
+ threads.destroy();
+ SetEvent(hThreadQueueEmpty); // thread list is empty now
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+const uint32_t MS_VC_EXCEPTION=0x406D1388;
+
+#pragma pack(push,8)
+typedef struct tagTHREADNAME_INFO
+{
+ uint32_t dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ uint32_t dwThreadID; // Thread ID (-1=caller thread).
+ uint32_t dwFlags; // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+MIR_CORE_DLL(void) Thread_SetName(const char *szThreadName)
+{
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = GetCurrentThreadId();
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {}
+}
diff --git a/src/mir_core/src/Windows/timezones.cpp b/src/mir_core/src/Windows/timezones.cpp index 63d026a3eb..13e2fddba9 100644 --- a/src/mir_core/src/Windows/timezones.cpp +++ b/src/mir_core/src/Windows/timezones.cpp @@ -1,593 +1,593 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -implements services to handle location - based timezones, instead of -simple UTC offsets. -*/ - -#include "../stdafx.h" - -typedef uint32_t (WINAPI *pfnGetDynamicTimeZoneInformation_t)(DYNAMIC_TIME_ZONE_INFORMATION *pdtzi); -static pfnGetDynamicTimeZoneInformation_t pfnGetDynamicTimeZoneInformation; - -struct REG_TZI_FORMAT -{ - LONG Bias; - LONG StandardBias; - LONG DaylightBias; - SYSTEMTIME StandardDate; - SYSTEMTIME DaylightDate; -}; - -#define MIM_TZ_DISPLAYLEN 128 - -struct MIM_TIMEZONE -{ - unsigned hash; - int offset; - - wchar_t tszName[MIM_TZ_NAMELEN]; // windows name for the time zone - wchar_t szDisplay[MIM_TZ_DISPLAYLEN]; // more descriptive display name (that's what usually appears in dialogs) - // every hour should be sufficient. - TIME_ZONE_INFORMATION tzi; - - static int compareBias(const MIM_TIMEZONE* p1, const MIM_TIMEZONE* p2) - { return p2->tzi.Bias - p1->tzi.Bias; - } -}; - -struct TZ_INT_INFO -{ - uint32_t timestamp; // last time updated - MIM_TIMEZONE myTZ; // set to my own timezone -}; - -static TZ_INT_INFO myInfo; - -static OBJLIST<MIM_TIMEZONE> g_timezones(55, NumericKeySortT); -static LIST<MIM_TIMEZONE> g_timezonesBias(55, MIM_TIMEZONE::compareBias); - -// KB167296 -void UnixTimeToFileTime(mir_time ts, LPFILETIME pft) -{ - unsigned __int64 ll = UInt32x32To64(ts, 10000000) + 116444736000000000i64; - pft->dwLowDateTime = (uint32_t)ll; - pft->dwHighDateTime = ll >> 32; -} - -mir_time FileTimeToUnixTime(LPFILETIME pft) -{ - unsigned __int64 ll = (unsigned __int64)pft->dwHighDateTime << 32 | pft->dwLowDateTime; - ll -= 116444736000000000i64; - return (mir_time)(ll / 10000000); -} - -void FormatTime(const SYSTEMTIME *st, const wchar_t *szFormat, wchar_t *szDest, size_t cbDest) -{ - if (szDest == nullptr || cbDest == 0) return; - - CMStringW tszTemp; - - for (const wchar_t* pFormat = szFormat; *pFormat; ++pFormat) { - uint32_t fmt = 0; - bool date = false, iso = false; - switch (*pFormat) { - case 't': - fmt = TIME_NOSECONDS; - date = false; - break; - - case 's': - fmt = 0; - date = false; - break; - - case 'm': - fmt = TIME_NOMINUTESORSECONDS; - date = false; - break; - - case 'd': - fmt = DATE_SHORTDATE; - date = true; - break; - - case 'D': - fmt = DATE_LONGDATE; - date = true; - break; - - case 'I': - iso = true; - break; - - default: - tszTemp.AppendChar(*pFormat); - continue; - } - - wchar_t dateTimeStr[64]; - if (iso) - tszTemp.AppendFormat(L"%d-%02d-%02dT%02d:%02d:%02dZ", st->wYear, st->wMonth, st->wDay, st->wHour, st->wMinute, st->wSecond); - else if (date) { - GetDateFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr)); - tszTemp.Append(dateTimeStr); - } - else { - GetTimeFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr)); - tszTemp.Append(dateTimeStr); - } - } - - wcsncpy_s(szDest, cbDest, tszTemp, _TRUNCATE); -} - -MIR_CORE_DLL(int) TimeZone_GetTimeZoneTime(HANDLE hTZ, SYSTEMTIME *st) -{ - if (st == nullptr) return 1; - - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == UTC_TIME_HANDLE) - GetSystemTime(st); - else if (tz && tz != &myInfo.myTZ) { - SYSTEMTIME sto; - GetSystemTime(&sto); - return !SystemTimeToTzSpecificLocalTime(&tz->tzi, &sto, st); - } - else - GetLocalTime(st); - - return 0; -} - -MIR_CORE_DLL(LPCTSTR) TimeZone_GetName(HANDLE hTZ) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == nullptr) - return myInfo.myTZ.tszName; - if (tz == UTC_TIME_HANDLE) - return L"UTC"; - - return tz->tszName; -} - -MIR_CORE_DLL(LPCTSTR) TimeZone_GetDescription(LPCTSTR TZname) -{ - for (auto &tz : g_timezonesBias) - if (!mir_wstrcmp(tz->tszName, TZname)) - return tz->szDisplay; - - return L""; -} - -static void CalcTsOffset(MIM_TIMEZONE *tz) -{ - SYSTEMTIME st, stl; - GetSystemTime(&st); - - FILETIME ft; - SystemTimeToFileTime(&st, &ft); - mir_time ts1 = FileTimeToUnixTime(&ft); - - if (!SystemTimeToTzSpecificLocalTime(&tz->tzi, &st, &stl)) - return; - - SystemTimeToFileTime(&stl, &ft); - mir_time ts2 = FileTimeToUnixTime(&ft); - - tz->offset = ts2 - ts1; -} - -static bool IsSameTime(MIM_TIMEZONE *tz) -{ - SYSTEMTIME st, stl; - - if (tz == &myInfo.myTZ) - return true; - - TimeZone_GetTimeZoneTime(tz, &stl); - TimeZone_GetTimeZoneTime(nullptr, &st); - - return st.wHour == stl.wHour && st.wMinute == stl.wMinute; -} - -MIR_CORE_DLL(HANDLE) TimeZone_CreateByName(LPCTSTR tszName, uint32_t dwFlags) -{ - if (tszName == nullptr) - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; - - if (!(dwFlags & TZF_PLF_CB)) - if (mir_wstrcmp(myInfo.myTZ.tszName, tszName) == 0) - return (dwFlags & TZF_DIFONLY) ? nullptr : &myInfo.myTZ; - - MIM_TIMEZONE tzsearch; - tzsearch.hash = mir_hashstrT(tszName); - - MIM_TIMEZONE *tz = g_timezones.find(&tzsearch); - if (tz == nullptr) - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; - - if (dwFlags & TZF_DIFONLY) - return IsSameTime(tz) ? nullptr : tz; - - return tz; -} - -MIR_CORE_DLL(HANDLE) TimeZone_CreateByContact(MCONTACT hContact, LPCSTR szModule, uint32_t dwFlags) -{ - if (hContact == NULL && szModule == nullptr) - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; - - if (szModule == nullptr) szModule = "UserInfo"; - - DBVARIANT dbv; - if (!db_get_ws(hContact, szModule, "TzName", &dbv)) { - HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags); - db_free(&dbv); - if (res) return res; - } - - signed char timezone = (signed char)db_get_b(hContact, szModule, "Timezone", -1); - if (timezone == -1) { - char *szProto = Proto_GetBaseAccountName(hContact); - if (!db_get_ws(hContact, szProto, "TzName", &dbv)) { - HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags); - db_free(&dbv); - if (res) return res; - } - timezone = (signed char)db_get_b(hContact, szProto, "Timezone", -1); - } - - if (timezone != -1) { - MIM_TIMEZONE tzsearch; - tzsearch.tzi.Bias = timezone * 30; - if (myInfo.myTZ.tzi.Bias == tzsearch.tzi.Bias) { - if (dwFlags & TZF_DIFONLY) return nullptr; - return &myInfo.myTZ; - } - - int i = g_timezonesBias.getIndex(&tzsearch); - while (i >= 0 && g_timezonesBias[i]->tzi.Bias == tzsearch.tzi.Bias) --i; - - int delta = LONG_MAX; - for (int j = ++i; j < g_timezonesBias.getCount() && g_timezonesBias[j]->tzi.Bias == tzsearch.tzi.Bias; ++j) { - int delta1 = abs(g_timezonesBias[j]->tzi.DaylightDate.wMonth - myInfo.myTZ.tzi.DaylightDate.wMonth); - if (delta1 <= delta) { - delta = delta1; - i = j; - } - } - - if (i >= 0) { - MIM_TIMEZONE *tz = g_timezonesBias[i]; - return ((dwFlags & TZF_DIFONLY) && IsSameTime(tz)) ? nullptr : tz; - } - } - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; -} - -MIR_CORE_DLL(void) TimeZone_StoreByContact(MCONTACT hContact, LPCSTR szModule, HANDLE hTZ) -{ - if (szModule == nullptr) szModule = "UserInfo"; - - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz) { - db_set_ws(hContact, szModule, "TzName", tz->tszName); - db_set_b(hContact, szModule, "Timezone", (char)((tz->tzi.Bias + tz->tzi.StandardBias) / 30)); - } - else { - db_unset(hContact, szModule, "TzName"); - db_unset(hContact, szModule, "Timezone"); - } -} - -MIR_CORE_DLL(int) TimeZone_PrintDateTime(HANDLE hTZ, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY))) - return 1; - - if (tz == nullptr) - tz = &myInfo.myTZ; - - SYSTEMTIME st; - if (TimeZone_GetTimeZoneTime(tz, &st)) - return 1; - - FormatTime(&st, szFormat, szDest, cbDest); - return 0; -} - -MIR_CORE_DLL(int) TimeZone_GetSystemTime(HANDLE hTZ, mir_time ts, SYSTEMTIME *dest, uint32_t dwFlags) -{ - if (dest == nullptr) - return 2; - - MIM_TIMEZONE *tz = (MIM_TIMEZONE *)hTZ; - if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY))) { - memset(dest, 0, sizeof(SYSTEMTIME)); - return 1; - } - - if (tz == nullptr) - tz = &myInfo.myTZ; - - FILETIME ft; - if (tz == UTC_TIME_HANDLE) - UnixTimeToFileTime(ts, &ft); - else { - if (tz->offset == INT_MIN) - CalcTsOffset(tz); - - UnixTimeToFileTime(ts + tz->offset, &ft); - } - - FileTimeToSystemTime(&ft, dest); - return 0; -} - -MIR_CORE_DLL(int) TimeZone_PrintTimeStamp(HANDLE hTZ, mir_time ts, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags) -{ - SYSTEMTIME st; - if (!TimeZone_GetSystemTime(hTZ, ts, &st, dwFlags)) - FormatTime(&st, szFormat, szDest, cbDest); - return 0; -} - -MIR_CORE_DLL(LPTIME_ZONE_INFORMATION) TimeZone_GetInfo(HANDLE hTZ) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - return tz ? &tz->tzi : &myInfo.myTZ.tzi; -} - -MIR_CORE_DLL(mir_time) TimeZone_UtcToLocal(HANDLE hTZ, mir_time ts) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == nullptr) - tz = &myInfo.myTZ; - - if (tz == UTC_TIME_HANDLE) - return ts; - - if (tz->offset == INT_MIN) - CalcTsOffset(tz); - - return ts + tz->offset; -} - -/////////////////////////////////////////////////////////////////////////////// - -struct ListMessages -{ - UINT addStr, getSel, setSel, getData, setData; -}; - -static const ListMessages lbMessages = { LB_ADDSTRING, LB_GETCURSEL, LB_SETCURSEL, LB_GETITEMDATA, LB_SETITEMDATA }; -static const ListMessages cbMessages = { CB_ADDSTRING, CB_GETCURSEL, CB_SETCURSEL, CB_GETITEMDATA, CB_SETITEMDATA }; - -static const ListMessages* GetListMessages(HWND hWnd, uint32_t dwFlags) -{ - if (hWnd == nullptr) - return nullptr; - - if (!(dwFlags & (TZF_PLF_CB | TZF_PLF_LB))) { - wchar_t tszClassName[128]; - GetClassName(hWnd, tszClassName, _countof(tszClassName)); - if (!mir_wstrcmpi(tszClassName, L"COMBOBOX")) - dwFlags |= TZF_PLF_CB; - else if (!mir_wstrcmpi(tszClassName, L"LISTBOX")) - dwFlags |= TZF_PLF_LB; - } - - if (dwFlags & TZF_PLF_CB) - return &cbMessages; - if (dwFlags & TZF_PLF_LB) - return &lbMessages; - return nullptr; -} - -/////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) TimeZone_SelectListItem(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) -{ - const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); - if (lstMsg == nullptr) - return -1; - - if (szModule == nullptr) - szModule = "UserInfo"; - - int iSelection = 0; - ptrW tszName(db_get_wsa(hContact, szModule, "TzName")); - if (tszName != NULL) { - unsigned hash = mir_hashstrT(tszName); - for (auto &it : g_timezonesBias) { - if (hash == it->hash) { - iSelection = g_timezonesBias.indexOf(&it) + 1; - break; - } - } - } - else { - signed char cBias = db_get_b(hContact, szModule, "Timezone", -100); - if (cBias != -100) { - int iBias = cBias * 30; - for (auto &it : g_timezonesBias) { - if (iBias == it->tzi.Bias) { - iSelection = g_timezonesBias.indexOf(&it) + 1; - break; - } - } - } - } - - SendMessage(hWnd, lstMsg->setSel, iSelection, 0); - return iSelection; -} - -MIR_CORE_DLL(int) TimeZone_PrepareList(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) -{ - const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); - if (lstMsg == nullptr) - return 0; - - SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)TranslateW_LP(L"<unspecified>")); - - for (auto &it : g_timezonesBias) { - SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)it->szDisplay); - SendMessage(hWnd, lstMsg->setData, g_timezonesBias.indexOf(&it) + 1, (LPARAM)it); - } - - return TimeZone_SelectListItem(hContact, szModule, hWnd, dwFlags); -} - -MIR_CORE_DLL(void) TimeZone_StoreListResult(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) -{ - if (szModule == nullptr) szModule = "UserInfo"; - - const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); - if (lstMsg) { - LRESULT offset = SendMessage(hWnd, lstMsg->getSel, 0, 0); - if (offset > 0) { - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)SendMessage(hWnd, lstMsg->getData, offset, 0); - if ((INT_PTR)tz != CB_ERR && tz != nullptr) - TimeZone_StoreByContact(hContact, szModule, tz); - } - else TimeZone_StoreByContact(hContact, szModule, nullptr); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(uint32_t) TimeZone_ToLocal(uint32_t timeVal) -{ - return TimeZone_UtcToLocal(nullptr, (mir_time)timeVal); -} - -MIR_CORE_DLL(char*) TimeZone_ToString(mir_time timeVal, const char *szFormat, char *szDest, size_t cchDest) -{ - wchar_t *szTemp = (wchar_t*)alloca(cchDest*sizeof(wchar_t)); - TimeZone_PrintTimeStamp(nullptr, timeVal, _A2T(szFormat), szTemp, cchDest, 0); - WideCharToMultiByte(CP_ACP, 0, szTemp, -1, szDest, (int)cchDest, nullptr, nullptr); - return szDest; -} - -MIR_CORE_DLL(wchar_t*) TimeZone_ToStringW(mir_time timeVal, const wchar_t *wszFormat, wchar_t *wszDest, size_t cchDest) -{ - TimeZone_PrintTimeStamp(nullptr, timeVal, wszFormat, wszDest, cchDest, 0); - return wszDest; -} - -/////////////////////////////////////////////////////////////////////////////// - -void GetLocalizedString(HKEY hSubKey, const wchar_t *szName, wchar_t *szBuf, uint32_t cbLen) -{ - DWORD dwLength = cbLen * sizeof(wchar_t); - RegQueryValueEx(hSubKey, szName, nullptr, nullptr, (unsigned char *)szBuf, &dwLength); - szBuf[min(dwLength / sizeof(wchar_t), cbLen - 1)] = 0; -} - -void RecalculateTime(void) -{ - GetTimeZoneInformation(&myInfo.myTZ.tzi); - myInfo.timestamp = time(0); - myInfo.myTZ.offset = INT_MIN; - - bool found = false; - DYNAMIC_TIME_ZONE_INFORMATION dtzi; - - if (pfnGetDynamicTimeZoneInformation && pfnGetDynamicTimeZoneInformation(&dtzi) != TIME_ZONE_ID_INVALID) { - wcsncpy_s(myInfo.myTZ.tszName, dtzi.TimeZoneKeyName, _TRUNCATE); - found = true; - } - - for (auto &tz : g_timezones) { - if (tz->offset != INT_MIN) - tz->offset = INT_MIN; - - if (!found) { - if (!mir_wstrcmp(tz->tzi.StandardName, myInfo.myTZ.tzi.StandardName) || !mir_wstrcmp(tz->tzi.DaylightName, myInfo.myTZ.tzi.DaylightName)) { - wcsncpy_s(myInfo.myTZ.tszName, tz->tszName, _TRUNCATE); - found = true; - } - } - } -} - -void InitTimeZones(void) -{ - REG_TZI_FORMAT tzi; - HKEY hKey; - - const wchar_t *tszKey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"; - - /* - * use GetDynamicTimeZoneInformation() on Vista+ - this will return a structure with - * the registry key name, so finding our own time zone later will be MUCH easier for - * localized systems or systems with a MUI pack installed - */ - if (IsWinVerVistaPlus()) - pfnGetDynamicTimeZoneInformation = (pfnGetDynamicTimeZoneInformation_t)GetProcAddress(GetModuleHandle(L"kernel32"), "GetDynamicTimeZoneInformation"); - - if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, tszKey, 0, KEY_ENUMERATE_SUB_KEYS, &hKey)) { - uint32_t dwIndex = 0; - HKEY hSubKey; - wchar_t tszName[MIM_TZ_NAMELEN]; - - DWORD dwSize = _countof(tszName); - while (ERROR_NO_MORE_ITEMS != RegEnumKeyEx(hKey, dwIndex++, tszName, &dwSize, nullptr, nullptr, nullptr, nullptr)) { - if (ERROR_SUCCESS == RegOpenKeyEx(hKey, tszName, 0, KEY_QUERY_VALUE, &hSubKey)) { - dwSize = sizeof(tszName); - - DWORD dwLength = sizeof(tzi); - if (ERROR_SUCCESS != RegQueryValueEx(hSubKey, L"TZI", nullptr, nullptr, (unsigned char *)&tzi, &dwLength)) - continue; - - MIM_TIMEZONE *tz = new MIM_TIMEZONE; - - tz->tzi.Bias = tzi.Bias; - tz->tzi.StandardDate = tzi.StandardDate; - tz->tzi.StandardBias = tzi.StandardBias; - tz->tzi.DaylightDate = tzi.DaylightDate; - tz->tzi.DaylightBias = tzi.DaylightBias; - - mir_wstrcpy(tz->tszName, tszName); - tz->hash = mir_hashstrT(tszName); - tz->offset = INT_MIN; - - GetLocalizedString(hSubKey, L"Display", tz->szDisplay, _countof(tz->szDisplay)); - GetLocalizedString(hSubKey, L"Std", tz->tzi.StandardName, _countof(tz->tzi.StandardName)); - GetLocalizedString(hSubKey, L"Dlt", tz->tzi.DaylightName, _countof(tz->tzi.DaylightName)); - - g_timezones.insert(tz); - g_timezonesBias.insert(tz); - - RegCloseKey(hSubKey); - } - dwSize = _countof(tszName); - } - RegCloseKey(hKey); - } - - RecalculateTime(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+implements services to handle location - based timezones, instead of
+simple UTC offsets.
+*/
+
+#include "../stdafx.h"
+
+typedef uint32_t (WINAPI *pfnGetDynamicTimeZoneInformation_t)(DYNAMIC_TIME_ZONE_INFORMATION *pdtzi);
+static pfnGetDynamicTimeZoneInformation_t pfnGetDynamicTimeZoneInformation;
+
+struct REG_TZI_FORMAT
+{
+ LONG Bias;
+ LONG StandardBias;
+ LONG DaylightBias;
+ SYSTEMTIME StandardDate;
+ SYSTEMTIME DaylightDate;
+};
+
+#define MIM_TZ_DISPLAYLEN 128
+
+struct MIM_TIMEZONE
+{
+ unsigned hash;
+ int offset;
+
+ wchar_t tszName[MIM_TZ_NAMELEN]; // windows name for the time zone
+ wchar_t szDisplay[MIM_TZ_DISPLAYLEN]; // more descriptive display name (that's what usually appears in dialogs)
+ // every hour should be sufficient.
+ TIME_ZONE_INFORMATION tzi;
+
+ static int compareBias(const MIM_TIMEZONE* p1, const MIM_TIMEZONE* p2)
+ { return p2->tzi.Bias - p1->tzi.Bias;
+ }
+};
+
+struct TZ_INT_INFO
+{
+ uint32_t timestamp; // last time updated
+ MIM_TIMEZONE myTZ; // set to my own timezone
+};
+
+static TZ_INT_INFO myInfo;
+
+static OBJLIST<MIM_TIMEZONE> g_timezones(55, NumericKeySortT);
+static LIST<MIM_TIMEZONE> g_timezonesBias(55, MIM_TIMEZONE::compareBias);
+
+// KB167296
+void UnixTimeToFileTime(mir_time ts, LPFILETIME pft)
+{
+ unsigned __int64 ll = UInt32x32To64(ts, 10000000) + 116444736000000000i64;
+ pft->dwLowDateTime = (uint32_t)ll;
+ pft->dwHighDateTime = ll >> 32;
+}
+
+mir_time FileTimeToUnixTime(LPFILETIME pft)
+{
+ unsigned __int64 ll = (unsigned __int64)pft->dwHighDateTime << 32 | pft->dwLowDateTime;
+ ll -= 116444736000000000i64;
+ return (mir_time)(ll / 10000000);
+}
+
+void FormatTime(const SYSTEMTIME *st, const wchar_t *szFormat, wchar_t *szDest, size_t cbDest)
+{
+ if (szDest == nullptr || cbDest == 0) return;
+
+ CMStringW tszTemp;
+
+ for (const wchar_t* pFormat = szFormat; *pFormat; ++pFormat) {
+ uint32_t fmt = 0;
+ bool date = false, iso = false;
+ switch (*pFormat) {
+ case 't':
+ fmt = TIME_NOSECONDS;
+ date = false;
+ break;
+
+ case 's':
+ fmt = 0;
+ date = false;
+ break;
+
+ case 'm':
+ fmt = TIME_NOMINUTESORSECONDS;
+ date = false;
+ break;
+
+ case 'd':
+ fmt = DATE_SHORTDATE;
+ date = true;
+ break;
+
+ case 'D':
+ fmt = DATE_LONGDATE;
+ date = true;
+ break;
+
+ case 'I':
+ iso = true;
+ break;
+
+ default:
+ tszTemp.AppendChar(*pFormat);
+ continue;
+ }
+
+ wchar_t dateTimeStr[64];
+ if (iso)
+ tszTemp.AppendFormat(L"%d-%02d-%02dT%02d:%02d:%02dZ", st->wYear, st->wMonth, st->wDay, st->wHour, st->wMinute, st->wSecond);
+ else if (date) {
+ GetDateFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr));
+ tszTemp.Append(dateTimeStr);
+ }
+ else {
+ GetTimeFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr));
+ tszTemp.Append(dateTimeStr);
+ }
+ }
+
+ wcsncpy_s(szDest, cbDest, tszTemp, _TRUNCATE);
+}
+
+MIR_CORE_DLL(int) TimeZone_GetTimeZoneTime(HANDLE hTZ, SYSTEMTIME *st)
+{
+ if (st == nullptr) return 1;
+
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == UTC_TIME_HANDLE)
+ GetSystemTime(st);
+ else if (tz && tz != &myInfo.myTZ) {
+ SYSTEMTIME sto;
+ GetSystemTime(&sto);
+ return !SystemTimeToTzSpecificLocalTime(&tz->tzi, &sto, st);
+ }
+ else
+ GetLocalTime(st);
+
+ return 0;
+}
+
+MIR_CORE_DLL(LPCTSTR) TimeZone_GetName(HANDLE hTZ)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == nullptr)
+ return myInfo.myTZ.tszName;
+ if (tz == UTC_TIME_HANDLE)
+ return L"UTC";
+
+ return tz->tszName;
+}
+
+MIR_CORE_DLL(LPCTSTR) TimeZone_GetDescription(LPCTSTR TZname)
+{
+ for (auto &tz : g_timezonesBias)
+ if (!mir_wstrcmp(tz->tszName, TZname))
+ return tz->szDisplay;
+
+ return L"";
+}
+
+static void CalcTsOffset(MIM_TIMEZONE *tz)
+{
+ SYSTEMTIME st, stl;
+ GetSystemTime(&st);
+
+ FILETIME ft;
+ SystemTimeToFileTime(&st, &ft);
+ mir_time ts1 = FileTimeToUnixTime(&ft);
+
+ if (!SystemTimeToTzSpecificLocalTime(&tz->tzi, &st, &stl))
+ return;
+
+ SystemTimeToFileTime(&stl, &ft);
+ mir_time ts2 = FileTimeToUnixTime(&ft);
+
+ tz->offset = ts2 - ts1;
+}
+
+static bool IsSameTime(MIM_TIMEZONE *tz)
+{
+ SYSTEMTIME st, stl;
+
+ if (tz == &myInfo.myTZ)
+ return true;
+
+ TimeZone_GetTimeZoneTime(tz, &stl);
+ TimeZone_GetTimeZoneTime(nullptr, &st);
+
+ return st.wHour == stl.wHour && st.wMinute == stl.wMinute;
+}
+
+MIR_CORE_DLL(HANDLE) TimeZone_CreateByName(LPCTSTR tszName, uint32_t dwFlags)
+{
+ if (tszName == nullptr)
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ;
+
+ if (!(dwFlags & TZF_PLF_CB))
+ if (mir_wstrcmp(myInfo.myTZ.tszName, tszName) == 0)
+ return (dwFlags & TZF_DIFONLY) ? nullptr : &myInfo.myTZ;
+
+ MIM_TIMEZONE tzsearch;
+ tzsearch.hash = mir_hashstrT(tszName);
+
+ MIM_TIMEZONE *tz = g_timezones.find(&tzsearch);
+ if (tz == nullptr)
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ;
+
+ if (dwFlags & TZF_DIFONLY)
+ return IsSameTime(tz) ? nullptr : tz;
+
+ return tz;
+}
+
+MIR_CORE_DLL(HANDLE) TimeZone_CreateByContact(MCONTACT hContact, LPCSTR szModule, uint32_t dwFlags)
+{
+ if (hContact == NULL && szModule == nullptr)
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ;
+
+ if (szModule == nullptr) szModule = "UserInfo";
+
+ DBVARIANT dbv;
+ if (!db_get_ws(hContact, szModule, "TzName", &dbv)) {
+ HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags);
+ db_free(&dbv);
+ if (res) return res;
+ }
+
+ signed char timezone = (signed char)db_get_b(hContact, szModule, "Timezone", -1);
+ if (timezone == -1) {
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (!db_get_ws(hContact, szProto, "TzName", &dbv)) {
+ HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags);
+ db_free(&dbv);
+ if (res) return res;
+ }
+ timezone = (signed char)db_get_b(hContact, szProto, "Timezone", -1);
+ }
+
+ if (timezone != -1) {
+ MIM_TIMEZONE tzsearch;
+ tzsearch.tzi.Bias = timezone * 30;
+ if (myInfo.myTZ.tzi.Bias == tzsearch.tzi.Bias) {
+ if (dwFlags & TZF_DIFONLY) return nullptr;
+ return &myInfo.myTZ;
+ }
+
+ int i = g_timezonesBias.getIndex(&tzsearch);
+ while (i >= 0 && g_timezonesBias[i]->tzi.Bias == tzsearch.tzi.Bias) --i;
+
+ int delta = LONG_MAX;
+ for (int j = ++i; j < g_timezonesBias.getCount() && g_timezonesBias[j]->tzi.Bias == tzsearch.tzi.Bias; ++j) {
+ int delta1 = abs(g_timezonesBias[j]->tzi.DaylightDate.wMonth - myInfo.myTZ.tzi.DaylightDate.wMonth);
+ if (delta1 <= delta) {
+ delta = delta1;
+ i = j;
+ }
+ }
+
+ if (i >= 0) {
+ MIM_TIMEZONE *tz = g_timezonesBias[i];
+ return ((dwFlags & TZF_DIFONLY) && IsSameTime(tz)) ? nullptr : tz;
+ }
+ }
+ return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ;
+}
+
+MIR_CORE_DLL(void) TimeZone_StoreByContact(MCONTACT hContact, LPCSTR szModule, HANDLE hTZ)
+{
+ if (szModule == nullptr) szModule = "UserInfo";
+
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz) {
+ db_set_ws(hContact, szModule, "TzName", tz->tszName);
+ db_set_b(hContact, szModule, "Timezone", (char)((tz->tzi.Bias + tz->tzi.StandardBias) / 30));
+ }
+ else {
+ db_unset(hContact, szModule, "TzName");
+ db_unset(hContact, szModule, "Timezone");
+ }
+}
+
+MIR_CORE_DLL(int) TimeZone_PrintDateTime(HANDLE hTZ, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)))
+ return 1;
+
+ if (tz == nullptr)
+ tz = &myInfo.myTZ;
+
+ SYSTEMTIME st;
+ if (TimeZone_GetTimeZoneTime(tz, &st))
+ return 1;
+
+ FormatTime(&st, szFormat, szDest, cbDest);
+ return 0;
+}
+
+MIR_CORE_DLL(int) TimeZone_GetSystemTime(HANDLE hTZ, mir_time ts, SYSTEMTIME *dest, uint32_t dwFlags)
+{
+ if (dest == nullptr)
+ return 2;
+
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE *)hTZ;
+ if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY))) {
+ memset(dest, 0, sizeof(SYSTEMTIME));
+ return 1;
+ }
+
+ if (tz == nullptr)
+ tz = &myInfo.myTZ;
+
+ FILETIME ft;
+ if (tz == UTC_TIME_HANDLE)
+ UnixTimeToFileTime(ts, &ft);
+ else {
+ if (tz->offset == INT_MIN)
+ CalcTsOffset(tz);
+
+ UnixTimeToFileTime(ts + tz->offset, &ft);
+ }
+
+ FileTimeToSystemTime(&ft, dest);
+ return 0;
+}
+
+MIR_CORE_DLL(int) TimeZone_PrintTimeStamp(HANDLE hTZ, mir_time ts, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags)
+{
+ SYSTEMTIME st;
+ if (!TimeZone_GetSystemTime(hTZ, ts, &st, dwFlags))
+ FormatTime(&st, szFormat, szDest, cbDest);
+ return 0;
+}
+
+MIR_CORE_DLL(LPTIME_ZONE_INFORMATION) TimeZone_GetInfo(HANDLE hTZ)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ return tz ? &tz->tzi : &myInfo.myTZ.tzi;
+}
+
+MIR_CORE_DLL(mir_time) TimeZone_UtcToLocal(HANDLE hTZ, mir_time ts)
+{
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ;
+ if (tz == nullptr)
+ tz = &myInfo.myTZ;
+
+ if (tz == UTC_TIME_HANDLE)
+ return ts;
+
+ if (tz->offset == INT_MIN)
+ CalcTsOffset(tz);
+
+ return ts + tz->offset;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct ListMessages
+{
+ UINT addStr, getSel, setSel, getData, setData;
+};
+
+static const ListMessages lbMessages = { LB_ADDSTRING, LB_GETCURSEL, LB_SETCURSEL, LB_GETITEMDATA, LB_SETITEMDATA };
+static const ListMessages cbMessages = { CB_ADDSTRING, CB_GETCURSEL, CB_SETCURSEL, CB_GETITEMDATA, CB_SETITEMDATA };
+
+static const ListMessages* GetListMessages(HWND hWnd, uint32_t dwFlags)
+{
+ if (hWnd == nullptr)
+ return nullptr;
+
+ if (!(dwFlags & (TZF_PLF_CB | TZF_PLF_LB))) {
+ wchar_t tszClassName[128];
+ GetClassName(hWnd, tszClassName, _countof(tszClassName));
+ if (!mir_wstrcmpi(tszClassName, L"COMBOBOX"))
+ dwFlags |= TZF_PLF_CB;
+ else if (!mir_wstrcmpi(tszClassName, L"LISTBOX"))
+ dwFlags |= TZF_PLF_LB;
+ }
+
+ if (dwFlags & TZF_PLF_CB)
+ return &cbMessages;
+ if (dwFlags & TZF_PLF_LB)
+ return &lbMessages;
+ return nullptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) TimeZone_SelectListItem(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags)
+{
+ const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags);
+ if (lstMsg == nullptr)
+ return -1;
+
+ if (szModule == nullptr)
+ szModule = "UserInfo";
+
+ int iSelection = 0;
+ ptrW tszName(db_get_wsa(hContact, szModule, "TzName"));
+ if (tszName != NULL) {
+ unsigned hash = mir_hashstrT(tszName);
+ for (auto &it : g_timezonesBias) {
+ if (hash == it->hash) {
+ iSelection = g_timezonesBias.indexOf(&it) + 1;
+ break;
+ }
+ }
+ }
+ else {
+ signed char cBias = db_get_b(hContact, szModule, "Timezone", -100);
+ if (cBias != -100) {
+ int iBias = cBias * 30;
+ for (auto &it : g_timezonesBias) {
+ if (iBias == it->tzi.Bias) {
+ iSelection = g_timezonesBias.indexOf(&it) + 1;
+ break;
+ }
+ }
+ }
+ }
+
+ SendMessage(hWnd, lstMsg->setSel, iSelection, 0);
+ return iSelection;
+}
+
+MIR_CORE_DLL(int) TimeZone_PrepareList(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags)
+{
+ const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags);
+ if (lstMsg == nullptr)
+ return 0;
+
+ SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)TranslateW_LP(L"<unspecified>"));
+
+ for (auto &it : g_timezonesBias) {
+ SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)it->szDisplay);
+ SendMessage(hWnd, lstMsg->setData, g_timezonesBias.indexOf(&it) + 1, (LPARAM)it);
+ }
+
+ return TimeZone_SelectListItem(hContact, szModule, hWnd, dwFlags);
+}
+
+MIR_CORE_DLL(void) TimeZone_StoreListResult(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags)
+{
+ if (szModule == nullptr) szModule = "UserInfo";
+
+ const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags);
+ if (lstMsg) {
+ LRESULT offset = SendMessage(hWnd, lstMsg->getSel, 0, 0);
+ if (offset > 0) {
+ MIM_TIMEZONE *tz = (MIM_TIMEZONE*)SendMessage(hWnd, lstMsg->getData, offset, 0);
+ if ((INT_PTR)tz != CB_ERR && tz != nullptr)
+ TimeZone_StoreByContact(hContact, szModule, tz);
+ }
+ else TimeZone_StoreByContact(hContact, szModule, nullptr);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(uint32_t) TimeZone_ToLocal(uint32_t timeVal)
+{
+ return TimeZone_UtcToLocal(nullptr, (mir_time)timeVal);
+}
+
+MIR_CORE_DLL(char*) TimeZone_ToString(mir_time timeVal, const char *szFormat, char *szDest, size_t cchDest)
+{
+ wchar_t *szTemp = (wchar_t*)alloca(cchDest*sizeof(wchar_t));
+ TimeZone_PrintTimeStamp(nullptr, timeVal, _A2T(szFormat), szTemp, cchDest, 0);
+ WideCharToMultiByte(CP_ACP, 0, szTemp, -1, szDest, (int)cchDest, nullptr, nullptr);
+ return szDest;
+}
+
+MIR_CORE_DLL(wchar_t*) TimeZone_ToStringW(mir_time timeVal, const wchar_t *wszFormat, wchar_t *wszDest, size_t cchDest)
+{
+ TimeZone_PrintTimeStamp(nullptr, timeVal, wszFormat, wszDest, cchDest, 0);
+ return wszDest;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GetLocalizedString(HKEY hSubKey, const wchar_t *szName, wchar_t *szBuf, uint32_t cbLen)
+{
+ DWORD dwLength = cbLen * sizeof(wchar_t);
+ RegQueryValueEx(hSubKey, szName, nullptr, nullptr, (unsigned char *)szBuf, &dwLength);
+ szBuf[min(dwLength / sizeof(wchar_t), cbLen - 1)] = 0;
+}
+
+void RecalculateTime(void)
+{
+ GetTimeZoneInformation(&myInfo.myTZ.tzi);
+ myInfo.timestamp = time(0);
+ myInfo.myTZ.offset = INT_MIN;
+
+ bool found = false;
+ DYNAMIC_TIME_ZONE_INFORMATION dtzi;
+
+ if (pfnGetDynamicTimeZoneInformation && pfnGetDynamicTimeZoneInformation(&dtzi) != TIME_ZONE_ID_INVALID) {
+ wcsncpy_s(myInfo.myTZ.tszName, dtzi.TimeZoneKeyName, _TRUNCATE);
+ found = true;
+ }
+
+ for (auto &tz : g_timezones) {
+ if (tz->offset != INT_MIN)
+ tz->offset = INT_MIN;
+
+ if (!found) {
+ if (!mir_wstrcmp(tz->tzi.StandardName, myInfo.myTZ.tzi.StandardName) || !mir_wstrcmp(tz->tzi.DaylightName, myInfo.myTZ.tzi.DaylightName)) {
+ wcsncpy_s(myInfo.myTZ.tszName, tz->tszName, _TRUNCATE);
+ found = true;
+ }
+ }
+ }
+}
+
+void InitTimeZones(void)
+{
+ REG_TZI_FORMAT tzi;
+ HKEY hKey;
+
+ const wchar_t *tszKey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones";
+
+ /*
+ * use GetDynamicTimeZoneInformation() on Vista+ - this will return a structure with
+ * the registry key name, so finding our own time zone later will be MUCH easier for
+ * localized systems or systems with a MUI pack installed
+ */
+ if (IsWinVerVistaPlus())
+ pfnGetDynamicTimeZoneInformation = (pfnGetDynamicTimeZoneInformation_t)GetProcAddress(GetModuleHandle(L"kernel32"), "GetDynamicTimeZoneInformation");
+
+ if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, tszKey, 0, KEY_ENUMERATE_SUB_KEYS, &hKey)) {
+ uint32_t dwIndex = 0;
+ HKEY hSubKey;
+ wchar_t tszName[MIM_TZ_NAMELEN];
+
+ DWORD dwSize = _countof(tszName);
+ while (ERROR_NO_MORE_ITEMS != RegEnumKeyEx(hKey, dwIndex++, tszName, &dwSize, nullptr, nullptr, nullptr, nullptr)) {
+ if (ERROR_SUCCESS == RegOpenKeyEx(hKey, tszName, 0, KEY_QUERY_VALUE, &hSubKey)) {
+ dwSize = sizeof(tszName);
+
+ DWORD dwLength = sizeof(tzi);
+ if (ERROR_SUCCESS != RegQueryValueEx(hSubKey, L"TZI", nullptr, nullptr, (unsigned char *)&tzi, &dwLength))
+ continue;
+
+ MIM_TIMEZONE *tz = new MIM_TIMEZONE;
+
+ tz->tzi.Bias = tzi.Bias;
+ tz->tzi.StandardDate = tzi.StandardDate;
+ tz->tzi.StandardBias = tzi.StandardBias;
+ tz->tzi.DaylightDate = tzi.DaylightDate;
+ tz->tzi.DaylightBias = tzi.DaylightBias;
+
+ mir_wstrcpy(tz->tszName, tszName);
+ tz->hash = mir_hashstrT(tszName);
+ tz->offset = INT_MIN;
+
+ GetLocalizedString(hSubKey, L"Display", tz->szDisplay, _countof(tz->szDisplay));
+ GetLocalizedString(hSubKey, L"Std", tz->tzi.StandardName, _countof(tz->tzi.StandardName));
+ GetLocalizedString(hSubKey, L"Dlt", tz->tzi.DaylightName, _countof(tz->tzi.DaylightName));
+
+ g_timezones.insert(tz);
+ g_timezonesBias.insert(tz);
+
+ RegCloseKey(hSubKey);
+ }
+ dwSize = _countof(tszName);
+ }
+ RegCloseKey(hKey);
+ }
+
+ RecalculateTime();
+}
diff --git a/src/mir_core/src/Windows/windowlist.cpp b/src/mir_core/src/Windows/windowlist.cpp index 6b6e28ad66..616db3df5c 100644 --- a/src/mir_core/src/Windows/windowlist.cpp +++ b/src/mir_core/src/Windows/windowlist.cpp @@ -1,105 +1,105 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -struct TWindowListItem -{ - TWindowListItem(UINT_PTR _param, HWND _wnd) : - param(_param), - hWnd(_wnd) - {} - - UINT_PTR param; - HWND hWnd; -}; - -struct TWindowList : public OBJLIST<TWindowListItem> -{ - TWindowList() : - OBJLIST<TWindowListItem>(10, NumericKeySortT) - {} -}; - -MIR_CORE_DLL(MWindowList) WindowList_Create(void) -{ - return new TWindowList(); -} - -MIR_CORE_DLL(void) WindowList_Destroy(MWindowList &hList) -{ - delete hList; - hList = nullptr; -} - -MIR_CORE_DLL(int) WindowList_Add(MWindowList hList, HWND hwnd, UINT_PTR param) -{ - if (hList == nullptr) - return 1; - - hList->insert(new TWindowListItem(param, hwnd)); - return 0; -} - -MIR_CORE_DLL(int) WindowList_Remove(MWindowList hList, HWND hwnd) -{ - if (hList == nullptr) return 1; - - for (auto &it : *hList) - if (it->hWnd == hwnd) { - hList->removeItem(&it); - return 0; - } - - return 1; -} - -MIR_CORE_DLL(HWND) WindowList_Find(MWindowList hList, UINT_PTR param) -{ - if (hList == nullptr) - return nullptr; - - TWindowListItem *p = hList->find((TWindowListItem*)¶m); - return (p == nullptr) ? nullptr : p->hWnd; -} - -MIR_CORE_DLL(int) WindowList_Broadcast(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam) -{ - if (hList == nullptr) - return 0; - - for (auto &it : hList->rev_iter()) - SendMessage(it->hWnd, message, wParam, lParam); - return 0; -} - -MIR_CORE_DLL(int) WindowList_BroadcastAsync(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam) -{ - if (hList == nullptr) - return 0; - - for (auto &it : hList->rev_iter()) - PostMessage(it->hWnd, message, wParam, lParam); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+struct TWindowListItem
+{
+ TWindowListItem(UINT_PTR _param, HWND _wnd) :
+ param(_param),
+ hWnd(_wnd)
+ {}
+
+ UINT_PTR param;
+ HWND hWnd;
+};
+
+struct TWindowList : public OBJLIST<TWindowListItem>
+{
+ TWindowList() :
+ OBJLIST<TWindowListItem>(10, NumericKeySortT)
+ {}
+};
+
+MIR_CORE_DLL(MWindowList) WindowList_Create(void)
+{
+ return new TWindowList();
+}
+
+MIR_CORE_DLL(void) WindowList_Destroy(MWindowList &hList)
+{
+ delete hList;
+ hList = nullptr;
+}
+
+MIR_CORE_DLL(int) WindowList_Add(MWindowList hList, HWND hwnd, UINT_PTR param)
+{
+ if (hList == nullptr)
+ return 1;
+
+ hList->insert(new TWindowListItem(param, hwnd));
+ return 0;
+}
+
+MIR_CORE_DLL(int) WindowList_Remove(MWindowList hList, HWND hwnd)
+{
+ if (hList == nullptr) return 1;
+
+ for (auto &it : *hList)
+ if (it->hWnd == hwnd) {
+ hList->removeItem(&it);
+ return 0;
+ }
+
+ return 1;
+}
+
+MIR_CORE_DLL(HWND) WindowList_Find(MWindowList hList, UINT_PTR param)
+{
+ if (hList == nullptr)
+ return nullptr;
+
+ TWindowListItem *p = hList->find((TWindowListItem*)¶m);
+ return (p == nullptr) ? nullptr : p->hWnd;
+}
+
+MIR_CORE_DLL(int) WindowList_Broadcast(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ if (hList == nullptr)
+ return 0;
+
+ for (auto &it : hList->rev_iter())
+ SendMessage(it->hWnd, message, wParam, lParam);
+ return 0;
+}
+
+MIR_CORE_DLL(int) WindowList_BroadcastAsync(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ if (hList == nullptr)
+ return 0;
+
+ for (auto &it : hList->rev_iter())
+ PostMessage(it->hWnd, message, wParam, lParam);
+ return 0;
+}
diff --git a/src/mir_core/src/Windows/winutil.cpp b/src/mir_core/src/Windows/winutil.cpp index 7a25523482..1d18041529 100644 --- a/src/mir_core/src/Windows/winutil.cpp +++ b/src/mir_core/src/Windows/winutil.cpp @@ -1,177 +1,177 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -MIR_CORE_DLL(int) Utils_SaveWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix) -{ - WINDOWPLACEMENT wp; - wp.length = sizeof(wp); - GetWindowPlacement(hwnd, &wp); - - char szSettingName[64]; - mir_snprintf(szSettingName, "%sx", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.left); - - mir_snprintf(szSettingName, "%sy", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.top); - - mir_snprintf(szSettingName, "%swidth", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.right-wp.rcNormalPosition.left); - - mir_snprintf(szSettingName, "%sheight", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.bottom-wp.rcNormalPosition.top); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -EXTERN_C MIR_CORE_DLL(int) Utils_RestoreWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix, int flags) -{ - WINDOWPLACEMENT wp; - wp.length = sizeof(wp); - GetWindowPlacement(hwnd, &wp); - - char szSettingName[64]; - mir_snprintf(szSettingName, "%sx", szNamePrefix); - int x = db_get_dw(hContact, szModule, szSettingName, -1); - if (x == -1) - return 1; - - mir_snprintf(szSettingName, "%sy", szNamePrefix); - int y = (int)db_get_dw(hContact, szModule, szSettingName, -1); - - if (flags & RWPF_NOSIZE) - OffsetRect(&wp.rcNormalPosition, x-wp.rcNormalPosition.left, y-wp.rcNormalPosition.top); - else { - wp.rcNormalPosition.left = x; - wp.rcNormalPosition.top = y; - - mir_snprintf(szSettingName, "%swidth", szNamePrefix); - wp.rcNormalPosition.right = wp.rcNormalPosition.left+db_get_dw(hContact, szModule, szSettingName, -1); - - mir_snprintf(szSettingName, "%sheight", szNamePrefix); - wp.rcNormalPosition.bottom = wp.rcNormalPosition.top+db_get_dw(hContact, szModule, szSettingName, -1); - } - wp.flags = 0; - if (flags & RWPF_HIDDEN) - wp.showCmd = SW_HIDE; - if (flags & RWPF_NOACTIVATE) - wp.showCmd = SW_SHOWNOACTIVATE; - - if (!(flags & RWPF_NOMOVE)) - Utils_AssertInsideScreen(&wp.rcNormalPosition); - - SetWindowPlacement(hwnd, &wp); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) Utils_AssertInsideScreen(RECT *rc) -{ - if (rc == nullptr) - return -1; - - RECT rcScreen; - SystemParametersInfo(SPI_GETWORKAREA, 0, &rcScreen, FALSE); - if (MonitorFromRect(rc, MONITOR_DEFAULTTONULL)) - return 0; - - MONITORINFO mi = { 0 }; - HMONITOR hMonitor = MonitorFromRect(rc, MONITOR_DEFAULTTONEAREST); - mi.cbSize = sizeof(mi); - if (GetMonitorInfo(hMonitor, &mi)) - rcScreen = mi.rcWork; - - if (rc->top >= rcScreen.bottom) - OffsetRect(rc, 0, rcScreen.bottom - rc->bottom); - else if (rc->bottom <= rcScreen.top) - OffsetRect(rc, 0, rcScreen.top - rc->top); - if (rc->left >= rcScreen.right) - OffsetRect(rc, rcScreen.right - rc->right, 0); - else if (rc->right <= rcScreen.left) - OffsetRect(rc, rcScreen.left - rc->left, 0); - - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static NONCLIENTMETRICSW ncm = {}; - -MIR_CORE_DLL(int) Utils_CorrectFontSize(int size) -{ - if (!g_bEnableDpiAware) - return size; - - if (!ncm.cbSize) { - ncm.cbSize = sizeof(ncm); - SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE); - } - - return size * ncm.lfMessageFont.lfHeight / -12; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) Utils_ClipboardCopy(const char *pszText) -{ - size_t cbLen = mir_strlen(pszText); - if (!cbLen) - return; - - if (!OpenClipboard(nullptr)) - return; - - EmptyClipboard(); - - HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cbLen); - if (hData) { - mir_strcpy((char *)GlobalLock(hData), pszText); - GlobalUnlock(hData); - SetClipboardData(CF_TEXT, hData); - } - CloseClipboard(); -} - -MIR_CORE_DLL(void) Utils_ClipboardCopy(const wchar_t *pwszText) -{ - size_t cbLen = mir_wstrlen(pwszText); - if (!cbLen) - return; - - if (!OpenClipboard(nullptr)) - return; - - EmptyClipboard(); - - HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (cbLen + 1) * sizeof(wchar_t)); - if (hData) { - mir_wstrcpy((wchar_t *)GlobalLock(hData), pwszText); - GlobalUnlock(hData); - SetClipboardData(CF_UNICODETEXT, hData); - } - CloseClipboard(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "../stdafx.h"
+
+MIR_CORE_DLL(int) Utils_SaveWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix)
+{
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ GetWindowPlacement(hwnd, &wp);
+
+ char szSettingName[64];
+ mir_snprintf(szSettingName, "%sx", szNamePrefix);
+ db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.left);
+
+ mir_snprintf(szSettingName, "%sy", szNamePrefix);
+ db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.top);
+
+ mir_snprintf(szSettingName, "%swidth", szNamePrefix);
+ db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.right-wp.rcNormalPosition.left);
+
+ mir_snprintf(szSettingName, "%sheight", szNamePrefix);
+ db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.bottom-wp.rcNormalPosition.top);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+EXTERN_C MIR_CORE_DLL(int) Utils_RestoreWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix, int flags)
+{
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ GetWindowPlacement(hwnd, &wp);
+
+ char szSettingName[64];
+ mir_snprintf(szSettingName, "%sx", szNamePrefix);
+ int x = db_get_dw(hContact, szModule, szSettingName, -1);
+ if (x == -1)
+ return 1;
+
+ mir_snprintf(szSettingName, "%sy", szNamePrefix);
+ int y = (int)db_get_dw(hContact, szModule, szSettingName, -1);
+
+ if (flags & RWPF_NOSIZE)
+ OffsetRect(&wp.rcNormalPosition, x-wp.rcNormalPosition.left, y-wp.rcNormalPosition.top);
+ else {
+ wp.rcNormalPosition.left = x;
+ wp.rcNormalPosition.top = y;
+
+ mir_snprintf(szSettingName, "%swidth", szNamePrefix);
+ wp.rcNormalPosition.right = wp.rcNormalPosition.left+db_get_dw(hContact, szModule, szSettingName, -1);
+
+ mir_snprintf(szSettingName, "%sheight", szNamePrefix);
+ wp.rcNormalPosition.bottom = wp.rcNormalPosition.top+db_get_dw(hContact, szModule, szSettingName, -1);
+ }
+ wp.flags = 0;
+ if (flags & RWPF_HIDDEN)
+ wp.showCmd = SW_HIDE;
+ if (flags & RWPF_NOACTIVATE)
+ wp.showCmd = SW_SHOWNOACTIVATE;
+
+ if (!(flags & RWPF_NOMOVE))
+ Utils_AssertInsideScreen(&wp.rcNormalPosition);
+
+ SetWindowPlacement(hwnd, &wp);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) Utils_AssertInsideScreen(RECT *rc)
+{
+ if (rc == nullptr)
+ return -1;
+
+ RECT rcScreen;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcScreen, FALSE);
+ if (MonitorFromRect(rc, MONITOR_DEFAULTTONULL))
+ return 0;
+
+ MONITORINFO mi = { 0 };
+ HMONITOR hMonitor = MonitorFromRect(rc, MONITOR_DEFAULTTONEAREST);
+ mi.cbSize = sizeof(mi);
+ if (GetMonitorInfo(hMonitor, &mi))
+ rcScreen = mi.rcWork;
+
+ if (rc->top >= rcScreen.bottom)
+ OffsetRect(rc, 0, rcScreen.bottom - rc->bottom);
+ else if (rc->bottom <= rcScreen.top)
+ OffsetRect(rc, 0, rcScreen.top - rc->top);
+ if (rc->left >= rcScreen.right)
+ OffsetRect(rc, rcScreen.right - rc->right, 0);
+ else if (rc->right <= rcScreen.left)
+ OffsetRect(rc, rcScreen.left - rc->left, 0);
+
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static NONCLIENTMETRICSW ncm = {};
+
+MIR_CORE_DLL(int) Utils_CorrectFontSize(int size)
+{
+ if (!g_bEnableDpiAware)
+ return size;
+
+ if (!ncm.cbSize) {
+ ncm.cbSize = sizeof(ncm);
+ SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE);
+ }
+
+ return size * ncm.lfMessageFont.lfHeight / -12;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) Utils_ClipboardCopy(const char *pszText)
+{
+ size_t cbLen = mir_strlen(pszText);
+ if (!cbLen)
+ return;
+
+ if (!OpenClipboard(nullptr))
+ return;
+
+ EmptyClipboard();
+
+ HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cbLen);
+ if (hData) {
+ mir_strcpy((char *)GlobalLock(hData), pszText);
+ GlobalUnlock(hData);
+ SetClipboardData(CF_TEXT, hData);
+ }
+ CloseClipboard();
+}
+
+MIR_CORE_DLL(void) Utils_ClipboardCopy(const wchar_t *pwszText)
+{
+ size_t cbLen = mir_wstrlen(pwszText);
+ if (!cbLen)
+ return;
+
+ if (!OpenClipboard(nullptr))
+ return;
+
+ EmptyClipboard();
+
+ HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (cbLen + 1) * sizeof(wchar_t));
+ if (hData) {
+ mir_wstrcpy((wchar_t *)GlobalLock(hData), pwszText);
+ GlobalUnlock(hData);
+ SetClipboardData(CF_UNICODETEXT, hData);
+ }
+ CloseClipboard();
+}
diff --git a/src/mir_core/src/Windows/winver.cpp b/src/mir_core/src/Windows/winver.cpp index 29a6fa7f3c..f52567ccab 100644 --- a/src/mir_core/src/Windows/winver.cpp +++ b/src/mir_core/src/Windows/winver.cpp @@ -1,372 +1,372 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "../stdafx.h" - -#ifndef _WIN32_WINNT_WIN8 -#define _WIN32_WINNT_WIN8 0x0602 // Windows 8 -#endif - -#ifndef _WIN32_WINNT_WINBLUE -#define _WIN32_WINNT_WINBLUE 0x0603 // Windows 8.1 -#endif - -#ifndef _WIN32_WINNT_WIN10 -#define _WIN32_WINNT_WIN10 0x0A00 // Windows 10 -#endif - -static int dwWinVer; - -void InitWinver() -{ - uint32_t dwVer = LOWORD(GetVersion()); - dwWinVer = MAKEWORD(HIBYTE(dwVer), LOBYTE(dwVer)); -} - -MIR_CORE_DLL(BOOL) IsWinVerVistaPlus() -{ - return dwWinVer >= _WIN32_WINNT_VISTA; -} - -MIR_CORE_DLL(BOOL) IsWinVer7Plus() -{ - return dwWinVer >= _WIN32_WINNT_WIN7; -} - -MIR_CORE_DLL(BOOL) IsWinVer8Plus() -{ - return dwWinVer >= _WIN32_WINNT_WIN8; -} - -MIR_CORE_DLL(BOOL) IsWinVer81Plus() -{ - return dwWinVer >= _WIN32_WINNT_WINBLUE; -} - -MIR_CORE_DLL(BOOL) IsWinVer10Plus() -{ - return dwWinVer >= _WIN32_WINNT_WIN10; -} - -MIR_CORE_DLL(BOOL) IsFullScreen() -{ - RECT rcScreen = { 0 }; - - rcScreen.right = GetSystemMetrics(SM_CXSCREEN); - rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN); - - HMONITOR hMon = MonitorFromWindow(GetForegroundWindow(), MONITOR_DEFAULTTONEAREST); - MONITORINFO mi; - mi.cbSize = sizeof(mi); - if (GetMonitorInfo(hMon, &mi)) - rcScreen = mi.rcMonitor; - - HWND hWndDesktop = GetDesktopWindow(); - HWND hWndShell = GetShellWindow(); - - // check foregroundwindow - HWND hWnd = GetForegroundWindow(); - if (hWnd && hWnd != hWndDesktop && hWnd != hWndShell) { - wchar_t tszClassName[128] = L""; - GetClassName(hWnd, tszClassName, _countof(tszClassName)); - if (wcscmp(tszClassName, L"WorkerW")) { - RECT rect, rectw, recti; - GetWindowRect(hWnd, &rectw); - - GetClientRect(hWnd, &rect); - ClientToScreen(hWnd, (LPPOINT)&rect); - ClientToScreen(hWnd, (LPPOINT)&rect.right); - - if (EqualRect(&rect, &rectw) && IntersectRect(&recti, &rect, &rcScreen) && EqualRect(&recti, &rcScreen)) - return true; - } - } - - return false; -} - -MIR_CORE_DLL(BOOL) IsWorkstationLocked(void) -{ - HDESK hDesk = OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP); - if (hDesk == nullptr) - return true; - - wchar_t tszName[100]; - DWORD cbName; - BOOL bLocked = (!GetUserObjectInformation(hDesk, UOI_NAME, tszName, _countof(tszName), &cbName) || mir_wstrcmpi(tszName, L"default") != 0); - CloseDesktop(hDesk); - return bLocked; -} - -MIR_CORE_DLL(BOOL) IsTerminalDisconnected(void) -{ - PVOID pBuffer = nullptr; - DWORD pBytesReturned = 0; - BOOL result = FALSE; - - if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, (LPTSTR *)&pBuffer, &pBytesReturned)) - if (*(PDWORD)pBuffer == WTSDisconnected) - result = TRUE; - - if (pBuffer) - WTSFreeMemory(pBuffer); - return result; -} - -MIR_CORE_DLL(BOOL) IsScreenSaverRunning(void) -{ - BOOL rc = FALSE; - SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &rc, FALSE); - return rc != 0; -} - -////////////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(BOOL) OS_GetShortString(char *buf, size_t bufSize) -{ - if (buf == nullptr || bufSize == 0) - return false; - - mir_snprintf(buf, bufSize, "Windows NT %d.%d", HIBYTE(dwWinVer), LOBYTE(dwWinVer)); - return true; -} - -////////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef PRODUCT_CORE_N // Win8 -#define PRODUCT_CORE_SINGLELANGUAGE 0x00000064 -#define PRODUCT_PROFESSIONAL_WMC 0x00000067 -#endif - -typedef BOOL(WINAPI *PGPI)(uint32_t, uint32_t, uint32_t, uint32_t, PDWORD); -typedef LPCSTR(WINAPI *WGV)(void); - -MIR_CORE_DLL(BOOL) OS_GetDisplayString(char *buf, size_t bufSize) -{ - if (buf == nullptr || bufSize == 0) - return 0; - - buf[0] = 0; - - OSVERSIONINFOEX osvi = { 0 }; - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - BOOL bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *)&osvi); - if (!bOsVersionInfoEx) { - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - if (!GetVersionEx((OSVERSIONINFO*)&osvi)) - return false; - } - - if (VER_PLATFORM_WIN32_NT != osvi.dwPlatformId || osvi.dwMajorVersion <= 4) - return false; - - SYSTEM_INFO sysInfo = { 0 }; - GetNativeSystemInfo(&sysInfo); - - CMStringA ret("Microsoft "); - - // Test for the specific product. - if (osvi.dwMajorVersion >= 6) { - if (osvi.dwMajorVersion == 10) { - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 10 "); - else - ret.Append("Windows Server 10 "); - } - else switch (osvi.dwMinorVersion) { - case 0: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows Vista "); - else - ret.Append("Windows Server 2008 "); - break; - - case 1: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 7 "); - else - ret.Append("Windows Server 2008 R2 "); - break; - - case 2: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 8 "); - else - ret.Append("Windows Server 2012 "); - break; - - case 3: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 8.1 "); - else - ret.Append("Windows Server 2012 R2 "); - break; - } - - DWORD dwType = 0; - HMODULE hKernel = GetModuleHandle(L"kernel32.dll"); - PGPI pGPI = (PGPI)GetProcAddress(hKernel, "GetProductInfo"); - if (pGPI != nullptr) - pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType); - - switch (dwType) { - case PRODUCT_ULTIMATE: - ret.Append("Ultimate Edition"); - break; - case PRODUCT_PROFESSIONAL: - ret.Append("Professional Edition"); - break; - case PRODUCT_PROFESSIONAL_WMC: - ret.Append("Professional Edition with Media Center"); - break; - case PRODUCT_HOME_PREMIUM: - ret.Append("Home Premium Edition"); - break; - case PRODUCT_HOME_BASIC: - ret.Append("Home Basic Edition"); - break; - case PRODUCT_ENTERPRISE: - ret.Append("Enterprise Edition"); - break; - case PRODUCT_BUSINESS: - ret.Append("Business Edition"); - break; - case PRODUCT_STARTER: - ret.Append("Starter Edition"); - break; - case PRODUCT_CLUSTER_SERVER: - ret.Append("Cluster Server Edition"); - break; - case PRODUCT_DATACENTER_SERVER: - ret.Append("Datacenter Edition"); - break; - case PRODUCT_DATACENTER_SERVER_CORE: - ret.Append("Datacenter Edition (core installation)"); - break; - case PRODUCT_ENTERPRISE_SERVER: - ret.Append("Enterprise Edition"); - break; - case PRODUCT_ENTERPRISE_SERVER_CORE: - ret.Append("Enterprise Edition (core installation)"); - break; - case PRODUCT_ENTERPRISE_SERVER_IA64: - ret.Append("Enterprise Edition for Itanium-based Systems"); - break; - case PRODUCT_SMALLBUSINESS_SERVER: - ret.Append("Small Business Server"); - break; - case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: - ret.Append("Small Business Server Premium Edition"); - break; - case PRODUCT_STANDARD_SERVER: - ret.Append("Standard Edition"); - break; - case PRODUCT_STANDARD_SERVER_CORE: - ret.Append("Standard Edition (core installation)"); - break; - case PRODUCT_WEB_SERVER: - ret.Append("Web Server Edition"); - break; - case PRODUCT_CORE_SINGLELANGUAGE: - ret.Append("Home Single Language"); - break; - } - if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) - ret.Append(", 64-bit"); - else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) - ret.Append(", 32-bit"); - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) { - if (GetSystemMetrics(SM_SERVERR2)) - ret.Append("Windows Server 2003 R2, "); - else if (osvi.wSuiteMask == VER_SUITE_STORAGE_SERVER) - ret.Append("Windows Storage Server 2003"); - else if (osvi.wSuiteMask == VER_SUITE_WH_SERVER) - ret.Append("Windows Home Server"); - else if (osvi.wProductType == VER_NT_WORKSTATION && sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) - ret.Append("Windows XP Professional x64 Edition"); - else - ret.Append("Windows Server 2003, "); - - // Test for the server type. - if (osvi.wProductType != VER_NT_WORKSTATION) { - if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter Edition for Itanium-based Systems"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Enterprise Edition for Itanium-based Systems"); - } - else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter x64 Edition"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Enterprise x64 Edition"); - else ret.Append("Standard x64 Edition"); - } - else { - if (osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER) - ret.Append("Compute Cluster Edition"); - else if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter Edition"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Enterprise Edition"); - else if (osvi.wSuiteMask & VER_SUITE_BLADE) - ret.Append("Web Edition"); - else ret.Append("Standard Edition"); - } - } - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { - ret.Append("Windows XP "); - if (osvi.wSuiteMask & VER_SUITE_PERSONAL) - ret.Append("Home Edition"); - else ret.Append("Professional"); - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) { - ret.Append("Windows 2000 "); - - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Professional"); - else { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter Server"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Advanced Server"); - else ret.Append("Server"); - } - } - - // Include service pack (if any) and build number. - if (mir_wstrlen(osvi.szCSDVersion) > 0) { - ret.Append(" "); - ret.Append(_T2A(osvi.szCSDVersion)); - } - - ret.AppendFormat(" (build %d)", osvi.dwBuildNumber); - - HMODULE hNtDll = GetModuleHandleA("ntdll.dll"); - if (WGV wine_get_version = (WGV)GetProcAddress(hNtDll, "wine_get_version")) - { - ret.AppendFormat(" (Wine %s)", wine_get_version()); - } - - mir_strncpy(buf, ret, bufSize); - return true; -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "../stdafx.h"
+
+#ifndef _WIN32_WINNT_WIN8
+#define _WIN32_WINNT_WIN8 0x0602 // Windows 8
+#endif
+
+#ifndef _WIN32_WINNT_WINBLUE
+#define _WIN32_WINNT_WINBLUE 0x0603 // Windows 8.1
+#endif
+
+#ifndef _WIN32_WINNT_WIN10
+#define _WIN32_WINNT_WIN10 0x0A00 // Windows 10
+#endif
+
+static int dwWinVer;
+
+void InitWinver()
+{
+ uint32_t dwVer = LOWORD(GetVersion());
+ dwWinVer = MAKEWORD(HIBYTE(dwVer), LOBYTE(dwVer));
+}
+
+MIR_CORE_DLL(BOOL) IsWinVerVistaPlus()
+{
+ return dwWinVer >= _WIN32_WINNT_VISTA;
+}
+
+MIR_CORE_DLL(BOOL) IsWinVer7Plus()
+{
+ return dwWinVer >= _WIN32_WINNT_WIN7;
+}
+
+MIR_CORE_DLL(BOOL) IsWinVer8Plus()
+{
+ return dwWinVer >= _WIN32_WINNT_WIN8;
+}
+
+MIR_CORE_DLL(BOOL) IsWinVer81Plus()
+{
+ return dwWinVer >= _WIN32_WINNT_WINBLUE;
+}
+
+MIR_CORE_DLL(BOOL) IsWinVer10Plus()
+{
+ return dwWinVer >= _WIN32_WINNT_WIN10;
+}
+
+MIR_CORE_DLL(BOOL) IsFullScreen()
+{
+ RECT rcScreen = { 0 };
+
+ rcScreen.right = GetSystemMetrics(SM_CXSCREEN);
+ rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN);
+
+ HMONITOR hMon = MonitorFromWindow(GetForegroundWindow(), MONITOR_DEFAULTTONEAREST);
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ if (GetMonitorInfo(hMon, &mi))
+ rcScreen = mi.rcMonitor;
+
+ HWND hWndDesktop = GetDesktopWindow();
+ HWND hWndShell = GetShellWindow();
+
+ // check foregroundwindow
+ HWND hWnd = GetForegroundWindow();
+ if (hWnd && hWnd != hWndDesktop && hWnd != hWndShell) {
+ wchar_t tszClassName[128] = L"";
+ GetClassName(hWnd, tszClassName, _countof(tszClassName));
+ if (wcscmp(tszClassName, L"WorkerW")) {
+ RECT rect, rectw, recti;
+ GetWindowRect(hWnd, &rectw);
+
+ GetClientRect(hWnd, &rect);
+ ClientToScreen(hWnd, (LPPOINT)&rect);
+ ClientToScreen(hWnd, (LPPOINT)&rect.right);
+
+ if (EqualRect(&rect, &rectw) && IntersectRect(&recti, &rect, &rcScreen) && EqualRect(&recti, &rcScreen))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+MIR_CORE_DLL(BOOL) IsWorkstationLocked(void)
+{
+ HDESK hDesk = OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP);
+ if (hDesk == nullptr)
+ return true;
+
+ wchar_t tszName[100];
+ DWORD cbName;
+ BOOL bLocked = (!GetUserObjectInformation(hDesk, UOI_NAME, tszName, _countof(tszName), &cbName) || mir_wstrcmpi(tszName, L"default") != 0);
+ CloseDesktop(hDesk);
+ return bLocked;
+}
+
+MIR_CORE_DLL(BOOL) IsTerminalDisconnected(void)
+{
+ PVOID pBuffer = nullptr;
+ DWORD pBytesReturned = 0;
+ BOOL result = FALSE;
+
+ if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, (LPTSTR *)&pBuffer, &pBytesReturned))
+ if (*(PDWORD)pBuffer == WTSDisconnected)
+ result = TRUE;
+
+ if (pBuffer)
+ WTSFreeMemory(pBuffer);
+ return result;
+}
+
+MIR_CORE_DLL(BOOL) IsScreenSaverRunning(void)
+{
+ BOOL rc = FALSE;
+ SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &rc, FALSE);
+ return rc != 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(BOOL) OS_GetShortString(char *buf, size_t bufSize)
+{
+ if (buf == nullptr || bufSize == 0)
+ return false;
+
+ mir_snprintf(buf, bufSize, "Windows NT %d.%d", HIBYTE(dwWinVer), LOBYTE(dwWinVer));
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef PRODUCT_CORE_N // Win8
+#define PRODUCT_CORE_SINGLELANGUAGE 0x00000064
+#define PRODUCT_PROFESSIONAL_WMC 0x00000067
+#endif
+
+typedef BOOL(WINAPI *PGPI)(uint32_t, uint32_t, uint32_t, uint32_t, PDWORD);
+typedef LPCSTR(WINAPI *WGV)(void);
+
+MIR_CORE_DLL(BOOL) OS_GetDisplayString(char *buf, size_t bufSize)
+{
+ if (buf == nullptr || bufSize == 0)
+ return 0;
+
+ buf[0] = 0;
+
+ OSVERSIONINFOEX osvi = { 0 };
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ BOOL bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *)&osvi);
+ if (!bOsVersionInfoEx) {
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (!GetVersionEx((OSVERSIONINFO*)&osvi))
+ return false;
+ }
+
+ if (VER_PLATFORM_WIN32_NT != osvi.dwPlatformId || osvi.dwMajorVersion <= 4)
+ return false;
+
+ SYSTEM_INFO sysInfo = { 0 };
+ GetNativeSystemInfo(&sysInfo);
+
+ CMStringA ret("Microsoft ");
+
+ // Test for the specific product.
+ if (osvi.dwMajorVersion >= 6) {
+ if (osvi.dwMajorVersion == 10) {
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Windows 10 ");
+ else
+ ret.Append("Windows Server 10 ");
+ }
+ else switch (osvi.dwMinorVersion) {
+ case 0:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Windows Vista ");
+ else
+ ret.Append("Windows Server 2008 ");
+ break;
+
+ case 1:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Windows 7 ");
+ else
+ ret.Append("Windows Server 2008 R2 ");
+ break;
+
+ case 2:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Windows 8 ");
+ else
+ ret.Append("Windows Server 2012 ");
+ break;
+
+ case 3:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Windows 8.1 ");
+ else
+ ret.Append("Windows Server 2012 R2 ");
+ break;
+ }
+
+ DWORD dwType = 0;
+ HMODULE hKernel = GetModuleHandle(L"kernel32.dll");
+ PGPI pGPI = (PGPI)GetProcAddress(hKernel, "GetProductInfo");
+ if (pGPI != nullptr)
+ pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);
+
+ switch (dwType) {
+ case PRODUCT_ULTIMATE:
+ ret.Append("Ultimate Edition");
+ break;
+ case PRODUCT_PROFESSIONAL:
+ ret.Append("Professional Edition");
+ break;
+ case PRODUCT_PROFESSIONAL_WMC:
+ ret.Append("Professional Edition with Media Center");
+ break;
+ case PRODUCT_HOME_PREMIUM:
+ ret.Append("Home Premium Edition");
+ break;
+ case PRODUCT_HOME_BASIC:
+ ret.Append("Home Basic Edition");
+ break;
+ case PRODUCT_ENTERPRISE:
+ ret.Append("Enterprise Edition");
+ break;
+ case PRODUCT_BUSINESS:
+ ret.Append("Business Edition");
+ break;
+ case PRODUCT_STARTER:
+ ret.Append("Starter Edition");
+ break;
+ case PRODUCT_CLUSTER_SERVER:
+ ret.Append("Cluster Server Edition");
+ break;
+ case PRODUCT_DATACENTER_SERVER:
+ ret.Append("Datacenter Edition");
+ break;
+ case PRODUCT_DATACENTER_SERVER_CORE:
+ ret.Append("Datacenter Edition (core installation)");
+ break;
+ case PRODUCT_ENTERPRISE_SERVER:
+ ret.Append("Enterprise Edition");
+ break;
+ case PRODUCT_ENTERPRISE_SERVER_CORE:
+ ret.Append("Enterprise Edition (core installation)");
+ break;
+ case PRODUCT_ENTERPRISE_SERVER_IA64:
+ ret.Append("Enterprise Edition for Itanium-based Systems");
+ break;
+ case PRODUCT_SMALLBUSINESS_SERVER:
+ ret.Append("Small Business Server");
+ break;
+ case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
+ ret.Append("Small Business Server Premium Edition");
+ break;
+ case PRODUCT_STANDARD_SERVER:
+ ret.Append("Standard Edition");
+ break;
+ case PRODUCT_STANDARD_SERVER_CORE:
+ ret.Append("Standard Edition (core installation)");
+ break;
+ case PRODUCT_WEB_SERVER:
+ ret.Append("Web Server Edition");
+ break;
+ case PRODUCT_CORE_SINGLELANGUAGE:
+ ret.Append("Home Single Language");
+ break;
+ }
+ if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ ret.Append(", 64-bit");
+ else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
+ ret.Append(", 32-bit");
+ }
+
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
+ if (GetSystemMetrics(SM_SERVERR2))
+ ret.Append("Windows Server 2003 R2, ");
+ else if (osvi.wSuiteMask == VER_SUITE_STORAGE_SERVER)
+ ret.Append("Windows Storage Server 2003");
+ else if (osvi.wSuiteMask == VER_SUITE_WH_SERVER)
+ ret.Append("Windows Home Server");
+ else if (osvi.wProductType == VER_NT_WORKSTATION && sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ ret.Append("Windows XP Professional x64 Edition");
+ else
+ ret.Append("Windows Server 2003, ");
+
+ // Test for the server type.
+ if (osvi.wProductType != VER_NT_WORKSTATION) {
+ if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) {
+ if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ ret.Append("Datacenter Edition for Itanium-based Systems");
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ ret.Append("Enterprise Edition for Itanium-based Systems");
+ }
+ else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
+ if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ ret.Append("Datacenter x64 Edition");
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ ret.Append("Enterprise x64 Edition");
+ else ret.Append("Standard x64 Edition");
+ }
+ else {
+ if (osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER)
+ ret.Append("Compute Cluster Edition");
+ else if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ ret.Append("Datacenter Edition");
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ ret.Append("Enterprise Edition");
+ else if (osvi.wSuiteMask & VER_SUITE_BLADE)
+ ret.Append("Web Edition");
+ else ret.Append("Standard Edition");
+ }
+ }
+ }
+
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
+ ret.Append("Windows XP ");
+ if (osvi.wSuiteMask & VER_SUITE_PERSONAL)
+ ret.Append("Home Edition");
+ else ret.Append("Professional");
+ }
+
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
+ ret.Append("Windows 2000 ");
+
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ ret.Append("Professional");
+ else {
+ if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+ ret.Append("Datacenter Server");
+ else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+ ret.Append("Advanced Server");
+ else ret.Append("Server");
+ }
+ }
+
+ // Include service pack (if any) and build number.
+ if (mir_wstrlen(osvi.szCSDVersion) > 0) {
+ ret.Append(" ");
+ ret.Append(_T2A(osvi.szCSDVersion));
+ }
+
+ ret.AppendFormat(" (build %d)", osvi.dwBuildNumber);
+
+ HMODULE hNtDll = GetModuleHandleA("ntdll.dll");
+ if (WGV wine_get_version = (WGV)GetProcAddress(hNtDll, "wine_get_version"))
+ {
+ ret.AppendFormat(" (Wine %s)", wine_get_version());
+ }
+
+ mir_strncpy(buf, ret, bufSize);
+ return true;
+}
diff --git a/src/mir_core/src/binbuffer.cpp b/src/mir_core/src/binbuffer.cpp index fbde7bfb4d..77016fbda9 100644 --- a/src/mir_core/src/binbuffer.cpp +++ b/src/mir_core/src/binbuffer.cpp @@ -1,170 +1,170 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "stdafx.h" - -struct BufImpl -{ - uint32_t size, lockCount; - - BufImpl* alloc(size_t newSize) - { - bool bEmpty = (this == nullptr); - auto *res = (BufImpl *)mir_realloc(this, newSize + sizeof(BufImpl)); - if (bEmpty) { - res->lockCount = 1; - res->size = 0; - } - return res; - } - - BufImpl* realloc(size_t newSize) - { - bool bEmpty; - newSize += sizeof(BufImpl); - if (this != nullptr) { - newSize += size; - bEmpty = false; - } - else bEmpty = true; - - auto *res = (BufImpl *)mir_realloc(this, newSize); - if (bEmpty) { - res->lockCount = 1; - res->size = 0; - } - return res; - } - - void free() - { - if (this == nullptr) - return; - - if (lockCount == 1) - mir_free(this); - else - lockCount--; - } -}; - -__forceinline BufImpl* ptr2buf(uint8_t *p) -{ - return (p == nullptr) ? nullptr : (BufImpl*)p-1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MBinBuffer::MBinBuffer() -{} - -MBinBuffer::MBinBuffer(const MBinBuffer &orig) -{ - ptr2buf(m_buf)->free(); - - BufImpl *p = ptr2buf(m_buf = orig.m_buf); - if (p) - p->lockCount++; -} - -MBinBuffer::MBinBuffer(size_t preAlloc) -{ - BufImpl *p = (BufImpl *)mir_alloc(sizeof(BufImpl) + preAlloc); - p->lockCount = 1; - p->size = (unsigned)preAlloc; - m_buf = (uint8_t *)(p + 1); -} - -MBinBuffer& MBinBuffer::operator=(MBinBuffer &&from) noexcept -{ - m_buf = from.m_buf; - from.m_buf = nullptr; - return *this; -} - -MBinBuffer::~MBinBuffer() -{ - ptr2buf(m_buf)->free(); -} - -void MBinBuffer::append(const void *pBuf, size_t bufLen) -{ - if (pBuf == nullptr || bufLen == 0) - return; - - BufImpl *p = ptr2buf(m_buf)->realloc(bufLen); - if (p) { - m_buf = (uint8_t *)(p + 1); - memcpy(m_buf + p->size, pBuf, bufLen); - p->size += (unsigned)bufLen; - } - else m_buf = nullptr; -} - -void MBinBuffer::appendBefore(const void *pBuf, size_t bufLen) -{ - if (pBuf == nullptr || bufLen == 0) - return; - - BufImpl *p = ptr2buf(m_buf)->realloc(bufLen); - if (p) { - m_buf = (uint8_t *)(p + 1); - memmove(m_buf + bufLen, m_buf, p->size); - memcpy(m_buf, pBuf, bufLen); - p->size += (unsigned)bufLen; - } - else m_buf = nullptr; -} - -void MBinBuffer::assign(const void *pBuf, size_t bufLen) -{ - if (pBuf == nullptr || bufLen == 0) - return; - - BufImpl *p = ptr2buf(m_buf)->alloc(bufLen); - if (p) { - p->size = (unsigned)bufLen; - m_buf = (uint8_t *)(p + 1); - memcpy(m_buf, pBuf, bufLen); - } - else m_buf = nullptr; -} - -size_t MBinBuffer::length() const -{ - BufImpl *p = ptr2buf(m_buf); - return (p) ? p->size : 0; -} - -void MBinBuffer::remove(size_t sz) -{ - BufImpl *p = ptr2buf(m_buf); - if (!p) - return; - - if (sz > p->size) - sz = p->size; - - if (p->size == sz) { - p->free(); - m_buf = nullptr; - } - else { - memmove(m_buf, m_buf + sz, p->size - sz); - p->size -= (unsigned)sz; - } -} +/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+struct BufImpl
+{
+ uint32_t size, lockCount;
+
+ BufImpl* alloc(size_t newSize)
+ {
+ bool bEmpty = (this == nullptr);
+ auto *res = (BufImpl *)mir_realloc(this, newSize + sizeof(BufImpl));
+ if (bEmpty) {
+ res->lockCount = 1;
+ res->size = 0;
+ }
+ return res;
+ }
+
+ BufImpl* realloc(size_t newSize)
+ {
+ bool bEmpty;
+ newSize += sizeof(BufImpl);
+ if (this != nullptr) {
+ newSize += size;
+ bEmpty = false;
+ }
+ else bEmpty = true;
+
+ auto *res = (BufImpl *)mir_realloc(this, newSize);
+ if (bEmpty) {
+ res->lockCount = 1;
+ res->size = 0;
+ }
+ return res;
+ }
+
+ void free()
+ {
+ if (this == nullptr)
+ return;
+
+ if (lockCount == 1)
+ mir_free(this);
+ else
+ lockCount--;
+ }
+};
+
+__forceinline BufImpl* ptr2buf(uint8_t *p)
+{
+ return (p == nullptr) ? nullptr : (BufImpl*)p-1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MBinBuffer::MBinBuffer()
+{}
+
+MBinBuffer::MBinBuffer(const MBinBuffer &orig)
+{
+ ptr2buf(m_buf)->free();
+
+ BufImpl *p = ptr2buf(m_buf = orig.m_buf);
+ if (p)
+ p->lockCount++;
+}
+
+MBinBuffer::MBinBuffer(size_t preAlloc)
+{
+ BufImpl *p = (BufImpl *)mir_alloc(sizeof(BufImpl) + preAlloc);
+ p->lockCount = 1;
+ p->size = (unsigned)preAlloc;
+ m_buf = (uint8_t *)(p + 1);
+}
+
+MBinBuffer& MBinBuffer::operator=(MBinBuffer &&from) noexcept
+{
+ m_buf = from.m_buf;
+ from.m_buf = nullptr;
+ return *this;
+}
+
+MBinBuffer::~MBinBuffer()
+{
+ ptr2buf(m_buf)->free();
+}
+
+void MBinBuffer::append(const void *pBuf, size_t bufLen)
+{
+ if (pBuf == nullptr || bufLen == 0)
+ return;
+
+ BufImpl *p = ptr2buf(m_buf)->realloc(bufLen);
+ if (p) {
+ m_buf = (uint8_t *)(p + 1);
+ memcpy(m_buf + p->size, pBuf, bufLen);
+ p->size += (unsigned)bufLen;
+ }
+ else m_buf = nullptr;
+}
+
+void MBinBuffer::appendBefore(const void *pBuf, size_t bufLen)
+{
+ if (pBuf == nullptr || bufLen == 0)
+ return;
+
+ BufImpl *p = ptr2buf(m_buf)->realloc(bufLen);
+ if (p) {
+ m_buf = (uint8_t *)(p + 1);
+ memmove(m_buf + bufLen, m_buf, p->size);
+ memcpy(m_buf, pBuf, bufLen);
+ p->size += (unsigned)bufLen;
+ }
+ else m_buf = nullptr;
+}
+
+void MBinBuffer::assign(const void *pBuf, size_t bufLen)
+{
+ if (pBuf == nullptr || bufLen == 0)
+ return;
+
+ BufImpl *p = ptr2buf(m_buf)->alloc(bufLen);
+ if (p) {
+ p->size = (unsigned)bufLen;
+ m_buf = (uint8_t *)(p + 1);
+ memcpy(m_buf, pBuf, bufLen);
+ }
+ else m_buf = nullptr;
+}
+
+size_t MBinBuffer::length() const
+{
+ BufImpl *p = ptr2buf(m_buf);
+ return (p) ? p->size : 0;
+}
+
+void MBinBuffer::remove(size_t sz)
+{
+ BufImpl *p = ptr2buf(m_buf);
+ if (!p)
+ return;
+
+ if (sz > p->size)
+ sz = p->size;
+
+ if (p->size == sz) {
+ p->free();
+ m_buf = nullptr;
+ }
+ else {
+ memmove(m_buf, m_buf + sz, p->size - sz);
+ p->size -= (unsigned)sz;
+ }
+}
diff --git a/src/mir_core/src/bitmaps.cpp b/src/mir_core/src/bitmaps.cpp index eb1a8b02d2..5ab665df84 100644 --- a/src/mir_core/src/bitmaps.cpp +++ b/src/mir_core/src/bitmaps.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team,
+Copyright (C) 2012-23 Miranda NG team,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_core/src/db.cpp b/src/mir_core/src/db.cpp index 8fb13fd4d3..362d359f17 100644 --- a/src/mir_core/src/db.cpp +++ b/src/mir_core/src/db.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_core/src/http.cpp b/src/mir_core/src/http.cpp index e983676dd0..879df3b038 100644 --- a/src/mir_core/src/http.cpp +++ b/src/mir_core/src/http.cpp @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/mir_core/src/lists.cpp b/src/mir_core/src/lists.cpp index 21d890a148..c5b1b36825 100644 --- a/src/mir_core/src/lists.cpp +++ b/src/mir_core/src/lists.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/mir_core/src/logger.cpp b/src/mir_core/src/logger.cpp index 24240d691d..83218f88ed 100644 --- a/src/mir_core/src/logger.cpp +++ b/src/mir_core/src/logger.cpp @@ -1,203 +1,203 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define SECRET_SIGNATURE 0x87654321 - -struct Logger -{ - Logger(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFilename, unsigned options) : - m_name(mir_strdup(pszName)), - m_descr(mir_wstrdup(ptszDescr)), - m_fileName(mir_wstrdup(ptszFilename)), - m_options(options), - m_signature(SECRET_SIGNATURE), - m_out(nullptr), - m_lastwrite(0) - { - } - - ~Logger() - { - if (m_out) - fclose(m_out); - } - - int m_signature; - ptrA m_name; - ptrW m_fileName, m_descr; - FILE *m_out; - time_t m_lastwrite; - unsigned m_options; - mir_cs m_cs; -}; - -static int CompareLoggers(const Logger *p1, const Logger *p2) -{ return strcmp(p1->m_name, p2->m_name); -} - -static OBJLIST<Logger> arLoggers(1, CompareLoggers); - -void InitLogs() -{ -} - -void UninitLogs() -{ - arLoggers.destroy(); -} - -void CheckLogs() -{ - time_t tm = time(0); - - for (auto &p : arLoggers) { - mir_cslock lck(p->m_cs); - if (p->m_out && tm - p->m_lastwrite > 5) { - fclose(p->m_out); - p->m_out = nullptr; - } - else fflush(p->m_out); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(HANDLE) mir_createLog(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFile, unsigned options) -{ - if (ptszFile == nullptr) - return nullptr; - - Logger *result = new Logger(pszName, ptszDescr, ptszFile, options); - if (result == nullptr) - return nullptr; - - int idx = arLoggers.getIndex(result); - if (idx != -1) { - delete result; - return &arLoggers[idx]; - } - - CreatePathToFileW(ptszFile); - _wremove(ptszFile); - arLoggers.insert(result); - return result; -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -static Logger* prepareLogger(HANDLE hLogger) -{ - if (hLogger == nullptr) - return nullptr; - - Logger *p = (Logger*)hLogger; - return (p->m_signature == SECRET_SIGNATURE) ? p : nullptr; -} - -MIR_CORE_DLL(void) mir_closeLog(HANDLE hLogger) -{ - Logger *p = prepareLogger(hLogger); - if (p != nullptr) - arLoggers.remove(p); -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_C_CORE_DLL(int) mir_writeLogA(HANDLE hLogger, const char *format, ...) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - va_list args; - va_start(args, format); - vfprintf(p->m_out, format, args); - va_end(args); - - p->m_lastwrite = time(0); - return 0; -} - -MIR_C_CORE_DLL(int) mir_writeLogW(HANDLE hLogger, const wchar_t *format, ...) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - va_list args; - va_start(args, format); - vfwprintf(p->m_out, format, args); - va_end(args); - - p->m_lastwrite = time(0); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) mir_writeLogVA(HANDLE hLogger, const char *format, va_list args) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - vfprintf(p->m_out, format, args); - - p->m_lastwrite = time(0); - return 0; -} - -MIR_CORE_DLL(int) mir_writeLogVW(HANDLE hLogger, const wchar_t *format, va_list args) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - vfwprintf(p->m_out, format, args); - - p->m_lastwrite = time(0); - return 0; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#define SECRET_SIGNATURE 0x87654321
+
+struct Logger
+{
+ Logger(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFilename, unsigned options) :
+ m_name(mir_strdup(pszName)),
+ m_descr(mir_wstrdup(ptszDescr)),
+ m_fileName(mir_wstrdup(ptszFilename)),
+ m_options(options),
+ m_signature(SECRET_SIGNATURE),
+ m_out(nullptr),
+ m_lastwrite(0)
+ {
+ }
+
+ ~Logger()
+ {
+ if (m_out)
+ fclose(m_out);
+ }
+
+ int m_signature;
+ ptrA m_name;
+ ptrW m_fileName, m_descr;
+ FILE *m_out;
+ time_t m_lastwrite;
+ unsigned m_options;
+ mir_cs m_cs;
+};
+
+static int CompareLoggers(const Logger *p1, const Logger *p2)
+{ return strcmp(p1->m_name, p2->m_name);
+}
+
+static OBJLIST<Logger> arLoggers(1, CompareLoggers);
+
+void InitLogs()
+{
+}
+
+void UninitLogs()
+{
+ arLoggers.destroy();
+}
+
+void CheckLogs()
+{
+ time_t tm = time(0);
+
+ for (auto &p : arLoggers) {
+ mir_cslock lck(p->m_cs);
+ if (p->m_out && tm - p->m_lastwrite > 5) {
+ fclose(p->m_out);
+ p->m_out = nullptr;
+ }
+ else fflush(p->m_out);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(HANDLE) mir_createLog(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFile, unsigned options)
+{
+ if (ptszFile == nullptr)
+ return nullptr;
+
+ Logger *result = new Logger(pszName, ptszDescr, ptszFile, options);
+ if (result == nullptr)
+ return nullptr;
+
+ int idx = arLoggers.getIndex(result);
+ if (idx != -1) {
+ delete result;
+ return &arLoggers[idx];
+ }
+
+ CreatePathToFileW(ptszFile);
+ _wremove(ptszFile);
+ arLoggers.insert(result);
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+static Logger* prepareLogger(HANDLE hLogger)
+{
+ if (hLogger == nullptr)
+ return nullptr;
+
+ Logger *p = (Logger*)hLogger;
+ return (p->m_signature == SECRET_SIGNATURE) ? p : nullptr;
+}
+
+MIR_CORE_DLL(void) mir_closeLog(HANDLE hLogger)
+{
+ Logger *p = prepareLogger(hLogger);
+ if (p != nullptr)
+ arLoggers.remove(p);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_C_CORE_DLL(int) mir_writeLogA(HANDLE hLogger, const char *format, ...)
+{
+ Logger *p = prepareLogger(hLogger);
+ if (p == nullptr)
+ return 1;
+
+ mir_cslock lck(p->m_cs);
+ if (p->m_out == nullptr)
+ if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr)
+ return 2;
+
+ va_list args;
+ va_start(args, format);
+ vfprintf(p->m_out, format, args);
+ va_end(args);
+
+ p->m_lastwrite = time(0);
+ return 0;
+}
+
+MIR_C_CORE_DLL(int) mir_writeLogW(HANDLE hLogger, const wchar_t *format, ...)
+{
+ Logger *p = prepareLogger(hLogger);
+ if (p == nullptr)
+ return 1;
+
+ mir_cslock lck(p->m_cs);
+ if (p->m_out == nullptr)
+ if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr)
+ return 2;
+
+ va_list args;
+ va_start(args, format);
+ vfwprintf(p->m_out, format, args);
+ va_end(args);
+
+ p->m_lastwrite = time(0);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) mir_writeLogVA(HANDLE hLogger, const char *format, va_list args)
+{
+ Logger *p = prepareLogger(hLogger);
+ if (p == nullptr)
+ return 1;
+
+ mir_cslock lck(p->m_cs);
+ if (p->m_out == nullptr)
+ if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr)
+ return 2;
+
+ vfprintf(p->m_out, format, args);
+
+ p->m_lastwrite = time(0);
+ return 0;
+}
+
+MIR_CORE_DLL(int) mir_writeLogVW(HANDLE hLogger, const wchar_t *format, va_list args)
+{
+ Logger *p = prepareLogger(hLogger);
+ if (p == nullptr)
+ return 1;
+
+ mir_cslock lck(p->m_cs);
+ if (p->m_out == nullptr)
+ if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr)
+ return 2;
+
+ vfwprintf(p->m_out, format, args);
+
+ p->m_lastwrite = time(0);
+ return 0;
+}
diff --git a/src/mir_core/src/memory.cpp b/src/mir_core/src/memory.cpp index 8cf95b81ab..5183310b48 100644 --- a/src/mir_core/src/memory.cpp +++ b/src/mir_core/src/memory.cpp @@ -1,295 +1,295 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define BLOCK_ALLOCED 0xABBABABA -#define BLOCK_FREED 0xDEADBEEF - -static int CheckBlock(void* blk) -{ - int result = FALSE; - char* p = (char*)blk - sizeof(uint32_t)*2; - uint32_t size, *b, *e; - - __try - { - size = *(uint32_t*)p; - b = (uint32_t*)&p[ sizeof(uint32_t) ]; - e = (uint32_t*)&p[ sizeof(uint32_t)*2 + size ]; - - if (*b != BLOCK_ALLOCED || *e != BLOCK_ALLOCED) - { - #ifdef _MSC_VER - if (*b == BLOCK_FREED && *e == BLOCK_FREED) - OutputDebugStringA("memory block is already deleted\n"); - else - OutputDebugStringA("memory block is corrupted\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - } - else result = TRUE; - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - #ifdef _MSC_VER - OutputDebugStringA("access violation during checking memory block\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - } - - return result; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void*) mir_alloc(size_t size) -{ - if (size == 0) - return nullptr; - - char *p = (char*)malloc(size + sizeof(uint32_t)* 3); - if (p == nullptr) { - #ifdef _MSC_VER - OutputDebugStringA("memory overflow\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - return nullptr; - } - - *(uint32_t*)p = (uint32_t)size; - *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED; - *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED; - return p + sizeof(uint32_t)* 2; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void*) mir_calloc(size_t size) -{ - void* p = mir_alloc(size); - if (p != nullptr) - memset(p, 0, size); - return p; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void*) mir_realloc(void* ptr, size_t size) -{ - char *p; - - if (ptr != nullptr) { - if (!CheckBlock(ptr)) - return nullptr; - p = (char*)ptr - sizeof(uint32_t)*2; - } - else p = nullptr; - - p = (char*)realloc(p, size + sizeof(uint32_t)*3); - if (p == nullptr) { - #ifdef _MSC_VER - OutputDebugStringA("memory overflow\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - return nullptr; - } - - *(uint32_t*)p = (uint32_t)size; - *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED; - *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED; - return p + sizeof(uint32_t)*2; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void) mir_free(void* ptr) -{ - char* p; - uint32_t size; - - if (ptr == nullptr) - return; - if (!CheckBlock(ptr)) - return; - - p = (char*)ptr - sizeof(uint32_t)*2; - size = *(uint32_t*)p; - - *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_FREED; - *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_FREED; - free(p); -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_strdup(const char *str) -{ - if (str == nullptr) - return nullptr; - - char *p = (char*)mir_alloc(strlen(str)+1); - if (p) - strcpy(p, str); - return p; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrdup(const wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(wcslen(str)+1)); - if (p) - wcscpy(p, str); - return p; -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_strndup(const char *str, size_t len) -{ - if (str == nullptr || len == 0) - return nullptr; - - char *p = (char*)mir_alloc(len+1); - if (p) { - memcpy(p, str, len); - p[len] = 0; - } - return p; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrndup(const wchar_t *str, size_t len) -{ - if (str == nullptr || len == 0) - return nullptr; - - wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(len+1)); - if (p) { - memcpy(p, str, sizeof(wchar_t)*len); - p[len] = 0; - } - return p; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_snprintf(char *buffer, size_t count, const char* fmt, ...) -{ - va_list va; - va_start(va, fmt); - int len = _vsnprintf(buffer, count-1, fmt, va); - va_end(va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_snwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, ...) -{ - va_list va; - va_start(va, fmt); - int len = _vsnwprintf(buffer, count-1, fmt, va); - va_end(va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_vsnprintf(char *buffer, size_t count, const char* fmt, va_list va) -{ - int len = _vsnprintf(buffer, count-1, fmt, va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_vsnwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, va_list va) -{ - int len = _vsnwprintf(buffer, count-1, fmt, va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -#ifdef _MSC_VER -MIR_CORE_DLL(wchar_t*) mir_a2u_cp(const char* src, int codepage) -{ - if (src == nullptr) - return nullptr; - - int cbLen = MultiByteToWideChar(codepage, 0, src, -1, nullptr, 0); - wchar_t* result = (wchar_t*)mir_alloc(sizeof(wchar_t)*(cbLen+1)); - if (result == nullptr) - return nullptr; - - MultiByteToWideChar(codepage, 0, src, -1, result, cbLen); - result[cbLen] = 0; - return result; -} - -/******************************************************************************/ - -MIR_CORE_DLL(wchar_t*) mir_a2u(const char* src) -{ - return mir_a2u_cp(src, Langpack_GetDefaultCodePage()); -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_u2a_cp(const wchar_t* src, int codepage) -{ - if (src == nullptr) - return nullptr; - - int cbLen = WideCharToMultiByte(codepage, 0, src, -1, nullptr, 0, nullptr, nullptr); - char* result = (char*)mir_alloc(cbLen+1); - if (result == nullptr) - return nullptr; - - WideCharToMultiByte(codepage, 0, src, -1, result, cbLen, nullptr, nullptr); - result[cbLen] = 0; - return result; -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_u2a(const wchar_t* src) -{ - return mir_u2a_cp(src, Langpack_GetDefaultCodePage()); -} -#endif +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+#define BLOCK_ALLOCED 0xABBABABA
+#define BLOCK_FREED 0xDEADBEEF
+
+static int CheckBlock(void* blk)
+{
+ int result = FALSE;
+ char* p = (char*)blk - sizeof(uint32_t)*2;
+ uint32_t size, *b, *e;
+
+ __try
+ {
+ size = *(uint32_t*)p;
+ b = (uint32_t*)&p[ sizeof(uint32_t) ];
+ e = (uint32_t*)&p[ sizeof(uint32_t)*2 + size ];
+
+ if (*b != BLOCK_ALLOCED || *e != BLOCK_ALLOCED)
+ {
+ #ifdef _MSC_VER
+ if (*b == BLOCK_FREED && *e == BLOCK_FREED)
+ OutputDebugStringA("memory block is already deleted\n");
+ else
+ OutputDebugStringA("memory block is corrupted\n");
+ #if defined(_DEBUG)
+ DebugBreak();
+ #endif
+ #endif
+ }
+ else result = TRUE;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ #ifdef _MSC_VER
+ OutputDebugStringA("access violation during checking memory block\n");
+ #if defined(_DEBUG)
+ DebugBreak();
+ #endif
+ #endif
+ }
+
+ return result;
+}
+
+/******************************************************************************/
+
+MIR_C_CORE_DLL(void*) mir_alloc(size_t size)
+{
+ if (size == 0)
+ return nullptr;
+
+ char *p = (char*)malloc(size + sizeof(uint32_t)* 3);
+ if (p == nullptr) {
+ #ifdef _MSC_VER
+ OutputDebugStringA("memory overflow\n");
+ #if defined(_DEBUG)
+ DebugBreak();
+ #endif
+ #endif
+ return nullptr;
+ }
+
+ *(uint32_t*)p = (uint32_t)size;
+ *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED;
+ *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED;
+ return p + sizeof(uint32_t)* 2;
+}
+
+/******************************************************************************/
+
+MIR_C_CORE_DLL(void*) mir_calloc(size_t size)
+{
+ void* p = mir_alloc(size);
+ if (p != nullptr)
+ memset(p, 0, size);
+ return p;
+}
+
+/******************************************************************************/
+
+MIR_C_CORE_DLL(void*) mir_realloc(void* ptr, size_t size)
+{
+ char *p;
+
+ if (ptr != nullptr) {
+ if (!CheckBlock(ptr))
+ return nullptr;
+ p = (char*)ptr - sizeof(uint32_t)*2;
+ }
+ else p = nullptr;
+
+ p = (char*)realloc(p, size + sizeof(uint32_t)*3);
+ if (p == nullptr) {
+ #ifdef _MSC_VER
+ OutputDebugStringA("memory overflow\n");
+ #if defined(_DEBUG)
+ DebugBreak();
+ #endif
+ #endif
+ return nullptr;
+ }
+
+ *(uint32_t*)p = (uint32_t)size;
+ *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED;
+ *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED;
+ return p + sizeof(uint32_t)*2;
+}
+
+/******************************************************************************/
+
+MIR_C_CORE_DLL(void) mir_free(void* ptr)
+{
+ char* p;
+ uint32_t size;
+
+ if (ptr == nullptr)
+ return;
+ if (!CheckBlock(ptr))
+ return;
+
+ p = (char*)ptr - sizeof(uint32_t)*2;
+ size = *(uint32_t*)p;
+
+ *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_FREED;
+ *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_FREED;
+ free(p);
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(char*) mir_strdup(const char *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ char *p = (char*)mir_alloc(strlen(str)+1);
+ if (p)
+ strcpy(p, str);
+ return p;
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrdup(const wchar_t *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(wcslen(str)+1));
+ if (p)
+ wcscpy(p, str);
+ return p;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(char*) mir_strndup(const char *str, size_t len)
+{
+ if (str == nullptr || len == 0)
+ return nullptr;
+
+ char *p = (char*)mir_alloc(len+1);
+ if (p) {
+ memcpy(p, str, len);
+ p[len] = 0;
+ }
+ return p;
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrndup(const wchar_t *str, size_t len)
+{
+ if (str == nullptr || len == 0)
+ return nullptr;
+
+ wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(len+1));
+ if (p) {
+ memcpy(p, str, sizeof(wchar_t)*len);
+ p[len] = 0;
+ }
+ return p;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(int) mir_snprintf(char *buffer, size_t count, const char* fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ int len = _vsnprintf(buffer, count-1, fmt, va);
+ va_end(va);
+ buffer[count-1] = 0;
+ return len;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(int) mir_snwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ int len = _vsnwprintf(buffer, count-1, fmt, va);
+ va_end(va);
+ buffer[count-1] = 0;
+ return len;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(int) mir_vsnprintf(char *buffer, size_t count, const char* fmt, va_list va)
+{
+ int len = _vsnprintf(buffer, count-1, fmt, va);
+ buffer[count-1] = 0;
+ return len;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(int) mir_vsnwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, va_list va)
+{
+ int len = _vsnwprintf(buffer, count-1, fmt, va);
+ buffer[count-1] = 0;
+ return len;
+}
+
+/******************************************************************************/
+
+#ifdef _MSC_VER
+MIR_CORE_DLL(wchar_t*) mir_a2u_cp(const char* src, int codepage)
+{
+ if (src == nullptr)
+ return nullptr;
+
+ int cbLen = MultiByteToWideChar(codepage, 0, src, -1, nullptr, 0);
+ wchar_t* result = (wchar_t*)mir_alloc(sizeof(wchar_t)*(cbLen+1));
+ if (result == nullptr)
+ return nullptr;
+
+ MultiByteToWideChar(codepage, 0, src, -1, result, cbLen);
+ result[cbLen] = 0;
+ return result;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(wchar_t*) mir_a2u(const char* src)
+{
+ return mir_a2u_cp(src, Langpack_GetDefaultCodePage());
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(char*) mir_u2a_cp(const wchar_t* src, int codepage)
+{
+ if (src == nullptr)
+ return nullptr;
+
+ int cbLen = WideCharToMultiByte(codepage, 0, src, -1, nullptr, 0, nullptr, nullptr);
+ char* result = (char*)mir_alloc(cbLen+1);
+ if (result == nullptr)
+ return nullptr;
+
+ WideCharToMultiByte(codepage, 0, src, -1, result, cbLen, nullptr, nullptr);
+ result[cbLen] = 0;
+ return result;
+}
+
+/******************************************************************************/
+
+MIR_CORE_DLL(char*) mir_u2a(const wchar_t* src)
+{
+ return mir_u2a_cp(src, Langpack_GetDefaultCodePage());
+}
+#endif
diff --git a/src/mir_core/src/miranda.h b/src/mir_core/src/miranda.h index 7264f27444..2b725888f2 100644 --- a/src/mir_core/src/miranda.h +++ b/src/mir_core/src/miranda.h @@ -1,101 +1,101 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -void UnloadLangPackModule(void); - -int InitialiseModularEngine(void); -void DestroyModularEngine(void); - -int InitPathUtils(void); - -extern HINSTANCE g_hInst; -extern HWND hAPCWindow; -extern HANDLE hThreadQueueEmpty; -extern HCURSOR g_hCursorNS, g_hCursorWE; -extern bool g_bEnableDpiAware; - -///////////////////////////////////////////////////////////////////////////////////////// -// modules.cpp - -struct THookSubscriber -{ - HINSTANCE hOwner; - int type; - union { - struct { - union { - MIRANDAHOOK pfnHook; - MIRANDAHOOKPARAM pfnHookParam; - MIRANDAHOOKOBJ pfnHookObj; - MIRANDAHOOKOBJPARAM pfnHookObjParam; - }; - void* object; - LPARAM lParam; - }; - struct { - HWND hwnd; - UINT message; - }; - }; -}; - -#define HOOK_SECRET_SIGNATURE 0xDEADBABA - -struct THook : public MZeroedObject -{ - char name[MAXMODULELABELLENGTH]; - int id; - int subscriberCount; - THookSubscriber* subscriber; - MIRANDAHOOK pfnHook; - uint32_t secretSignature = HOOK_SECRET_SIGNATURE; - mir_cs csHook; -}; - -extern LIST<CMPluginBase> pluginListAddr; - -///////////////////////////////////////////////////////////////////////////////////////// -// langpack.cpp - -char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W); - -///////////////////////////////////////////////////////////////////////////////////////// -// miranda.cpp - -EXTERN_C MIR_CORE_DLL(void) BeginMessageLoop(void); -EXTERN_C MIR_CORE_DLL(void) EnterMessageLoop(void); -EXTERN_C MIR_CORE_DLL(void) LeaveMessageLoop(void); - -///////////////////////////////////////////////////////////////////////////////////////// -// threads.cpp - -extern uint32_t mir_tls; - -///////////////////////////////////////////////////////////////////////////////////////// -// utils.cpp - -typedef BOOL(MIR_SYSCALL *PGENRANDOM)(void*, uint32_t); -extern PGENRANDOM pfnRtlGenRandom; +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#pragma once
+
+void UnloadLangPackModule(void);
+
+int InitialiseModularEngine(void);
+void DestroyModularEngine(void);
+
+int InitPathUtils(void);
+
+extern HINSTANCE g_hInst;
+extern HWND hAPCWindow;
+extern HANDLE hThreadQueueEmpty;
+extern HCURSOR g_hCursorNS, g_hCursorWE;
+extern bool g_bEnableDpiAware;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// modules.cpp
+
+struct THookSubscriber
+{
+ HINSTANCE hOwner;
+ int type;
+ union {
+ struct {
+ union {
+ MIRANDAHOOK pfnHook;
+ MIRANDAHOOKPARAM pfnHookParam;
+ MIRANDAHOOKOBJ pfnHookObj;
+ MIRANDAHOOKOBJPARAM pfnHookObjParam;
+ };
+ void* object;
+ LPARAM lParam;
+ };
+ struct {
+ HWND hwnd;
+ UINT message;
+ };
+ };
+};
+
+#define HOOK_SECRET_SIGNATURE 0xDEADBABA
+
+struct THook : public MZeroedObject
+{
+ char name[MAXMODULELABELLENGTH];
+ int id;
+ int subscriberCount;
+ THookSubscriber* subscriber;
+ MIRANDAHOOK pfnHook;
+ uint32_t secretSignature = HOOK_SECRET_SIGNATURE;
+ mir_cs csHook;
+};
+
+extern LIST<CMPluginBase> pluginListAddr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// langpack.cpp
+
+char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// miranda.cpp
+
+EXTERN_C MIR_CORE_DLL(void) BeginMessageLoop(void);
+EXTERN_C MIR_CORE_DLL(void) EnterMessageLoop(void);
+EXTERN_C MIR_CORE_DLL(void) LeaveMessageLoop(void);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// threads.cpp
+
+extern uint32_t mir_tls;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// utils.cpp
+
+typedef BOOL(MIR_SYSCALL *PGENRANDOM)(void*, uint32_t);
+extern PGENRANDOM pfnRtlGenRandom;
diff --git a/src/mir_core/src/modules.cpp b/src/mir_core/src/modules.cpp index 1636d7449e..0d471c8c6c 100644 --- a/src/mir_core/src/modules.cpp +++ b/src/mir_core/src/modules.cpp @@ -1,704 +1,704 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -// list of hooks - -static int compareHooks(const THook* p1, const THook* p2) -{ - return strcmp(p1->name, p2->name); -} - -static LIST<THook> hooks(50, compareHooks); - -struct THookToMainThreadItem -{ - THook* hook; - HANDLE hDoneEvent; - WPARAM wParam; - LPARAM lParam; - int result; -}; - -// list of services - -struct TService -{ - uint32_t nameHash; - HINSTANCE hOwner; - union - { - MIRANDASERVICE pfnService; - MIRANDASERVICEPARAM pfnServiceParam; - MIRANDASERVICEOBJ pfnServiceObj; - MIRANDASERVICEOBJPARAM pfnServiceObjParam; - }; - int flags; - LPARAM lParam; - void* object; - char name[1]; -}; - -LIST<TService> services(100, NumericKeySortT); - -struct TServiceToMainThreadItem -{ - HANDLE hDoneEvent; - WPARAM wParam; - LPARAM lParam; - int result; - const char *name; -}; - -// other static variables -static BOOL bServiceMode = FALSE; -static mir_cs csHooks, csServices; -static uint32_t mainThreadId; -static int sttHookId = 1; - -///////////////////////////////////////////////////////////////////////////////////////// - -__forceinline HANDLE getThreadEvent() -{ - HANDLE pData = (HANDLE)TlsGetValue(mir_tls); - if (pData == nullptr) { - pData = CreateEvent(nullptr, FALSE, FALSE, nullptr); - TlsSetValue(mir_tls, pData); - } - return pData; -} - -static int QueueMainThread(PAPCFUNC pFunc, void* pParam, HANDLE hDoneEvent) -{ - int result = PostMessage(hAPCWindow, WM_USER + 1, (WPARAM)pFunc, (LPARAM)pParam); // let this get processed in its own time - if (hDoneEvent) - WaitForSingleObject(hDoneEvent, INFINITE); - - return result; -} - -/////////////////////////////////////////////////////////////////////////////// -// HOOKS - -MIR_CORE_DLL(HANDLE) CreateHookableEvent(const char *name) -{ - if (name == nullptr) - return nullptr; - - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) != -1) - return hooks[idx]; - - THook *newItem = new THook(); - strncpy(newItem->name, name, sizeof(newItem->name)); newItem->name[MAXMODULELABELLENGTH - 1] = 0; - newItem->id = sttHookId++; - hooks.insert(newItem); - return (HANDLE)newItem; -} - -MIR_CORE_DLL(int) DestroyHookableEvent(HANDLE hEvent) -{ - if (hEvent == nullptr) - return 1; - - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)hEvent)) == -1) - return 1; - - THook *p = hooks[idx]; - p->secretSignature = 0; - if (p->subscriberCount) { - mir_free(p->subscriber); - p->subscriber = nullptr; - p->subscriberCount = 0; - } - hooks.remove(idx); - delete p; - return 0; -} - -MIR_CORE_DLL(int) SetHookDefaultForHookableEvent(HANDLE hEvent, MIRANDAHOOK pfnHook) -{ - THook *p = (THook*)hEvent; - - mir_cslock lck(csHooks); - if (hooks.getIndex(p) != -1) - p->pfnHook = pfnHook; - return 0; -} - -MIR_CORE_DLL(int) CallPluginEventHook(HINSTANCE hInst, const char *pszEvent, WPARAM wParam, LPARAM lParam) -{ - int idx; - if ((idx = hooks.getIndex((THook *)pszEvent)) == -1) - return -1; - - THook *p = hooks[idx]; - if (p == nullptr || hInst == nullptr) - return -1; - - mir_cslock lck(p->csHook); - for (int i = 0; i < p->subscriberCount; i++) { - THookSubscriber* s = &p->subscriber[i]; - if (s->hOwner != hInst) - continue; - - int returnVal; - switch (s->type) { - case 1: returnVal = s->pfnHook(wParam, lParam); break; - case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break; - case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; - case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; - case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break; - default: continue; - } - if (returnVal) - return returnVal; - } - - if (p->subscriberCount == 0 && p->pfnHook != nullptr) - return p->pfnHook(wParam, lParam); - - return 0; -} - -MIR_CORE_DLL(int) CallObjectEventHook(void *pObject, HANDLE hEvent, WPARAM wParam, LPARAM lParam) -{ - THook *p = (THook*)hEvent; - if (p == nullptr || pObject == nullptr) - return -1; - - mir_cslock lck(p->csHook); - for (int i = 0; i < p->subscriberCount; i++) { - THookSubscriber* s = &p->subscriber[i]; - if (s->object != pObject) - continue; - - int returnVal; - switch (s->type) { - case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; - case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; - default: continue; - } - if (returnVal) - return returnVal; - } - - if (p->subscriberCount == 0 && p->pfnHook != nullptr) - return p->pfnHook(wParam, lParam); - - return 0; -} - -static int CallHookSubscribers(THook *p, WPARAM wParam, LPARAM lParam) -{ - if (p == nullptr) - return -1; - - mir_cslock lck(p->csHook); - - // NOTE: We've got the critical section while all this lot are called. That's mostly safe, though. - for (int i = 0; i < p->subscriberCount; i++) { - THookSubscriber* s = &p->subscriber[i]; - - int returnVal; - switch (s->type) { - case 1: returnVal = s->pfnHook(wParam, lParam); break; - case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break; - case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; - case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; - case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break; - default: continue; - } - if (returnVal) - return returnVal; - } - - // call the default hook if any - if (p->pfnHook != nullptr) - return p->pfnHook(wParam, lParam); - - return 0; -} - -enum { hookOk, hookEmpty, hookInvalid }; - -int checkHook(THook *p) -{ - if (p == nullptr) - return hookInvalid; - - int ret; - __try { - if (p->secretSignature != HOOK_SECRET_SIGNATURE) - ret = hookInvalid; - else if (p->subscriberCount == 0 && p->pfnHook == nullptr) - ret = hookEmpty; - else - ret = hookOk; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - ret = hookInvalid; - } - - return ret; -} - -static void CALLBACK HookToMainAPCFunc(ULONG_PTR dwParam) -{ - THookToMainThreadItem* item = (THookToMainThreadItem*)dwParam; - item->result = CallHookSubscribers(item->hook, item->wParam, item->lParam); - SetEvent(item->hDoneEvent); -} - -MIR_CORE_DLL(int) NotifyEventHooks(HANDLE hEvent, WPARAM wParam, LPARAM lParam) -{ - switch (checkHook((THook*)hEvent)) { - case hookInvalid: return -1; - case hookEmpty: return 0; - } - - if (GetCurrentThreadId() == mainThreadId) - return CallHookSubscribers((THook*)hEvent, wParam, lParam); - - THookToMainThreadItem item; - item.hDoneEvent = getThreadEvent(); - item.hook = (THook*)hEvent; - item.wParam = wParam; - item.lParam = lParam; - QueueMainThread(HookToMainAPCFunc, &item, item.hDoneEvent); - return item.result; -} - -MIR_CORE_DLL(int) NotifyFastHook(HANDLE hEvent, WPARAM wParam, LPARAM lParam) -{ - switch (checkHook((THook*)hEvent)) { - case hookInvalid: return -1; - case hookEmpty: return 0; - } - - return CallHookSubscribers((THook*)hEvent, wParam, lParam); -} - -extern "C" MIR_CORE_DLL(int) GetSubscribersCount(THook* pHook) -{ - switch (checkHook(pHook)) { - case hookInvalid: - case hookEmpty: return 0; - } - return pHook->subscriberCount; -} - -static HANDLE HookEventInt(int type, const char *name, MIRANDAHOOK hookProc, void* object, LPARAM lParam) -{ - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) == -1) - return nullptr; - - THook *p = hooks[idx]; - p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); - - THookSubscriber &s = p->subscriber[p->subscriberCount]; - s.type = type; - s.pfnHook = hookProc; - s.object = object; - s.lParam = lParam; - s.hOwner = GetInstByAddress(hookProc); - p->subscriberCount++; - - return (HANDLE)((p->id << 16) | p->subscriberCount); -} - -MIR_CORE_DLL(HANDLE) HookEvent(const char *name, MIRANDAHOOK hookProc) -{ - return HookEventInt(1, name, hookProc, nullptr, 0); -} - -MIR_CORE_DLL(HANDLE) HookEventParam(const char *name, MIRANDAHOOKPARAM hookProc, LPARAM lParam) -{ - return HookEventInt(2, name, (MIRANDAHOOK)hookProc, nullptr, lParam); -} - -MIR_CORE_DLL(HANDLE) HookEventObj(const char *name, MIRANDAHOOKOBJ hookProc, void* object) -{ - return HookEventInt(3, name, (MIRANDAHOOK)hookProc, object, 0); -} - -MIR_CORE_DLL(HANDLE) HookEventObjParam(const char *name, MIRANDAHOOKOBJPARAM hookProc, void* object, LPARAM lParam) -{ - return HookEventInt(4, name, (MIRANDAHOOK)hookProc, object, lParam); -} - -MIR_CORE_DLL(HANDLE) HookTemporaryEvent(const char *name, MIRANDAHOOK hookProc) -{ - mir_cslockfull lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) == -1) { - lck.unlock(); - hookProc(0, 0); - return nullptr; - } - - THook *p = hooks[idx]; - p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); - - THookSubscriber &s = p->subscriber[p->subscriberCount]; - memset(&s, 0, sizeof(THookSubscriber)); - s.type = 1; - s.pfnHook = hookProc; - s.hOwner = GetInstByAddress(hookProc); - - p->subscriberCount++; - return (HANDLE)((p->id << 16) | p->subscriberCount); -} - -MIR_CORE_DLL(HANDLE) HookEventMessage(const char *name, HWND hwnd, UINT message) -{ - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) == -1) - return nullptr; - - THook *p = hooks[idx]; - p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); - p->subscriber[p->subscriberCount].type = 5; - p->subscriber[p->subscriberCount].hwnd = hwnd; - p->subscriber[p->subscriberCount].message = message; - p->subscriberCount++; - return (HANDLE)((p->id << 16) | p->subscriberCount); -} - -MIR_CORE_DLL(int) UnhookEvent(HANDLE hHook) -{ - if (hHook == nullptr) - return 0; - - int hookId = (INT_PTR)hHook >> 16; - int subscriberId = ((INT_PTR)hHook & 0xFFFF) - 1; - - mir_cslock lck(csHooks); - - THook *p = nullptr; - for (auto &it : hooks) - if (it->id == hookId) { - p = it; - break; - } - - if (p == nullptr) - return 1; - - if (subscriberId >= p->subscriberCount || subscriberId < 0) - return 1; - - p->subscriber[subscriberId].type = 0; - p->subscriber[subscriberId].pfnHook = nullptr; - p->subscriber[subscriberId].hOwner = nullptr; - while (p->subscriberCount && p->subscriber[p->subscriberCount - 1].type == 0) - p->subscriberCount--; - if (p->subscriberCount == 0) { - mir_free(p->subscriber); - p->subscriber = nullptr; - } - return 0; -} - -MIR_CORE_DLL(void) KillModuleEventHooks(HINSTANCE hInst) -{ - mir_cslock lck(csHooks); - - for (auto &it : hooks.rev_iter()) { - if (it->subscriberCount == 0) - continue; - - for (int j = it->subscriberCount - 1; j >= 0; j--) { - if (it->subscriber[j].hOwner != hInst) - continue; - - char szModuleName[MAX_PATH]; - GetModuleFileNameA(it->subscriber[j].hOwner, szModuleName, sizeof(szModuleName)); - UnhookEvent((HANDLE)((it->id << 16) + j + 1)); - if (it->subscriberCount == 0) - break; - } - } -} - -MIR_CORE_DLL(void) KillObjectEventHooks(void* pObject) -{ - mir_cslock lck(csHooks); - - for (auto &it : hooks.rev_iter()) { - if (it->subscriberCount == 0) - continue; - - for (int j = it->subscriberCount - 1; j >= 0; j--) { - if (it->subscriber[j].object == pObject) { - UnhookEvent((HANDLE)((it->id << 16) + j + 1)); - if (it->subscriberCount == 0) - break; - } - } - } -} - -static void DestroyHooks() -{ - mir_cslock lck(csHooks); - - for (auto &it : hooks) { - if (it->subscriberCount) - mir_free(it->subscriber); - delete it; - } -} - -/////////////////////SERVICES - -static __inline TService* FindServiceByName(const char *name) -{ - unsigned hash = mir_hashstr(name); - return services.find((TService*)&hash); -} - -static HANDLE CreateServiceInt(int type, const char *name, MIRANDASERVICE serviceProc, void* object, LPARAM lParam) -{ - if (name == nullptr) - return nullptr; - - TService tmp; - tmp.nameHash = mir_hashstr(name); - - mir_cslock lck(csServices); - - if (services.getIndex(&tmp) != -1) - return nullptr; - - TService* p = (TService*)mir_alloc(sizeof(*p) + strlen(name)); - strcpy(p->name, name); - p->nameHash = tmp.nameHash; - p->pfnService = serviceProc; - p->hOwner = GetInstByAddress(serviceProc); - p->flags = type; - p->lParam = lParam; - p->object = object; - services.insert(p); - - return (HANDLE)tmp.nameHash; -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunction(const char *name, MIRANDASERVICE serviceProc) -{ - return CreateServiceInt(0, name, serviceProc, nullptr, 0); -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunctionParam(const char *name, MIRANDASERVICEPARAM serviceProc, LPARAM lParam) -{ - return CreateServiceInt(1, name, (MIRANDASERVICE)serviceProc, nullptr, lParam); -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunctionObj(const char *name, MIRANDASERVICEOBJ serviceProc, void* object) -{ - return CreateServiceInt(2, name, (MIRANDASERVICE)serviceProc, object, 0); -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunctionObjParam(const char *name, MIRANDASERVICEOBJPARAM serviceProc, void* object, LPARAM lParam) -{ - return CreateServiceInt(3, name, (MIRANDASERVICE)serviceProc, object, lParam); -} - -MIR_CORE_DLL(HANDLE) CreateProtoServiceFunction(const char *szModule, const char *szService, MIRANDASERVICE serviceProc) -{ - char str[MAXMODULELABELLENGTH * 2]; - strncpy_s(str, szModule, _TRUNCATE); - strncat_s(str, szService, _TRUNCATE); - return CreateServiceFunction(str, serviceProc); -} - -MIR_CORE_DLL(int) DestroyServiceFunction(HANDLE hService) -{ - mir_cslock lck(csServices); - - int idx = services.getIndex((TService*)&hService); - if (idx != -1) { - mir_free(services[idx]); - services.remove(idx); - } - - return 0; -} - -MIR_CORE_DLL(bool) ServiceExists(const char *name) -{ - if (name == nullptr) - return FALSE; - - mir_cslock lck(csServices); - return FindServiceByName(name) != nullptr; -} - -MIR_CORE_DLL(INT_PTR) CallService(const char *name, WPARAM wParam, LPARAM lParam) -{ - if (name == nullptr) - return CALLSERVICE_NOTFOUND; - - TService *pService; - { - mir_cslock lck(csServices); - if ((pService = FindServiceByName(name)) == nullptr) - return CALLSERVICE_NOTFOUND; - } - - MIRANDASERVICE pfnService = pService->pfnService; - int flags = pService->flags; - LPARAM fnParam = pService->lParam; - void* object = pService->object; - switch (flags) { - case 1: return ((MIRANDASERVICEPARAM)pfnService)(wParam, lParam, fnParam); - case 2: return ((MIRANDASERVICEOBJ)pfnService)(object, wParam, lParam); - case 3: return ((MIRANDASERVICEOBJPARAM)pfnService)(object, wParam, lParam, fnParam); - default: return pfnService(wParam, lParam); - } -} - -static void CALLBACK CallServiceToMainAPCFunc(ULONG_PTR dwParam) -{ - TServiceToMainThreadItem *item = (TServiceToMainThreadItem*)dwParam; - item->result = CallService(item->name, item->wParam, item->lParam); - SetEvent(item->hDoneEvent); -} - -MIR_CORE_DLL(INT_PTR) CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam) -{ - if (name == nullptr) - return CALLSERVICE_NOTFOUND; - - // the service is looked up within the main thread, since the time it takes - // for the APC queue to clear the service being called maybe removed. - // even thou it may exists before the call, the critsec can't be locked between calls. - if (GetCurrentThreadId() == mainThreadId) - return CallService(name, wParam, lParam); - - TServiceToMainThreadItem item; - item.wParam = wParam; - item.lParam = lParam; - item.name = name; - item.hDoneEvent = getThreadEvent(); - QueueMainThread(CallServiceToMainAPCFunc, &item, item.hDoneEvent); - return item.result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) CallFunctionAsync(void(__stdcall *func)(void *), void *arg) -{ - if (GetCurrentThreadId() == mainThreadId) - func(arg); - else - QueueMainThread((PAPCFUNC)func, arg, nullptr); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct TSyncCallParam -{ - INT_PTR(__stdcall *func)(void *); - void *arg; - HANDLE hDoneEvent; - INT_PTR result; -}; - -static void CALLBACK CallFuncToMainAPCFunc(ULONG_PTR dwParam) -{ - TSyncCallParam *item = (TSyncCallParam*)dwParam; - item->result = (*item->func)(item->arg); - SetEvent(item->hDoneEvent); -} - -MIR_CORE_DLL(INT_PTR) CallFunctionSync(INT_PTR(__stdcall *func)(void *), void *arg) -{ - if (GetCurrentThreadId() == mainThreadId) - return func(arg); - - TSyncCallParam param = { func, arg, getThreadEvent() }; - QueueMainThread(CallFuncToMainAPCFunc, ¶m, param.hDoneEvent); - return param.result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) KillModuleServices(HINSTANCE hInst) -{ - mir_cslock lck(csServices); - - for (auto &it : services.rev_iter()) { - if (it->hOwner == hInst) { - char szModuleName[MAX_PATH]; - GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName)); - DestroyServiceFunction((HANDLE)it->nameHash); - } - } -} - -MIR_CORE_DLL(void) KillObjectServices(void* pObject) -{ - mir_cslock lck(csServices); - - for (auto &it : services.rev_iter()) - if (it->object == pObject) - DestroyServiceFunction((HANDLE)it->nameHash); -} - -static void DestroyServices() -{ - mir_cslock lck(csServices); - - for (auto &it : services) - mir_free(it); -} - -/////////////////////////////////////////////////////////////////////////////// - -int InitialiseModularEngine(void) -{ - mainThreadId = GetCurrentThreadId(); - return 0; -} - -void DestroyModularEngine(void) -{ - DestroyHooks(); - DestroyServices(); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+// list of hooks
+
+static int compareHooks(const THook* p1, const THook* p2)
+{
+ return strcmp(p1->name, p2->name);
+}
+
+static LIST<THook> hooks(50, compareHooks);
+
+struct THookToMainThreadItem
+{
+ THook* hook;
+ HANDLE hDoneEvent;
+ WPARAM wParam;
+ LPARAM lParam;
+ int result;
+};
+
+// list of services
+
+struct TService
+{
+ uint32_t nameHash;
+ HINSTANCE hOwner;
+ union
+ {
+ MIRANDASERVICE pfnService;
+ MIRANDASERVICEPARAM pfnServiceParam;
+ MIRANDASERVICEOBJ pfnServiceObj;
+ MIRANDASERVICEOBJPARAM pfnServiceObjParam;
+ };
+ int flags;
+ LPARAM lParam;
+ void* object;
+ char name[1];
+};
+
+LIST<TService> services(100, NumericKeySortT);
+
+struct TServiceToMainThreadItem
+{
+ HANDLE hDoneEvent;
+ WPARAM wParam;
+ LPARAM lParam;
+ int result;
+ const char *name;
+};
+
+// other static variables
+static BOOL bServiceMode = FALSE;
+static mir_cs csHooks, csServices;
+static uint32_t mainThreadId;
+static int sttHookId = 1;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+__forceinline HANDLE getThreadEvent()
+{
+ HANDLE pData = (HANDLE)TlsGetValue(mir_tls);
+ if (pData == nullptr) {
+ pData = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+ TlsSetValue(mir_tls, pData);
+ }
+ return pData;
+}
+
+static int QueueMainThread(PAPCFUNC pFunc, void* pParam, HANDLE hDoneEvent)
+{
+ int result = PostMessage(hAPCWindow, WM_USER + 1, (WPARAM)pFunc, (LPARAM)pParam); // let this get processed in its own time
+ if (hDoneEvent)
+ WaitForSingleObject(hDoneEvent, INFINITE);
+
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HOOKS
+
+MIR_CORE_DLL(HANDLE) CreateHookableEvent(const char *name)
+{
+ if (name == nullptr)
+ return nullptr;
+
+ mir_cslock lck(csHooks);
+
+ int idx;
+ if ((idx = hooks.getIndex((THook*)name)) != -1)
+ return hooks[idx];
+
+ THook *newItem = new THook();
+ strncpy(newItem->name, name, sizeof(newItem->name)); newItem->name[MAXMODULELABELLENGTH - 1] = 0;
+ newItem->id = sttHookId++;
+ hooks.insert(newItem);
+ return (HANDLE)newItem;
+}
+
+MIR_CORE_DLL(int) DestroyHookableEvent(HANDLE hEvent)
+{
+ if (hEvent == nullptr)
+ return 1;
+
+ mir_cslock lck(csHooks);
+
+ int idx;
+ if ((idx = hooks.getIndex((THook*)hEvent)) == -1)
+ return 1;
+
+ THook *p = hooks[idx];
+ p->secretSignature = 0;
+ if (p->subscriberCount) {
+ mir_free(p->subscriber);
+ p->subscriber = nullptr;
+ p->subscriberCount = 0;
+ }
+ hooks.remove(idx);
+ delete p;
+ return 0;
+}
+
+MIR_CORE_DLL(int) SetHookDefaultForHookableEvent(HANDLE hEvent, MIRANDAHOOK pfnHook)
+{
+ THook *p = (THook*)hEvent;
+
+ mir_cslock lck(csHooks);
+ if (hooks.getIndex(p) != -1)
+ p->pfnHook = pfnHook;
+ return 0;
+}
+
+MIR_CORE_DLL(int) CallPluginEventHook(HINSTANCE hInst, const char *pszEvent, WPARAM wParam, LPARAM lParam)
+{
+ int idx;
+ if ((idx = hooks.getIndex((THook *)pszEvent)) == -1)
+ return -1;
+
+ THook *p = hooks[idx];
+ if (p == nullptr || hInst == nullptr)
+ return -1;
+
+ mir_cslock lck(p->csHook);
+ for (int i = 0; i < p->subscriberCount; i++) {
+ THookSubscriber* s = &p->subscriber[i];
+ if (s->hOwner != hInst)
+ continue;
+
+ int returnVal;
+ switch (s->type) {
+ case 1: returnVal = s->pfnHook(wParam, lParam); break;
+ case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break;
+ case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break;
+ case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break;
+ case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break;
+ default: continue;
+ }
+ if (returnVal)
+ return returnVal;
+ }
+
+ if (p->subscriberCount == 0 && p->pfnHook != nullptr)
+ return p->pfnHook(wParam, lParam);
+
+ return 0;
+}
+
+MIR_CORE_DLL(int) CallObjectEventHook(void *pObject, HANDLE hEvent, WPARAM wParam, LPARAM lParam)
+{
+ THook *p = (THook*)hEvent;
+ if (p == nullptr || pObject == nullptr)
+ return -1;
+
+ mir_cslock lck(p->csHook);
+ for (int i = 0; i < p->subscriberCount; i++) {
+ THookSubscriber* s = &p->subscriber[i];
+ if (s->object != pObject)
+ continue;
+
+ int returnVal;
+ switch (s->type) {
+ case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break;
+ case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break;
+ default: continue;
+ }
+ if (returnVal)
+ return returnVal;
+ }
+
+ if (p->subscriberCount == 0 && p->pfnHook != nullptr)
+ return p->pfnHook(wParam, lParam);
+
+ return 0;
+}
+
+static int CallHookSubscribers(THook *p, WPARAM wParam, LPARAM lParam)
+{
+ if (p == nullptr)
+ return -1;
+
+ mir_cslock lck(p->csHook);
+
+ // NOTE: We've got the critical section while all this lot are called. That's mostly safe, though.
+ for (int i = 0; i < p->subscriberCount; i++) {
+ THookSubscriber* s = &p->subscriber[i];
+
+ int returnVal;
+ switch (s->type) {
+ case 1: returnVal = s->pfnHook(wParam, lParam); break;
+ case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break;
+ case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break;
+ case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break;
+ case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break;
+ default: continue;
+ }
+ if (returnVal)
+ return returnVal;
+ }
+
+ // call the default hook if any
+ if (p->pfnHook != nullptr)
+ return p->pfnHook(wParam, lParam);
+
+ return 0;
+}
+
+enum { hookOk, hookEmpty, hookInvalid };
+
+int checkHook(THook *p)
+{
+ if (p == nullptr)
+ return hookInvalid;
+
+ int ret;
+ __try {
+ if (p->secretSignature != HOOK_SECRET_SIGNATURE)
+ ret = hookInvalid;
+ else if (p->subscriberCount == 0 && p->pfnHook == nullptr)
+ ret = hookEmpty;
+ else
+ ret = hookOk;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ ret = hookInvalid;
+ }
+
+ return ret;
+}
+
+static void CALLBACK HookToMainAPCFunc(ULONG_PTR dwParam)
+{
+ THookToMainThreadItem* item = (THookToMainThreadItem*)dwParam;
+ item->result = CallHookSubscribers(item->hook, item->wParam, item->lParam);
+ SetEvent(item->hDoneEvent);
+}
+
+MIR_CORE_DLL(int) NotifyEventHooks(HANDLE hEvent, WPARAM wParam, LPARAM lParam)
+{
+ switch (checkHook((THook*)hEvent)) {
+ case hookInvalid: return -1;
+ case hookEmpty: return 0;
+ }
+
+ if (GetCurrentThreadId() == mainThreadId)
+ return CallHookSubscribers((THook*)hEvent, wParam, lParam);
+
+ THookToMainThreadItem item;
+ item.hDoneEvent = getThreadEvent();
+ item.hook = (THook*)hEvent;
+ item.wParam = wParam;
+ item.lParam = lParam;
+ QueueMainThread(HookToMainAPCFunc, &item, item.hDoneEvent);
+ return item.result;
+}
+
+MIR_CORE_DLL(int) NotifyFastHook(HANDLE hEvent, WPARAM wParam, LPARAM lParam)
+{
+ switch (checkHook((THook*)hEvent)) {
+ case hookInvalid: return -1;
+ case hookEmpty: return 0;
+ }
+
+ return CallHookSubscribers((THook*)hEvent, wParam, lParam);
+}
+
+extern "C" MIR_CORE_DLL(int) GetSubscribersCount(THook* pHook)
+{
+ switch (checkHook(pHook)) {
+ case hookInvalid:
+ case hookEmpty: return 0;
+ }
+ return pHook->subscriberCount;
+}
+
+static HANDLE HookEventInt(int type, const char *name, MIRANDAHOOK hookProc, void* object, LPARAM lParam)
+{
+ mir_cslock lck(csHooks);
+
+ int idx;
+ if ((idx = hooks.getIndex((THook*)name)) == -1)
+ return nullptr;
+
+ THook *p = hooks[idx];
+ p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1));
+
+ THookSubscriber &s = p->subscriber[p->subscriberCount];
+ s.type = type;
+ s.pfnHook = hookProc;
+ s.object = object;
+ s.lParam = lParam;
+ s.hOwner = GetInstByAddress(hookProc);
+ p->subscriberCount++;
+
+ return (HANDLE)((p->id << 16) | p->subscriberCount);
+}
+
+MIR_CORE_DLL(HANDLE) HookEvent(const char *name, MIRANDAHOOK hookProc)
+{
+ return HookEventInt(1, name, hookProc, nullptr, 0);
+}
+
+MIR_CORE_DLL(HANDLE) HookEventParam(const char *name, MIRANDAHOOKPARAM hookProc, LPARAM lParam)
+{
+ return HookEventInt(2, name, (MIRANDAHOOK)hookProc, nullptr, lParam);
+}
+
+MIR_CORE_DLL(HANDLE) HookEventObj(const char *name, MIRANDAHOOKOBJ hookProc, void* object)
+{
+ return HookEventInt(3, name, (MIRANDAHOOK)hookProc, object, 0);
+}
+
+MIR_CORE_DLL(HANDLE) HookEventObjParam(const char *name, MIRANDAHOOKOBJPARAM hookProc, void* object, LPARAM lParam)
+{
+ return HookEventInt(4, name, (MIRANDAHOOK)hookProc, object, lParam);
+}
+
+MIR_CORE_DLL(HANDLE) HookTemporaryEvent(const char *name, MIRANDAHOOK hookProc)
+{
+ mir_cslockfull lck(csHooks);
+
+ int idx;
+ if ((idx = hooks.getIndex((THook*)name)) == -1) {
+ lck.unlock();
+ hookProc(0, 0);
+ return nullptr;
+ }
+
+ THook *p = hooks[idx];
+ p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1));
+
+ THookSubscriber &s = p->subscriber[p->subscriberCount];
+ memset(&s, 0, sizeof(THookSubscriber));
+ s.type = 1;
+ s.pfnHook = hookProc;
+ s.hOwner = GetInstByAddress(hookProc);
+
+ p->subscriberCount++;
+ return (HANDLE)((p->id << 16) | p->subscriberCount);
+}
+
+MIR_CORE_DLL(HANDLE) HookEventMessage(const char *name, HWND hwnd, UINT message)
+{
+ mir_cslock lck(csHooks);
+
+ int idx;
+ if ((idx = hooks.getIndex((THook*)name)) == -1)
+ return nullptr;
+
+ THook *p = hooks[idx];
+ p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1));
+ p->subscriber[p->subscriberCount].type = 5;
+ p->subscriber[p->subscriberCount].hwnd = hwnd;
+ p->subscriber[p->subscriberCount].message = message;
+ p->subscriberCount++;
+ return (HANDLE)((p->id << 16) | p->subscriberCount);
+}
+
+MIR_CORE_DLL(int) UnhookEvent(HANDLE hHook)
+{
+ if (hHook == nullptr)
+ return 0;
+
+ int hookId = (INT_PTR)hHook >> 16;
+ int subscriberId = ((INT_PTR)hHook & 0xFFFF) - 1;
+
+ mir_cslock lck(csHooks);
+
+ THook *p = nullptr;
+ for (auto &it : hooks)
+ if (it->id == hookId) {
+ p = it;
+ break;
+ }
+
+ if (p == nullptr)
+ return 1;
+
+ if (subscriberId >= p->subscriberCount || subscriberId < 0)
+ return 1;
+
+ p->subscriber[subscriberId].type = 0;
+ p->subscriber[subscriberId].pfnHook = nullptr;
+ p->subscriber[subscriberId].hOwner = nullptr;
+ while (p->subscriberCount && p->subscriber[p->subscriberCount - 1].type == 0)
+ p->subscriberCount--;
+ if (p->subscriberCount == 0) {
+ mir_free(p->subscriber);
+ p->subscriber = nullptr;
+ }
+ return 0;
+}
+
+MIR_CORE_DLL(void) KillModuleEventHooks(HINSTANCE hInst)
+{
+ mir_cslock lck(csHooks);
+
+ for (auto &it : hooks.rev_iter()) {
+ if (it->subscriberCount == 0)
+ continue;
+
+ for (int j = it->subscriberCount - 1; j >= 0; j--) {
+ if (it->subscriber[j].hOwner != hInst)
+ continue;
+
+ char szModuleName[MAX_PATH];
+ GetModuleFileNameA(it->subscriber[j].hOwner, szModuleName, sizeof(szModuleName));
+ UnhookEvent((HANDLE)((it->id << 16) + j + 1));
+ if (it->subscriberCount == 0)
+ break;
+ }
+ }
+}
+
+MIR_CORE_DLL(void) KillObjectEventHooks(void* pObject)
+{
+ mir_cslock lck(csHooks);
+
+ for (auto &it : hooks.rev_iter()) {
+ if (it->subscriberCount == 0)
+ continue;
+
+ for (int j = it->subscriberCount - 1; j >= 0; j--) {
+ if (it->subscriber[j].object == pObject) {
+ UnhookEvent((HANDLE)((it->id << 16) + j + 1));
+ if (it->subscriberCount == 0)
+ break;
+ }
+ }
+ }
+}
+
+static void DestroyHooks()
+{
+ mir_cslock lck(csHooks);
+
+ for (auto &it : hooks) {
+ if (it->subscriberCount)
+ mir_free(it->subscriber);
+ delete it;
+ }
+}
+
+/////////////////////SERVICES
+
+static __inline TService* FindServiceByName(const char *name)
+{
+ unsigned hash = mir_hashstr(name);
+ return services.find((TService*)&hash);
+}
+
+static HANDLE CreateServiceInt(int type, const char *name, MIRANDASERVICE serviceProc, void* object, LPARAM lParam)
+{
+ if (name == nullptr)
+ return nullptr;
+
+ TService tmp;
+ tmp.nameHash = mir_hashstr(name);
+
+ mir_cslock lck(csServices);
+
+ if (services.getIndex(&tmp) != -1)
+ return nullptr;
+
+ TService* p = (TService*)mir_alloc(sizeof(*p) + strlen(name));
+ strcpy(p->name, name);
+ p->nameHash = tmp.nameHash;
+ p->pfnService = serviceProc;
+ p->hOwner = GetInstByAddress(serviceProc);
+ p->flags = type;
+ p->lParam = lParam;
+ p->object = object;
+ services.insert(p);
+
+ return (HANDLE)tmp.nameHash;
+}
+
+MIR_CORE_DLL(HANDLE) CreateServiceFunction(const char *name, MIRANDASERVICE serviceProc)
+{
+ return CreateServiceInt(0, name, serviceProc, nullptr, 0);
+}
+
+MIR_CORE_DLL(HANDLE) CreateServiceFunctionParam(const char *name, MIRANDASERVICEPARAM serviceProc, LPARAM lParam)
+{
+ return CreateServiceInt(1, name, (MIRANDASERVICE)serviceProc, nullptr, lParam);
+}
+
+MIR_CORE_DLL(HANDLE) CreateServiceFunctionObj(const char *name, MIRANDASERVICEOBJ serviceProc, void* object)
+{
+ return CreateServiceInt(2, name, (MIRANDASERVICE)serviceProc, object, 0);
+}
+
+MIR_CORE_DLL(HANDLE) CreateServiceFunctionObjParam(const char *name, MIRANDASERVICEOBJPARAM serviceProc, void* object, LPARAM lParam)
+{
+ return CreateServiceInt(3, name, (MIRANDASERVICE)serviceProc, object, lParam);
+}
+
+MIR_CORE_DLL(HANDLE) CreateProtoServiceFunction(const char *szModule, const char *szService, MIRANDASERVICE serviceProc)
+{
+ char str[MAXMODULELABELLENGTH * 2];
+ strncpy_s(str, szModule, _TRUNCATE);
+ strncat_s(str, szService, _TRUNCATE);
+ return CreateServiceFunction(str, serviceProc);
+}
+
+MIR_CORE_DLL(int) DestroyServiceFunction(HANDLE hService)
+{
+ mir_cslock lck(csServices);
+
+ int idx = services.getIndex((TService*)&hService);
+ if (idx != -1) {
+ mir_free(services[idx]);
+ services.remove(idx);
+ }
+
+ return 0;
+}
+
+MIR_CORE_DLL(bool) ServiceExists(const char *name)
+{
+ if (name == nullptr)
+ return FALSE;
+
+ mir_cslock lck(csServices);
+ return FindServiceByName(name) != nullptr;
+}
+
+MIR_CORE_DLL(INT_PTR) CallService(const char *name, WPARAM wParam, LPARAM lParam)
+{
+ if (name == nullptr)
+ return CALLSERVICE_NOTFOUND;
+
+ TService *pService;
+ {
+ mir_cslock lck(csServices);
+ if ((pService = FindServiceByName(name)) == nullptr)
+ return CALLSERVICE_NOTFOUND;
+ }
+
+ MIRANDASERVICE pfnService = pService->pfnService;
+ int flags = pService->flags;
+ LPARAM fnParam = pService->lParam;
+ void* object = pService->object;
+ switch (flags) {
+ case 1: return ((MIRANDASERVICEPARAM)pfnService)(wParam, lParam, fnParam);
+ case 2: return ((MIRANDASERVICEOBJ)pfnService)(object, wParam, lParam);
+ case 3: return ((MIRANDASERVICEOBJPARAM)pfnService)(object, wParam, lParam, fnParam);
+ default: return pfnService(wParam, lParam);
+ }
+}
+
+static void CALLBACK CallServiceToMainAPCFunc(ULONG_PTR dwParam)
+{
+ TServiceToMainThreadItem *item = (TServiceToMainThreadItem*)dwParam;
+ item->result = CallService(item->name, item->wParam, item->lParam);
+ SetEvent(item->hDoneEvent);
+}
+
+MIR_CORE_DLL(INT_PTR) CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam)
+{
+ if (name == nullptr)
+ return CALLSERVICE_NOTFOUND;
+
+ // the service is looked up within the main thread, since the time it takes
+ // for the APC queue to clear the service being called maybe removed.
+ // even thou it may exists before the call, the critsec can't be locked between calls.
+ if (GetCurrentThreadId() == mainThreadId)
+ return CallService(name, wParam, lParam);
+
+ TServiceToMainThreadItem item;
+ item.wParam = wParam;
+ item.lParam = lParam;
+ item.name = name;
+ item.hDoneEvent = getThreadEvent();
+ QueueMainThread(CallServiceToMainAPCFunc, &item, item.hDoneEvent);
+ return item.result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) CallFunctionAsync(void(__stdcall *func)(void *), void *arg)
+{
+ if (GetCurrentThreadId() == mainThreadId)
+ func(arg);
+ else
+ QueueMainThread((PAPCFUNC)func, arg, nullptr);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct TSyncCallParam
+{
+ INT_PTR(__stdcall *func)(void *);
+ void *arg;
+ HANDLE hDoneEvent;
+ INT_PTR result;
+};
+
+static void CALLBACK CallFuncToMainAPCFunc(ULONG_PTR dwParam)
+{
+ TSyncCallParam *item = (TSyncCallParam*)dwParam;
+ item->result = (*item->func)(item->arg);
+ SetEvent(item->hDoneEvent);
+}
+
+MIR_CORE_DLL(INT_PTR) CallFunctionSync(INT_PTR(__stdcall *func)(void *), void *arg)
+{
+ if (GetCurrentThreadId() == mainThreadId)
+ return func(arg);
+
+ TSyncCallParam param = { func, arg, getThreadEvent() };
+ QueueMainThread(CallFuncToMainAPCFunc, ¶m, param.hDoneEvent);
+ return param.result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) KillModuleServices(HINSTANCE hInst)
+{
+ mir_cslock lck(csServices);
+
+ for (auto &it : services.rev_iter()) {
+ if (it->hOwner == hInst) {
+ char szModuleName[MAX_PATH];
+ GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName));
+ DestroyServiceFunction((HANDLE)it->nameHash);
+ }
+ }
+}
+
+MIR_CORE_DLL(void) KillObjectServices(void* pObject)
+{
+ mir_cslock lck(csServices);
+
+ for (auto &it : services.rev_iter())
+ if (it->object == pObject)
+ DestroyServiceFunction((HANDLE)it->nameHash);
+}
+
+static void DestroyServices()
+{
+ mir_cslock lck(csServices);
+
+ for (auto &it : services)
+ mir_free(it);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int InitialiseModularEngine(void)
+{
+ mainThreadId = GetCurrentThreadId();
+ return 0;
+}
+
+void DestroyModularEngine(void)
+{
+ DestroyHooks();
+ DestroyServices();
+}
diff --git a/src/mir_core/src/mstring.cpp b/src/mir_core/src/mstring.cpp index 2bff18e7f1..04a26f8a9d 100644 --- a/src/mir_core/src/mstring.cpp +++ b/src/mir_core/src/mstring.cpp @@ -1,145 +1,145 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CMBaseString - -class CNilMStringData : public CMStringData -{ -public: - CNilMStringData(); - -public: - wchar_t achNil[2]; -}; - -CNilMStringData::CNilMStringData() -{ - nRefs = 2; // Never gets freed - nDataLength = 0; - nAllocLength = 0; - achNil[0] = 0; - achNil[1] = 0; -} - -static CNilMStringData *m_nil = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// -// CMBaseString - -MIR_CORE_DLL(CMStringData*) mirstr_allocate(int nChars, int nCharSize) -{ - nChars++; // nil char - size_t nDataBytes = nCharSize * nChars; - size_t nTotalSize = nDataBytes + sizeof(CMStringData); - - CMStringData *pData = static_cast<CMStringData*>(malloc(nTotalSize)); - if (pData == nullptr) - return nullptr; - - pData->nRefs = 1; - pData->nAllocLength = nChars - 1; - pData->nDataLength = 0; - return pData; -} - -MIR_CORE_DLL(void) mirstr_free(CMStringData *pData) -{ - free(pData); -} - -MIR_CORE_DLL(CMStringData*) mirstr_realloc(CMStringData* pData, int nChars, int nCharSize) -{ - nChars++; // nil char - uint32_t nDataBytes = nCharSize * nChars; - uint32_t nTotalSize = nDataBytes + sizeof(CMStringData); - - CMStringData *pNewData = static_cast<CMStringData*>(realloc(pData, nTotalSize)); - if (pNewData == nullptr) - return nullptr; - - pNewData->nAllocLength = nChars - 1; - return pNewData; -} - -MIR_CORE_DLL(CMStringData*) mirstr_getNil() -{ - if (m_nil == nullptr) - m_nil = new CNilMStringData(); - m_nil->AddRef(); - return m_nil; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// CMStringData - -MIR_CORE_DLL(void) mirstr_lock(CMStringData* pThis) -{ - pThis->nRefs--; // Locked buffers can't be shared, so no interlocked operation necessary - if (pThis->nRefs == 0) - pThis->nRefs = -1; -} - -MIR_CORE_DLL(void) mirstr_release(CMStringData* pThis) -{ - if (InterlockedDecrement(&pThis->nRefs) <= 0) - mirstr_free(pThis); -} - -MIR_CORE_DLL(void) mirstr_unlock(CMStringData* pThis) -{ - if (pThis->IsLocked()) - { - pThis->nRefs++; // Locked buffers can't be shared, so no interlocked operation necessary - if (pThis->nRefs == 0) - pThis->nRefs = 1; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// don't remove it -// this code just instantiates templates for CMStringW[A/W] - -#ifdef _MSC_VER -template MIR_CORE_EXPORT CMStringW; -template MIR_CORE_EXPORT CMStringA; -#endif - -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const CMStringW& str2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const wchar_t *psz2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const wchar_t *psz1, const CMStringW& str2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, wchar_t ch2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, char ch2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(wchar_t ch1, const CMStringW& str2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(char ch1, const CMStringW& str2); - -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const CMStringA& str2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const char *psz2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const char *psz1, const CMStringA& str2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, wchar_t ch2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, char ch2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(wchar_t ch1, const CMStringA& str2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(char ch1, const CMStringA& str2); +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CMBaseString
+
+class CNilMStringData : public CMStringData
+{
+public:
+ CNilMStringData();
+
+public:
+ wchar_t achNil[2];
+};
+
+CNilMStringData::CNilMStringData()
+{
+ nRefs = 2; // Never gets freed
+ nDataLength = 0;
+ nAllocLength = 0;
+ achNil[0] = 0;
+ achNil[1] = 0;
+}
+
+static CNilMStringData *m_nil = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CMBaseString
+
+MIR_CORE_DLL(CMStringData*) mirstr_allocate(int nChars, int nCharSize)
+{
+ nChars++; // nil char
+ size_t nDataBytes = nCharSize * nChars;
+ size_t nTotalSize = nDataBytes + sizeof(CMStringData);
+
+ CMStringData *pData = static_cast<CMStringData*>(malloc(nTotalSize));
+ if (pData == nullptr)
+ return nullptr;
+
+ pData->nRefs = 1;
+ pData->nAllocLength = nChars - 1;
+ pData->nDataLength = 0;
+ return pData;
+}
+
+MIR_CORE_DLL(void) mirstr_free(CMStringData *pData)
+{
+ free(pData);
+}
+
+MIR_CORE_DLL(CMStringData*) mirstr_realloc(CMStringData* pData, int nChars, int nCharSize)
+{
+ nChars++; // nil char
+ uint32_t nDataBytes = nCharSize * nChars;
+ uint32_t nTotalSize = nDataBytes + sizeof(CMStringData);
+
+ CMStringData *pNewData = static_cast<CMStringData*>(realloc(pData, nTotalSize));
+ if (pNewData == nullptr)
+ return nullptr;
+
+ pNewData->nAllocLength = nChars - 1;
+ return pNewData;
+}
+
+MIR_CORE_DLL(CMStringData*) mirstr_getNil()
+{
+ if (m_nil == nullptr)
+ m_nil = new CNilMStringData();
+ m_nil->AddRef();
+ return m_nil;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CMStringData
+
+MIR_CORE_DLL(void) mirstr_lock(CMStringData* pThis)
+{
+ pThis->nRefs--; // Locked buffers can't be shared, so no interlocked operation necessary
+ if (pThis->nRefs == 0)
+ pThis->nRefs = -1;
+}
+
+MIR_CORE_DLL(void) mirstr_release(CMStringData* pThis)
+{
+ if (InterlockedDecrement(&pThis->nRefs) <= 0)
+ mirstr_free(pThis);
+}
+
+MIR_CORE_DLL(void) mirstr_unlock(CMStringData* pThis)
+{
+ if (pThis->IsLocked())
+ {
+ pThis->nRefs++; // Locked buffers can't be shared, so no interlocked operation necessary
+ if (pThis->nRefs == 0)
+ pThis->nRefs = 1;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// don't remove it
+// this code just instantiates templates for CMStringW[A/W]
+
+#ifdef _MSC_VER
+template MIR_CORE_EXPORT CMStringW;
+template MIR_CORE_EXPORT CMStringA;
+#endif
+
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const CMStringW& str2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const wchar_t *psz2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const wchar_t *psz1, const CMStringW& str2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, wchar_t ch2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, char ch2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(wchar_t ch1, const CMStringW& str2);
+template MIR_CORE_EXPORT CMStringW CALLBACK operator+(char ch1, const CMStringW& str2);
+
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const CMStringA& str2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const char *psz2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const char *psz1, const CMStringA& str2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, wchar_t ch2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, char ch2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(wchar_t ch1, const CMStringA& str2);
+template MIR_CORE_EXPORT CMStringA CALLBACK operator+(char ch1, const CMStringA& str2);
diff --git a/src/mir_core/src/stdafx.cxx b/src/mir_core/src/stdafx.cxx index 564f422ca2..8c570f6949 100644 --- a/src/mir_core/src/stdafx.cxx +++ b/src/mir_core/src/stdafx.cxx @@ -1,5 +1,5 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/mir_core/src/stdafx.h b/src/mir_core/src/stdafx.h index 54ec535a4c..dd6d93d679 100644 --- a/src/mir_core/src/stdafx.h +++ b/src/mir_core/src/stdafx.h @@ -1,84 +1,84 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -#define INCL_WINSOCK_API_TYPEDEFS 1 - -#ifdef _MSC_VER - #include <winsock2.h> - #include <ws2tcpip.h> - #include <windows.h> - #include <windowsx.h> - #include <shlobj.h> - #include <commctrl.h> - #include <ShellAPI.h> - #include <vssym32.h> - #include <Uxtheme.h> - #include <Richedit.h> - #include <Wtsapi32.h> - - #include <process.h> - #include <io.h> - #include <direct.h> - - #ifdef _DEBUG - #include <crtdbg.h> - #endif -#else - #include <Elementary.h> -#endif // _WINDOWS - -#include <malloc.h> -#include <stdio.h> -#include <time.h> -#include <stdarg.h> -#include <stddef.h> -#include <limits.h> -#include <string.h> -#include <locale.h> - -#define __NO_CMPLUGIN_NEEDED -#include <m_system.h> -#include <m_database.h> -#include <m_db_int.h> -#include <newpluginapi.h> -#include <m_langpack.h> -#include <m_metacontacts.h> -#include <m_skin.h> -#include <m_icolib.h> -#include <m_netlib.h> -#include <m_timezones.h> -#include <m_protocols.h> -#include <m_button.h> -#include <m_gui.h> -#include <m_chat_int.h> - -#include "miranda.h" - -#include <m_xml.h> - -#include <m_string.inl> - -void GetDefaultLang(); +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#pragma once
+
+#define INCL_WINSOCK_API_TYPEDEFS 1
+
+#ifdef _MSC_VER
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #include <windows.h>
+ #include <windowsx.h>
+ #include <shlobj.h>
+ #include <commctrl.h>
+ #include <ShellAPI.h>
+ #include <vssym32.h>
+ #include <Uxtheme.h>
+ #include <Richedit.h>
+ #include <Wtsapi32.h>
+
+ #include <process.h>
+ #include <io.h>
+ #include <direct.h>
+
+ #ifdef _DEBUG
+ #include <crtdbg.h>
+ #endif
+#else
+ #include <Elementary.h>
+#endif // _WINDOWS
+
+#include <malloc.h>
+#include <stdio.h>
+#include <time.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <limits.h>
+#include <string.h>
+#include <locale.h>
+
+#define __NO_CMPLUGIN_NEEDED
+#include <m_system.h>
+#include <m_database.h>
+#include <m_db_int.h>
+#include <newpluginapi.h>
+#include <m_langpack.h>
+#include <m_metacontacts.h>
+#include <m_skin.h>
+#include <m_icolib.h>
+#include <m_netlib.h>
+#include <m_timezones.h>
+#include <m_protocols.h>
+#include <m_button.h>
+#include <m_gui.h>
+#include <m_chat_int.h>
+
+#include "miranda.h"
+
+#include <m_xml.h>
+
+#include <m_string.inl>
+
+void GetDefaultLang();
diff --git a/src/mir_core/src/utf.cpp b/src/mir_core/src/utf.cpp index 1e17ed60e8..eefa519727 100644 --- a/src/mir_core/src/utf.cpp +++ b/src/mir_core/src/utf.cpp @@ -1,441 +1,441 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - - Copyright 2000 Alexandre Julliard of Wine project - (UTF-8 conversion routines) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -/* number of following bytes in sequence based on first byte value (for bytes above 0x7f) */ -static const char utf8_length[128] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80-0x8f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x9f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0-0xaf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0-0xbf */ - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xc0-0xcf */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xd0-0xdf */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xe0-0xef */ - 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0-0xff */ -}; - -/* first byte mask depending on UTF-8 sequence length */ -static const unsigned char utf8_mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 }; - -/* minimum Unicode value depending on UTF-8 sequence length */ -static const unsigned int utf8_minval[4] = { 0x0, 0x80, 0x800, 0x10000 }; - -/* get the next char value taking surrogates into account */ -static unsigned int getSurrogateValue(const wchar_t *src, unsigned int srclen) -{ - if (src[0] >= 0xd800 && src[0] <= 0xdfff) { /* surrogate pair */ - if (src[0] > 0xdbff || /* invalid high surrogate */ - srclen <= 1 || /* missing low surrogate */ - src[1] < 0xdc00 || src[1] > 0xdfff) /* invalid low surrogate */ - return 0; - return 0x10000 + ((src[0] & 0x3ff) << 10) + (src[1] & 0x3ff); - } - return src[0]; -} - -/* query necessary dst length for src string */ -static int mir_utf8len(const wchar_t *src, unsigned int srclen) -{ - int len; - unsigned int val; - - for (len = 0; srclen; srclen--, src++) { - if (*src < 0x80) { /* 0x00-0x7f: 1 byte */ - len++; - continue; - } - if (*src < 0x800) { /* 0x80-0x7ff: 2 bytes */ - len += 2; - continue; - } - if (!(val = getSurrogateValue(src, srclen))) - return -2; - - if (val < 0x10000) /* 0x800-0xffff: 3 bytes */ - len += 3; - else { /* 0x10000-0x10ffff: 4 bytes */ - len += 4; - src++; - srclen--; - } - } - return len; -} - -MIR_CORE_DLL(int) mir_utf8lenW(const wchar_t *src) -{ - if (src == nullptr) - return 0; - - return mir_utf8len(src, (int)wcslen(src)); -} - -/* wide char to UTF-8 string conversion */ -/* return -1 on dst buffer overflow, -2 on invalid input char */ -int Ucs2toUtf8(const wchar_t *src, int srclen, char *dst, int dstlen) -{ - int len; - - for (len = dstlen; srclen; srclen--, src++) { - wchar_t ch = *src; - unsigned int val; - - if (ch < 0x80) { /* 0x00-0x7f: 1 byte */ - if (!len--) return -1; /* overflow */ - *dst++ = ch; - continue; - } - - if (ch < 0x800) { /* 0x80-0x7ff: 2 bytes */ - if ((len -= 2) < 0) return -1; /* overflow */ - dst[1] = 0x80 | (ch & 0x3f); - ch >>= 6; - dst[0] = 0xc0 | ch; - dst += 2; - continue; - } - - if (!(val = getSurrogateValue(src, srclen))) - return -2; - - if (val < 0x10000) { /* 0x800-0xffff: 3 bytes */ - if ((len -= 3) < 0) return -1; /* overflow */ - dst[2] = 0x80 | (val & 0x3f); - val >>= 6; - dst[1] = 0x80 | (val & 0x3f); - val >>= 6; - dst[0] = 0xe0 | val; - dst += 3; - } - else { /* 0x10000-0x10ffff: 4 bytes */ - if ((len -= 4) < 0) return -1; /* overflow */ - dst[3] = 0x80 | (val & 0x3f); - val >>= 6; - dst[2] = 0x80 | (val & 0x3f); - val >>= 6; - dst[1] = 0x80 | (val & 0x3f); - val >>= 6; - dst[0] = 0xf0 | val; - dst += 4; - src++; - srclen--; - } - } - return dstlen - len; -} - -/* helper for the various utf8 mbstowcs functions */ -static unsigned int decodeUtf8Char(unsigned char ch, const char **str, const char *strend) -{ - unsigned int len = utf8_length[ch - 0x80]; - unsigned int res = ch & utf8_mask[len]; - const char *end = *str + len; - - if (end > strend) return ~0; - switch (len) { - case 3: - if ((ch = end[-3] ^ 0x80) >= 0x40) break; - res = (res << 6) | ch; - (*str)++; - - case 2: - if ((ch = end[-2] ^ 0x80) >= 0x40) break; - res = (res << 6) | ch; - (*str)++; - - case 1: - if ((ch = end[-1] ^ 0x80) >= 0x40) break; - res = (res << 6) | ch; - (*str)++; - if (res < utf8_minval[len]) break; - return res; - } - return ~0; -} - -/* query necessary dst length for src string */ -static int Utf8toUcs2Len(const char *src, size_t srclen) -{ - int ret = 0; - unsigned int res; - const char *srcend = src + srclen; - - while (src < srcend) { - unsigned char ch = *src++; - if (ch < 0x80) { /* special fast case for 7-bit ASCII */ - ret++; - continue; - } - if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0x10ffff) { - if (res > 0xffff) ret++; - ret++; - } - else return -2; /* bad char */ - /* otherwise ignore it */ - } - return ret; -} - -/* UTF-8 to wide char string conversion */ -/* return -1 on dst buffer overflow, -2 on invalid input char */ -MIR_CORE_DLL(int) Utf8toUcs2(const char *src, size_t srclen, wchar_t *dst, size_t dstlen) -{ - unsigned int res; - const char *srcend = src + srclen; // including trailing zero - wchar_t *dstend = dst + dstlen; - - while ((dst < dstend) && (src < srcend)) { - unsigned char ch = *src++; - if (ch < 0x80) { /* special fast case for 7-bit ASCII */ - *dst++ = ch; - continue; - } - - if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0xffff) - *dst++ = res; - else if (res <= 0x10ffff) { /* we need surrogates */ - if (dst == dstend - 1) - return -1; /* overflow */ - res -= 0x10000; - *dst++ = 0xd800 | (res >> 10); - *dst++ = 0xdc00 | (res & 0x3ff); - } - else return -2; /* bad char */ - } - - if (src < srcend) - return -1; /* overflow */ - - return (int)(dstlen - (dstend - dst)); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// mir_utf8decode - converts UTF8-encoded string to the UCS2/MBCS format - -#ifdef _MSC_VER -MIR_CORE_DLL(char*) mir_utf8decodecp(char *str, int codepage, wchar_t **ucs2) -{ - bool needs_free = false; - wchar_t* tempBuf = nullptr; - if (ucs2) - *ucs2 = nullptr; - - if (str == nullptr) - return nullptr; - - size_t len = strlen(str); - if (len < 2) { - if (ucs2 != nullptr) { - *ucs2 = tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t)); - MultiByteToWideChar(codepage, 0, str, (int)len, tempBuf, (int)len); - tempBuf[len] = 0; - } - return str; - } - - int destlen = Utf8toUcs2Len(str, len); - if (destlen < 0) - return nullptr; - - if (ucs2 == nullptr) { - __try { - tempBuf = (wchar_t*)alloca((destlen + 1) * sizeof(wchar_t)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - tempBuf = nullptr; - needs_free = true; - } - } - - if (tempBuf == nullptr) { - tempBuf = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t)); - if (tempBuf == nullptr) - return nullptr; - } - - Utf8toUcs2(str, len, tempBuf, destlen); - tempBuf[destlen] = 0; - WideCharToMultiByte(codepage, 0, tempBuf, -1, str, (int)len + 1, "?", nullptr); - - if (ucs2) - *ucs2 = tempBuf; - else if (needs_free) - mir_free(tempBuf); - - return str; -} - -MIR_CORE_DLL(char*) mir_utf8decode(char *str, wchar_t **ucs2) -{ - return mir_utf8decodecp(str, Langpack_GetDefaultCodePage(), ucs2); -} -#endif - -MIR_CORE_DLL(wchar_t*) mir_utf8decodeW(const char *str) -{ - if (str == nullptr) - return nullptr; - - size_t len = strlen(str); - - int destlen = Utf8toUcs2Len(str, len); - if (destlen < 0) - return nullptr; - - wchar_t* ucs2 = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t)); - if (ucs2 == nullptr) - return nullptr; - - if (Utf8toUcs2(str, len, ucs2, destlen) >= 0) { - ucs2[destlen] = 0; - return ucs2; - } - - mir_free(ucs2); - - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// mir_utf8encode - converts MBCS string to the UTF8-encoded format - -#ifdef _MSC_VER -MIR_CORE_DLL(char*) mir_utf8encodecp(const char* src, int codepage) -{ - int len; - bool needs_free = false; - char* result = nullptr; - wchar_t* tempBuf; - - if (src == nullptr) - return nullptr; - - len = (int)strlen(src); - - __try { - tempBuf = (wchar_t*)alloca((len + 1) * sizeof(wchar_t)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t)); - if (tempBuf == nullptr) return nullptr; - needs_free = true; - } - - len = MultiByteToWideChar(codepage, 0, src, -1, tempBuf, len + 1); - - int destlen = mir_utf8len(tempBuf, len); - if (destlen >= 0) { - result = (char*)mir_alloc(destlen + 1); - if (result) { - Ucs2toUtf8(tempBuf, len, result, destlen); - result[destlen] = 0; - } - } - - if (needs_free) - mir_free(tempBuf); - - return result; -} - -MIR_CORE_DLL(char*) mir_utf8encode(const char* src) -{ - return mir_utf8encodecp(src, Langpack_GetDefaultCodePage()); -} -#endif - -///////////////////////////////////////////////////////////////////////////////////////// -// mir_utf8encode - converts UCS2 string to the UTF8-encoded format - -MIR_CORE_DLL(char*) mir_utf8encodeW(const wchar_t* src) -{ - if (src == nullptr) - return nullptr; - - int len = (int)wcslen(src); - - int destlen = mir_utf8len(src, len); - if (destlen < 0) return nullptr; - - char* result = (char*)mir_alloc(destlen + 1); - if (result == nullptr) - return nullptr; - - Ucs2toUtf8(src, len, result, destlen); - result[destlen] = 0; - - return result; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Utf8CheckString - checks if a string is a valid utf8-encoded string - -MIR_CORE_DLL(BOOL) Utf8CheckString(const char *str) -{ - int expect_bytes = 0, utf_found = 0; - - if (!str) return 0; - - while (*str) { - if ((*str & 0x80) == 0) { - /* Looks like an ASCII character */ - if (expect_bytes) - /* byte of UTF-8 character expected */ - return 0; - } - else { - /* Looks like byte of an UTF-8 character */ - if (expect_bytes) { - /* expect_bytes already set: first byte of UTF-8 char already seen */ - if ((*str & 0xC0) != 0x80) { - /* again first byte ?!?! */ - return 0; - } - } - else { - /* First byte of the UTF-8 character */ - /* count initial one bits and set expect_bytes to 1 less */ - char ch = *str; - while (ch & 0x80) { - expect_bytes++; - ch = (ch & 0x7f) << 1; - } - } - /* OK, next byte of UTF-8 character */ - /* Decrement number of expected bytes */ - if (--expect_bytes == 0) - utf_found = 1; - } - str++; - } - - return (utf_found && expect_bytes == 0); -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+ Copyright 2000 Alexandre Julliard of Wine project
+ (UTF-8 conversion routines)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/* number of following bytes in sequence based on first byte value (for bytes above 0x7f) */
+static const char utf8_length[128] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80-0x8f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x9f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0-0xaf */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0-0xbf */
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xc0-0xcf */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xd0-0xdf */
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xe0-0xef */
+ 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0-0xff */
+};
+
+/* first byte mask depending on UTF-8 sequence length */
+static const unsigned char utf8_mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 };
+
+/* minimum Unicode value depending on UTF-8 sequence length */
+static const unsigned int utf8_minval[4] = { 0x0, 0x80, 0x800, 0x10000 };
+
+/* get the next char value taking surrogates into account */
+static unsigned int getSurrogateValue(const wchar_t *src, unsigned int srclen)
+{
+ if (src[0] >= 0xd800 && src[0] <= 0xdfff) { /* surrogate pair */
+ if (src[0] > 0xdbff || /* invalid high surrogate */
+ srclen <= 1 || /* missing low surrogate */
+ src[1] < 0xdc00 || src[1] > 0xdfff) /* invalid low surrogate */
+ return 0;
+ return 0x10000 + ((src[0] & 0x3ff) << 10) + (src[1] & 0x3ff);
+ }
+ return src[0];
+}
+
+/* query necessary dst length for src string */
+static int mir_utf8len(const wchar_t *src, unsigned int srclen)
+{
+ int len;
+ unsigned int val;
+
+ for (len = 0; srclen; srclen--, src++) {
+ if (*src < 0x80) { /* 0x00-0x7f: 1 byte */
+ len++;
+ continue;
+ }
+ if (*src < 0x800) { /* 0x80-0x7ff: 2 bytes */
+ len += 2;
+ continue;
+ }
+ if (!(val = getSurrogateValue(src, srclen)))
+ return -2;
+
+ if (val < 0x10000) /* 0x800-0xffff: 3 bytes */
+ len += 3;
+ else { /* 0x10000-0x10ffff: 4 bytes */
+ len += 4;
+ src++;
+ srclen--;
+ }
+ }
+ return len;
+}
+
+MIR_CORE_DLL(int) mir_utf8lenW(const wchar_t *src)
+{
+ if (src == nullptr)
+ return 0;
+
+ return mir_utf8len(src, (int)wcslen(src));
+}
+
+/* wide char to UTF-8 string conversion */
+/* return -1 on dst buffer overflow, -2 on invalid input char */
+int Ucs2toUtf8(const wchar_t *src, int srclen, char *dst, int dstlen)
+{
+ int len;
+
+ for (len = dstlen; srclen; srclen--, src++) {
+ wchar_t ch = *src;
+ unsigned int val;
+
+ if (ch < 0x80) { /* 0x00-0x7f: 1 byte */
+ if (!len--) return -1; /* overflow */
+ *dst++ = ch;
+ continue;
+ }
+
+ if (ch < 0x800) { /* 0x80-0x7ff: 2 bytes */
+ if ((len -= 2) < 0) return -1; /* overflow */
+ dst[1] = 0x80 | (ch & 0x3f);
+ ch >>= 6;
+ dst[0] = 0xc0 | ch;
+ dst += 2;
+ continue;
+ }
+
+ if (!(val = getSurrogateValue(src, srclen)))
+ return -2;
+
+ if (val < 0x10000) { /* 0x800-0xffff: 3 bytes */
+ if ((len -= 3) < 0) return -1; /* overflow */
+ dst[2] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[1] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[0] = 0xe0 | val;
+ dst += 3;
+ }
+ else { /* 0x10000-0x10ffff: 4 bytes */
+ if ((len -= 4) < 0) return -1; /* overflow */
+ dst[3] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[2] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[1] = 0x80 | (val & 0x3f);
+ val >>= 6;
+ dst[0] = 0xf0 | val;
+ dst += 4;
+ src++;
+ srclen--;
+ }
+ }
+ return dstlen - len;
+}
+
+/* helper for the various utf8 mbstowcs functions */
+static unsigned int decodeUtf8Char(unsigned char ch, const char **str, const char *strend)
+{
+ unsigned int len = utf8_length[ch - 0x80];
+ unsigned int res = ch & utf8_mask[len];
+ const char *end = *str + len;
+
+ if (end > strend) return ~0;
+ switch (len) {
+ case 3:
+ if ((ch = end[-3] ^ 0x80) >= 0x40) break;
+ res = (res << 6) | ch;
+ (*str)++;
+
+ case 2:
+ if ((ch = end[-2] ^ 0x80) >= 0x40) break;
+ res = (res << 6) | ch;
+ (*str)++;
+
+ case 1:
+ if ((ch = end[-1] ^ 0x80) >= 0x40) break;
+ res = (res << 6) | ch;
+ (*str)++;
+ if (res < utf8_minval[len]) break;
+ return res;
+ }
+ return ~0;
+}
+
+/* query necessary dst length for src string */
+static int Utf8toUcs2Len(const char *src, size_t srclen)
+{
+ int ret = 0;
+ unsigned int res;
+ const char *srcend = src + srclen;
+
+ while (src < srcend) {
+ unsigned char ch = *src++;
+ if (ch < 0x80) { /* special fast case for 7-bit ASCII */
+ ret++;
+ continue;
+ }
+ if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0x10ffff) {
+ if (res > 0xffff) ret++;
+ ret++;
+ }
+ else return -2; /* bad char */
+ /* otherwise ignore it */
+ }
+ return ret;
+}
+
+/* UTF-8 to wide char string conversion */
+/* return -1 on dst buffer overflow, -2 on invalid input char */
+MIR_CORE_DLL(int) Utf8toUcs2(const char *src, size_t srclen, wchar_t *dst, size_t dstlen)
+{
+ unsigned int res;
+ const char *srcend = src + srclen; // including trailing zero
+ wchar_t *dstend = dst + dstlen;
+
+ while ((dst < dstend) && (src < srcend)) {
+ unsigned char ch = *src++;
+ if (ch < 0x80) { /* special fast case for 7-bit ASCII */
+ *dst++ = ch;
+ continue;
+ }
+
+ if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0xffff)
+ *dst++ = res;
+ else if (res <= 0x10ffff) { /* we need surrogates */
+ if (dst == dstend - 1)
+ return -1; /* overflow */
+ res -= 0x10000;
+ *dst++ = 0xd800 | (res >> 10);
+ *dst++ = 0xdc00 | (res & 0x3ff);
+ }
+ else return -2; /* bad char */
+ }
+
+ if (src < srcend)
+ return -1; /* overflow */
+
+ return (int)(dstlen - (dstend - dst));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// mir_utf8decode - converts UTF8-encoded string to the UCS2/MBCS format
+
+#ifdef _MSC_VER
+MIR_CORE_DLL(char*) mir_utf8decodecp(char *str, int codepage, wchar_t **ucs2)
+{
+ bool needs_free = false;
+ wchar_t* tempBuf = nullptr;
+ if (ucs2)
+ *ucs2 = nullptr;
+
+ if (str == nullptr)
+ return nullptr;
+
+ size_t len = strlen(str);
+ if (len < 2) {
+ if (ucs2 != nullptr) {
+ *ucs2 = tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t));
+ MultiByteToWideChar(codepage, 0, str, (int)len, tempBuf, (int)len);
+ tempBuf[len] = 0;
+ }
+ return str;
+ }
+
+ int destlen = Utf8toUcs2Len(str, len);
+ if (destlen < 0)
+ return nullptr;
+
+ if (ucs2 == nullptr) {
+ __try {
+ tempBuf = (wchar_t*)alloca((destlen + 1) * sizeof(wchar_t));
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ tempBuf = nullptr;
+ needs_free = true;
+ }
+ }
+
+ if (tempBuf == nullptr) {
+ tempBuf = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t));
+ if (tempBuf == nullptr)
+ return nullptr;
+ }
+
+ Utf8toUcs2(str, len, tempBuf, destlen);
+ tempBuf[destlen] = 0;
+ WideCharToMultiByte(codepage, 0, tempBuf, -1, str, (int)len + 1, "?", nullptr);
+
+ if (ucs2)
+ *ucs2 = tempBuf;
+ else if (needs_free)
+ mir_free(tempBuf);
+
+ return str;
+}
+
+MIR_CORE_DLL(char*) mir_utf8decode(char *str, wchar_t **ucs2)
+{
+ return mir_utf8decodecp(str, Langpack_GetDefaultCodePage(), ucs2);
+}
+#endif
+
+MIR_CORE_DLL(wchar_t*) mir_utf8decodeW(const char *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ size_t len = strlen(str);
+
+ int destlen = Utf8toUcs2Len(str, len);
+ if (destlen < 0)
+ return nullptr;
+
+ wchar_t* ucs2 = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t));
+ if (ucs2 == nullptr)
+ return nullptr;
+
+ if (Utf8toUcs2(str, len, ucs2, destlen) >= 0) {
+ ucs2[destlen] = 0;
+ return ucs2;
+ }
+
+ mir_free(ucs2);
+
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// mir_utf8encode - converts MBCS string to the UTF8-encoded format
+
+#ifdef _MSC_VER
+MIR_CORE_DLL(char*) mir_utf8encodecp(const char* src, int codepage)
+{
+ int len;
+ bool needs_free = false;
+ char* result = nullptr;
+ wchar_t* tempBuf;
+
+ if (src == nullptr)
+ return nullptr;
+
+ len = (int)strlen(src);
+
+ __try {
+ tempBuf = (wchar_t*)alloca((len + 1) * sizeof(wchar_t));
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t));
+ if (tempBuf == nullptr) return nullptr;
+ needs_free = true;
+ }
+
+ len = MultiByteToWideChar(codepage, 0, src, -1, tempBuf, len + 1);
+
+ int destlen = mir_utf8len(tempBuf, len);
+ if (destlen >= 0) {
+ result = (char*)mir_alloc(destlen + 1);
+ if (result) {
+ Ucs2toUtf8(tempBuf, len, result, destlen);
+ result[destlen] = 0;
+ }
+ }
+
+ if (needs_free)
+ mir_free(tempBuf);
+
+ return result;
+}
+
+MIR_CORE_DLL(char*) mir_utf8encode(const char* src)
+{
+ return mir_utf8encodecp(src, Langpack_GetDefaultCodePage());
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// mir_utf8encode - converts UCS2 string to the UTF8-encoded format
+
+MIR_CORE_DLL(char*) mir_utf8encodeW(const wchar_t* src)
+{
+ if (src == nullptr)
+ return nullptr;
+
+ int len = (int)wcslen(src);
+
+ int destlen = mir_utf8len(src, len);
+ if (destlen < 0) return nullptr;
+
+ char* result = (char*)mir_alloc(destlen + 1);
+ if (result == nullptr)
+ return nullptr;
+
+ Ucs2toUtf8(src, len, result, destlen);
+ result[destlen] = 0;
+
+ return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Utf8CheckString - checks if a string is a valid utf8-encoded string
+
+MIR_CORE_DLL(BOOL) Utf8CheckString(const char *str)
+{
+ int expect_bytes = 0, utf_found = 0;
+
+ if (!str) return 0;
+
+ while (*str) {
+ if ((*str & 0x80) == 0) {
+ /* Looks like an ASCII character */
+ if (expect_bytes)
+ /* byte of UTF-8 character expected */
+ return 0;
+ }
+ else {
+ /* Looks like byte of an UTF-8 character */
+ if (expect_bytes) {
+ /* expect_bytes already set: first byte of UTF-8 char already seen */
+ if ((*str & 0xC0) != 0x80) {
+ /* again first byte ?!?! */
+ return 0;
+ }
+ }
+ else {
+ /* First byte of the UTF-8 character */
+ /* count initial one bits and set expect_bytes to 1 less */
+ char ch = *str;
+ while (ch & 0x80) {
+ expect_bytes++;
+ ch = (ch & 0x7f) << 1;
+ }
+ }
+ /* OK, next byte of UTF-8 character */
+ /* Decrement number of expected bytes */
+ if (--expect_bytes == 0)
+ utf_found = 1;
+ }
+ str++;
+ }
+
+ return (utf_found && expect_bytes == 0);
+}
diff --git a/src/mir_core/src/utils.cpp b/src/mir_core/src/utils.cpp index b6953a027a..07613f6394 100644 --- a/src/mir_core/src/utils.cpp +++ b/src/mir_core/src/utils.cpp @@ -1,570 +1,570 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -MIR_CORE_DLL(char*) replaceStr(char* &dest, const char *src) -{ - if (dest != nullptr) - mir_free(dest); - - return dest = (src != nullptr) ? mir_strdup(src) : nullptr; -} - -MIR_CORE_DLL(wchar_t*) replaceStrW(wchar_t* &dest, const wchar_t *src) -{ - if (dest != nullptr) - mir_free(dest); - - return dest = (src != nullptr) ? mir_wstrdup(src) : nullptr; -} - -MIR_CORE_DLL(char*) rtrim(char *str) -{ - if (str == nullptr) - return nullptr; - - char* p = strchr(str, 0); - while (--p >= str) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - *p = 0; break; - default: - return str; - } - } - return str; -} - -MIR_CORE_DLL(wchar_t*) rtrimw(wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = wcschr(str, 0); - while (--p >= str) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - *p = 0; break; - default: - return str; - } - } - return str; -} - -MIR_CORE_DLL(char*) ltrim(char *str) -{ - if (str == nullptr) - return nullptr; - - char* p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - memmove(str, p, strlen(p) + 1); - return str; - } - } -} - -MIR_CORE_DLL(wchar_t*) ltrimw(wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - memmove(str, p, sizeof(wchar_t)*(wcslen(p) + 1)); - return str; - } - } -} - -MIR_CORE_DLL(char*) ltrimp(char *str) -{ - if (str == nullptr) - return nullptr; - - char *p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - return p; - } - } -} - -MIR_CORE_DLL(wchar_t*) ltrimpw(wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - return p; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(char*) strdel(char *str, size_t len) -{ - char* p; - for (p = str + len; *p != 0; p++) - *(p - len) = *p; - - *(p - len) = '\0'; - return str; -} - -MIR_CORE_DLL(wchar_t*) strdelw(wchar_t *str, size_t len) -{ - wchar_t* p; - for (p = str + len; *p != 0; p++) - *(p - len) = *p; - - *(p - len) = '\0'; - return str; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) wildcmp(const char *name, const char *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const char *last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && *mask != *name) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -MIR_CORE_DLL(int) wildcmpw(const wchar_t *name, const wchar_t *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const wchar_t* last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && *mask != *name) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -#define _qtoupper(_c) (((_c) >= 'a' && (_c) <= 'z')?((_c)-'a'+'A'):(_c)) - -MIR_CORE_DLL(int) wildcmpi(const char *name, const char *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const char *last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -MIR_CORE_DLL(int) wildcmpiw(const wchar_t *name, const wchar_t *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const wchar_t* last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static char szHexTable[] = "0123456789abcdef"; - -MIR_CORE_DLL(char*) bin2hex(const void *pData, size_t len, char *dest) -{ - const uint8_t *p = (const uint8_t*)pData; - char *d = dest; - - for (size_t i = 0; i < len; i++, p++) { - *d++ = szHexTable[*p >> 4]; - *d++ = szHexTable[*p & 0x0F]; - } - *d = 0; - - return dest; -} - -MIR_CORE_DLL(wchar_t*) bin2hexW(const void *pData, size_t len, wchar_t *dest) -{ - const uint8_t *p = (const uint8_t*)pData; - wchar_t *d = dest; - - for (size_t i = 0; i < len; i++, p++) { - *d++ = szHexTable[*p >> 4]; - *d++ = szHexTable[*p & 0x0F]; - } - *d = 0; - - return dest; -} - -static int hex2dec(int iHex) -{ - if (iHex >= '0' && iHex <= '9') - return iHex - '0'; - if (iHex >= 'a' && iHex <= 'f') - return iHex - 'a' + 10; - if (iHex >= 'A' && iHex <= 'F') - return iHex - 'A' + 10; - return 0; -} - -MIR_CORE_DLL(bool) hex2bin(const char *pSrc, void *pData, size_t len) -{ - if (pSrc == nullptr || pData == nullptr || len == 0) - return false; - - size_t bufLen = strlen(pSrc)/2; - if (pSrc[bufLen*2] != 0 || bufLen > len) - return false; - - uint8_t *pDest = (uint8_t*)pData; - const char *p = (const char *)pSrc; - for (size_t i = 0; i < bufLen; i++, p += 2) - pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]); - - if (bufLen < len) - memset(pDest + bufLen, 0, len - bufLen); - return true; -} - -MIR_CORE_DLL(bool) hex2binW(const wchar_t *pSrc, void *pData, size_t len) -{ - if (pSrc == nullptr || pData == nullptr || len == 0) - return false; - - size_t bufLen = wcslen(pSrc)/2; - if (pSrc[bufLen * 2] != 0 || bufLen > len) - return false; - - uint8_t *pDest = (uint8_t*)pData; - const wchar_t *p = (const wchar_t *)pSrc; - for (size_t i = 0; i < bufLen; i++, p += 2) - pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]); - - if (bufLen < len) - memset(pDest+bufLen, 0, len - bufLen); - return true; -} - - -///////////////////////////////////////////////////////////////////////////////////////// - -#pragma intrinsic(strlen, strcpy, strcat, strcmp, wcslen, wcscpy, wcscat, wcscmp) - -MIR_CORE_DLL(size_t) mir_strlen(const char *p) -{ - return (p) ? strlen(p) : 0; -} - -MIR_CORE_DLL(size_t) mir_wstrlen(const wchar_t *p) -{ - return (p) ? wcslen(p) : 0; -} - -MIR_CORE_DLL(char*) mir_strcpy(char *dest, const char *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return strcpy(dest, src); -} - -MIR_CORE_DLL(wchar_t*) mir_wstrcpy(wchar_t *dest, const wchar_t *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return wcscpy(dest, src); -} - -MIR_CORE_DLL(char*) mir_strncpy(char *dest, const char *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - strncpy_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrncpy(wchar_t *dest, const wchar_t *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - wcsncpy_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(char*) mir_strcat(char *dest, const char *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return strcat(dest, src); -} - -MIR_CORE_DLL(wchar_t*) mir_wstrcat(wchar_t *dest, const wchar_t *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return wcscat(dest, src); -} - -MIR_CORE_DLL(char*) mir_strncat(char *dest, const char *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - strncat_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrncat(wchar_t *dest, const wchar_t *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - wcsncat_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(int) mir_strcmp(const char *p1, const char *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return strcmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_wstrcmp(const wchar_t *p1, const wchar_t *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcscmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_strcmpi(const char *p1, const char *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return stricmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_wstrcmpi(const wchar_t *p1, const wchar_t *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcsicmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_strncmp(const char *p1, const char *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return strncmp(p1, p2, n); -} - -MIR_CORE_DLL(int) mir_wstrncmp(const wchar_t *p1, const wchar_t *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcsncmp(p1, p2, n); -} - -MIR_CORE_DLL(int) mir_strncmpi(const char *p1, const char *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return strnicmp(p1, p2, n); -} - -MIR_CORE_DLL(int) mir_wstrncmpi(const wchar_t *p1, const wchar_t *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcsnicmp(p1, p2, n); -} - -#ifdef _MSC_VER -MIR_CORE_DLL(const wchar_t*) mir_wstrstri(const wchar_t *s1, const wchar_t *s2) -{ - for (int i = 0; s1[i]; i++) - for (int j = i, k = 0; towlower(s1[j]) == towlower(s2[k]); j++, k++) - if (!s2[k + 1]) - return s1 + i; - - return nullptr; -} -#endif - -///////////////////////////////////////////////////////////////////////////////////////// - -#ifdef _MSC_VER - PGENRANDOM pfnRtlGenRandom; -#endif - -MIR_CORE_DLL(void) Utils_GetRandom(void *pszDest, size_t cbLen) -{ - if (pszDest == nullptr || cbLen == 0) - return; - - #ifdef _MSC_VER - if (pfnRtlGenRandom != nullptr) { - pfnRtlGenRandom(pszDest, (uint32_t)cbLen); - return; - } - #endif - - srand(time(0)); - uint8_t *p = (uint8_t*)pszDest; - for (size_t i = 0; i < cbLen; i++) - p[i] = rand() & 0xFF; -} - -MIR_CORE_DLL(bool) Utils_IsRtl(const wchar_t *pszwText) -{ - #ifdef _MSC_VER - size_t iLen = mir_wstrlen(pszwText); - mir_ptr<uint16_t> infoTypeC2((uint16_t*)mir_calloc(sizeof(uint16_t) * (iLen + 2))); - GetStringTypeW(CT_CTYPE2, pszwText, (int)iLen, infoTypeC2); - - for (size_t i = 0; i < iLen; i++) - if (infoTypeC2[i] == C2_RIGHTTOLEFT) - return true; - #endif - - return false; -} +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+MIR_CORE_DLL(char*) replaceStr(char* &dest, const char *src)
+{
+ if (dest != nullptr)
+ mir_free(dest);
+
+ return dest = (src != nullptr) ? mir_strdup(src) : nullptr;
+}
+
+MIR_CORE_DLL(wchar_t*) replaceStrW(wchar_t* &dest, const wchar_t *src)
+{
+ if (dest != nullptr)
+ mir_free(dest);
+
+ return dest = (src != nullptr) ? mir_wstrdup(src) : nullptr;
+}
+
+MIR_CORE_DLL(char*) rtrim(char *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ char* p = strchr(str, 0);
+ while (--p >= str) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ *p = 0; break;
+ default:
+ return str;
+ }
+ }
+ return str;
+}
+
+MIR_CORE_DLL(wchar_t*) rtrimw(wchar_t *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ wchar_t *p = wcschr(str, 0);
+ while (--p >= str) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ *p = 0; break;
+ default:
+ return str;
+ }
+ }
+ return str;
+}
+
+MIR_CORE_DLL(char*) ltrim(char *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ char* p = str;
+ for (;;) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ ++p; break;
+ default:
+ memmove(str, p, strlen(p) + 1);
+ return str;
+ }
+ }
+}
+
+MIR_CORE_DLL(wchar_t*) ltrimw(wchar_t *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ wchar_t *p = str;
+ for (;;) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ ++p; break;
+ default:
+ memmove(str, p, sizeof(wchar_t)*(wcslen(p) + 1));
+ return str;
+ }
+ }
+}
+
+MIR_CORE_DLL(char*) ltrimp(char *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ char *p = str;
+ for (;;) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ ++p; break;
+ default:
+ return p;
+ }
+ }
+}
+
+MIR_CORE_DLL(wchar_t*) ltrimpw(wchar_t *str)
+{
+ if (str == nullptr)
+ return nullptr;
+
+ wchar_t *p = str;
+ for (;;) {
+ switch (*p) {
+ case ' ': case '\t': case '\n': case '\r':
+ ++p; break;
+ default:
+ return p;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(char*) strdel(char *str, size_t len)
+{
+ char* p;
+ for (p = str + len; *p != 0; p++)
+ *(p - len) = *p;
+
+ *(p - len) = '\0';
+ return str;
+}
+
+MIR_CORE_DLL(wchar_t*) strdelw(wchar_t *str, size_t len)
+{
+ wchar_t* p;
+ for (p = str + len; *p != 0; p++)
+ *(p - len) = *p;
+
+ *(p - len) = '\0';
+ return str;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(int) wildcmp(const char *name, const char *mask)
+{
+ if (name == nullptr || mask == nullptr)
+ return false;
+
+ const char *last = nullptr;
+ for (;; mask++, name++) {
+ if (*mask != '?' && *mask != *name) break;
+ if (*name == '\0') return ((BOOL)!*mask);
+ }
+ if (*mask != '*') return FALSE;
+ for (;; mask++, name++) {
+ while (*mask == '*') {
+ last = mask++;
+ if (*mask == '\0') return ((BOOL)!*mask); /* true */
+ }
+ if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */
+ if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last;
+ }
+}
+
+MIR_CORE_DLL(int) wildcmpw(const wchar_t *name, const wchar_t *mask)
+{
+ if (name == nullptr || mask == nullptr)
+ return false;
+
+ const wchar_t* last = nullptr;
+ for (;; mask++, name++) {
+ if (*mask != '?' && *mask != *name) break;
+ if (*name == '\0') return ((BOOL)!*mask);
+ }
+ if (*mask != '*') return FALSE;
+ for (;; mask++, name++) {
+ while (*mask == '*') {
+ last = mask++;
+ if (*mask == '\0') return ((BOOL)!*mask); /* true */
+ }
+ if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */
+ if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last;
+ }
+}
+
+#define _qtoupper(_c) (((_c) >= 'a' && (_c) <= 'z')?((_c)-'a'+'A'):(_c))
+
+MIR_CORE_DLL(int) wildcmpi(const char *name, const char *mask)
+{
+ if (name == nullptr || mask == nullptr)
+ return false;
+
+ const char *last = nullptr;
+ for (;; mask++, name++) {
+ if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break;
+ if (*name == '\0') return ((BOOL)!*mask);
+ }
+ if (*mask != '*') return FALSE;
+ for (;; mask++, name++) {
+ while (*mask == '*') {
+ last = mask++;
+ if (*mask == '\0') return ((BOOL)!*mask); /* true */
+ }
+ if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */
+ if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last;
+ }
+}
+
+MIR_CORE_DLL(int) wildcmpiw(const wchar_t *name, const wchar_t *mask)
+{
+ if (name == nullptr || mask == nullptr)
+ return false;
+
+ const wchar_t* last = nullptr;
+ for (;; mask++, name++) {
+ if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break;
+ if (*name == '\0') return ((BOOL)!*mask);
+ }
+ if (*mask != '*') return FALSE;
+ for (;; mask++, name++) {
+ while (*mask == '*') {
+ last = mask++;
+ if (*mask == '\0') return ((BOOL)!*mask); /* true */
+ }
+ if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */
+ if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static char szHexTable[] = "0123456789abcdef";
+
+MIR_CORE_DLL(char*) bin2hex(const void *pData, size_t len, char *dest)
+{
+ const uint8_t *p = (const uint8_t*)pData;
+ char *d = dest;
+
+ for (size_t i = 0; i < len; i++, p++) {
+ *d++ = szHexTable[*p >> 4];
+ *d++ = szHexTable[*p & 0x0F];
+ }
+ *d = 0;
+
+ return dest;
+}
+
+MIR_CORE_DLL(wchar_t*) bin2hexW(const void *pData, size_t len, wchar_t *dest)
+{
+ const uint8_t *p = (const uint8_t*)pData;
+ wchar_t *d = dest;
+
+ for (size_t i = 0; i < len; i++, p++) {
+ *d++ = szHexTable[*p >> 4];
+ *d++ = szHexTable[*p & 0x0F];
+ }
+ *d = 0;
+
+ return dest;
+}
+
+static int hex2dec(int iHex)
+{
+ if (iHex >= '0' && iHex <= '9')
+ return iHex - '0';
+ if (iHex >= 'a' && iHex <= 'f')
+ return iHex - 'a' + 10;
+ if (iHex >= 'A' && iHex <= 'F')
+ return iHex - 'A' + 10;
+ return 0;
+}
+
+MIR_CORE_DLL(bool) hex2bin(const char *pSrc, void *pData, size_t len)
+{
+ if (pSrc == nullptr || pData == nullptr || len == 0)
+ return false;
+
+ size_t bufLen = strlen(pSrc)/2;
+ if (pSrc[bufLen*2] != 0 || bufLen > len)
+ return false;
+
+ uint8_t *pDest = (uint8_t*)pData;
+ const char *p = (const char *)pSrc;
+ for (size_t i = 0; i < bufLen; i++, p += 2)
+ pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]);
+
+ if (bufLen < len)
+ memset(pDest + bufLen, 0, len - bufLen);
+ return true;
+}
+
+MIR_CORE_DLL(bool) hex2binW(const wchar_t *pSrc, void *pData, size_t len)
+{
+ if (pSrc == nullptr || pData == nullptr || len == 0)
+ return false;
+
+ size_t bufLen = wcslen(pSrc)/2;
+ if (pSrc[bufLen * 2] != 0 || bufLen > len)
+ return false;
+
+ uint8_t *pDest = (uint8_t*)pData;
+ const wchar_t *p = (const wchar_t *)pSrc;
+ for (size_t i = 0; i < bufLen; i++, p += 2)
+ pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]);
+
+ if (bufLen < len)
+ memset(pDest+bufLen, 0, len - bufLen);
+ return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#pragma intrinsic(strlen, strcpy, strcat, strcmp, wcslen, wcscpy, wcscat, wcscmp)
+
+MIR_CORE_DLL(size_t) mir_strlen(const char *p)
+{
+ return (p) ? strlen(p) : 0;
+}
+
+MIR_CORE_DLL(size_t) mir_wstrlen(const wchar_t *p)
+{
+ return (p) ? wcslen(p) : 0;
+}
+
+MIR_CORE_DLL(char*) mir_strcpy(char *dest, const char *src)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr) {
+ *dest = 0;
+ return dest;
+ }
+
+ return strcpy(dest, src);
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrcpy(wchar_t *dest, const wchar_t *src)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr) {
+ *dest = 0;
+ return dest;
+ }
+
+ return wcscpy(dest, src);
+}
+
+MIR_CORE_DLL(char*) mir_strncpy(char *dest, const char *src, size_t len)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr)
+ *dest = 0;
+ else
+ strncpy_s(dest, len, src, _TRUNCATE);
+ return dest;
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrncpy(wchar_t *dest, const wchar_t *src, size_t len)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr)
+ *dest = 0;
+ else
+ wcsncpy_s(dest, len, src, _TRUNCATE);
+ return dest;
+}
+
+MIR_CORE_DLL(char*) mir_strcat(char *dest, const char *src)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr) {
+ *dest = 0;
+ return dest;
+ }
+
+ return strcat(dest, src);
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrcat(wchar_t *dest, const wchar_t *src)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr) {
+ *dest = 0;
+ return dest;
+ }
+
+ return wcscat(dest, src);
+}
+
+MIR_CORE_DLL(char*) mir_strncat(char *dest, const char *src, size_t len)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr)
+ *dest = 0;
+ else
+ strncat_s(dest, len, src, _TRUNCATE);
+ return dest;
+}
+
+MIR_CORE_DLL(wchar_t*) mir_wstrncat(wchar_t *dest, const wchar_t *src, size_t len)
+{
+ if (dest == nullptr)
+ return nullptr;
+
+ if (src == nullptr)
+ *dest = 0;
+ else
+ wcsncat_s(dest, len, src, _TRUNCATE);
+ return dest;
+}
+
+MIR_CORE_DLL(int) mir_strcmp(const char *p1, const char *p2)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return strcmp(p1, p2);
+}
+
+MIR_CORE_DLL(int) mir_wstrcmp(const wchar_t *p1, const wchar_t *p2)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return wcscmp(p1, p2);
+}
+
+MIR_CORE_DLL(int) mir_strcmpi(const char *p1, const char *p2)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return stricmp(p1, p2);
+}
+
+MIR_CORE_DLL(int) mir_wstrcmpi(const wchar_t *p1, const wchar_t *p2)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return wcsicmp(p1, p2);
+}
+
+MIR_CORE_DLL(int) mir_strncmp(const char *p1, const char *p2, size_t n)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return strncmp(p1, p2, n);
+}
+
+MIR_CORE_DLL(int) mir_wstrncmp(const wchar_t *p1, const wchar_t *p2, size_t n)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return wcsncmp(p1, p2, n);
+}
+
+MIR_CORE_DLL(int) mir_strncmpi(const char *p1, const char *p2, size_t n)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return strnicmp(p1, p2, n);
+}
+
+MIR_CORE_DLL(int) mir_wstrncmpi(const wchar_t *p1, const wchar_t *p2, size_t n)
+{
+ if (p1 == nullptr)
+ return (p2 == nullptr) ? 0 : -1;
+ if (p2 == nullptr)
+ return 1;
+ return wcsnicmp(p1, p2, n);
+}
+
+#ifdef _MSC_VER
+MIR_CORE_DLL(const wchar_t*) mir_wstrstri(const wchar_t *s1, const wchar_t *s2)
+{
+ for (int i = 0; s1[i]; i++)
+ for (int j = i, k = 0; towlower(s1[j]) == towlower(s2[k]); j++, k++)
+ if (!s2[k + 1])
+ return s1 + i;
+
+ return nullptr;
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef _MSC_VER
+ PGENRANDOM pfnRtlGenRandom;
+#endif
+
+MIR_CORE_DLL(void) Utils_GetRandom(void *pszDest, size_t cbLen)
+{
+ if (pszDest == nullptr || cbLen == 0)
+ return;
+
+ #ifdef _MSC_VER
+ if (pfnRtlGenRandom != nullptr) {
+ pfnRtlGenRandom(pszDest, (uint32_t)cbLen);
+ return;
+ }
+ #endif
+
+ srand(time(0));
+ uint8_t *p = (uint8_t*)pszDest;
+ for (size_t i = 0; i < cbLen; i++)
+ p[i] = rand() & 0xFF;
+}
+
+MIR_CORE_DLL(bool) Utils_IsRtl(const wchar_t *pszwText)
+{
+ #ifdef _MSC_VER
+ size_t iLen = mir_wstrlen(pszwText);
+ mir_ptr<uint16_t> infoTypeC2((uint16_t*)mir_calloc(sizeof(uint16_t) * (iLen + 2)));
+ GetStringTypeW(CT_CTYPE2, pszwText, (int)iLen, infoTypeC2);
+
+ for (size_t i = 0; i < iLen; i++)
+ if (infoTypeC2[i] == C2_RIGHTTOLEFT)
+ return true;
+ #endif
+
+ return false;
+}
diff --git a/src/miranda32/res/version.rc b/src/miranda32/res/version.rc index e7ba01cf8e..7106e0debb 100644 --- a/src/miranda32/res/version.rc +++ b/src/miranda32/res/version.rc @@ -38,7 +38,7 @@ BEGIN VALUE "FileDescription", "Miranda NG\0"
VALUE "FileVersion", MIRANDA_VERSION_DISPLAY
VALUE "InternalName", "miranda32\0"
- VALUE "LegalCopyright", "Copyright © 2000-11 Miranda IM, 2012-22 Miranda NG team. This software is released under the terms of the GNU General Public License.\0"
+ VALUE "LegalCopyright", "Copyright © 2000-11 Miranda IM, 2012-23 Miranda NG team. This software is released under the terms of the GNU General Public License.\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "miranda32.exe\0"
VALUE "PrivateBuild", "\0"
diff --git a/src/miranda32/src/miranda.cpp b/src/miranda32/src/miranda.cpp index 59e5dde9df..59fd3cf184 100644 --- a/src/miranda32/src/miranda.cpp +++ b/src/miranda32/src/miranda.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/src/miranda32/src/stdafx.cxx b/src/miranda32/src/stdafx.cxx index 52ec2d6817..e23069a5b8 100644 --- a/src/miranda32/src/stdafx.cxx +++ b/src/miranda32/src/stdafx.cxx @@ -1,6 +1,6 @@ /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/src/miranda32/src/stdafx.h b/src/miranda32/src/stdafx.h index 96509b59f8..986030ab1f 100644 --- a/src/miranda32/src/stdafx.h +++ b/src/miranda32/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
|