summaryrefslogtreecommitdiff
path: root/plugins/NewStory/src
diff options
context:
space:
mode:
authordartraiden <wowemuh@gmail.com>2024-02-19 20:13:12 +0300
committerdartraiden <wowemuh@gmail.com>2024-02-19 20:16:52 +0300
commit2f47988d1ef6672929762fba39fa69ef1b4dfa7a (patch)
treebaa3433b11b1c44ab5a12c919ab004a24d645043 /plugins/NewStory/src
parentf50885f33f7d1e129d719f06a43cc73518e0ffb1 (diff)
NewStory:
- add [cX] tag to variables help - convert sources to CR+LF
Diffstat (limited to 'plugins/NewStory/src')
-rw-r--r--plugins/NewStory/src/calendartool.cpp128
-rw-r--r--plugins/NewStory/src/fonts.cpp264
-rw-r--r--plugins/NewStory/src/fonts.h114
-rw-r--r--plugins/NewStory/src/history.h84
-rw-r--r--plugins/NewStory/src/history_array.cpp1440
-rw-r--r--plugins/NewStory/src/history_array.h328
-rw-r--r--plugins/NewStory/src/history_control.cpp3082
-rw-r--r--plugins/NewStory/src/history_control.h196
-rw-r--r--plugins/NewStory/src/history_dlg.cpp2446
-rw-r--r--plugins/NewStory/src/history_log.cpp232
-rw-r--r--plugins/NewStory/src/history_menus.cpp602
-rw-r--r--plugins/NewStory/src/history_svc.cpp124
-rw-r--r--plugins/NewStory/src/main.cpp406
-rw-r--r--plugins/NewStory/src/options.cpp669
-rw-r--r--plugins/NewStory/src/resource.h257
-rw-r--r--plugins/NewStory/src/stdafx.h236
-rw-r--r--plugins/NewStory/src/templates.cpp946
-rw-r--r--plugins/NewStory/src/templates.h144
-rw-r--r--plugins/NewStory/src/utils.cpp227
-rw-r--r--plugins/NewStory/src/utils.h24
20 files changed, 5968 insertions, 5981 deletions
diff --git a/plugins/NewStory/src/calendartool.cpp b/plugins/NewStory/src/calendartool.cpp
index ab158c10bd..d7c3a4bb27 100644
--- a/plugins/NewStory/src/calendartool.cpp
+++ b/plugins/NewStory/src/calendartool.cpp
@@ -1,64 +1,64 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
-
-INT_PTR CALLBACK CalendarToolDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch (msg) {
- case WM_INITDIALOG:
- // This causes ALL miranda dialogs to have drop-shadow enabled. That's bad =(
- // SetClassLong(hwnd, GCL_STYLE, GetClassLong(hwnd, GCL_STYLE)|CS_DROPSHADOW);
- SetWindowPos(hwnd, HWND_TOP, LOWORD(lParam), HIWORD(lParam), 0, 0, SWP_NOSIZE);
- return TRUE;
-
- case WM_ACTIVATE:
- if (wParam == WA_INACTIVE)
- PostMessage(hwnd, WM_CLOSE, 0, 0);
- break;
-
- case WM_NOTIFY:
- {
- LPNMHDR hdr = (LPNMHDR)lParam;
- if ((hdr->idFrom == IDC_MONTHCALENDAR) && (hdr->code == MCN_SELECT)) {
- LPNMSELCHANGE lpnmsc = (LPNMSELCHANGE)lParam;
- struct tm tm_sel;
- tm_sel.tm_hour = tm_sel.tm_min = tm_sel.tm_sec = 0;
- tm_sel.tm_isdst = 1;
- tm_sel.tm_mday = lpnmsc->stSelStart.wDay;
- tm_sel.tm_mon = lpnmsc->stSelStart.wMonth - 1;
- tm_sel.tm_year = lpnmsc->stSelStart.wYear - 1900;
- PostMessage(GetParent(hwnd), WM_USER + 0x600, mktime(&tm_sel), 0);
- EndDialog(hwnd, 0);
- }
- }
- return TRUE;
-
- case WM_CLOSE:
- DestroyWindow(hwnd);
- return TRUE;
- }
- return FALSE;
-}
-
-time_t CalendarTool_Show(HWND hwnd, int x, int y)
-{
- HWND hwndCalendar = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_CALENDARTOOL), hwnd, CalendarToolDlgProc, MAKELONG(x, y));
- ShowWindow(hwndCalendar, SW_SHOW);
- return 0;
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
+
+INT_PTR CALLBACK CalendarToolDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ // This causes ALL miranda dialogs to have drop-shadow enabled. That's bad =(
+ // SetClassLong(hwnd, GCL_STYLE, GetClassLong(hwnd, GCL_STYLE)|CS_DROPSHADOW);
+ SetWindowPos(hwnd, HWND_TOP, LOWORD(lParam), HIWORD(lParam), 0, 0, SWP_NOSIZE);
+ return TRUE;
+
+ case WM_ACTIVATE:
+ if (wParam == WA_INACTIVE)
+ PostMessage(hwnd, WM_CLOSE, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR hdr = (LPNMHDR)lParam;
+ if ((hdr->idFrom == IDC_MONTHCALENDAR) && (hdr->code == MCN_SELECT)) {
+ LPNMSELCHANGE lpnmsc = (LPNMSELCHANGE)lParam;
+ struct tm tm_sel;
+ tm_sel.tm_hour = tm_sel.tm_min = tm_sel.tm_sec = 0;
+ tm_sel.tm_isdst = 1;
+ tm_sel.tm_mday = lpnmsc->stSelStart.wDay;
+ tm_sel.tm_mon = lpnmsc->stSelStart.wMonth - 1;
+ tm_sel.tm_year = lpnmsc->stSelStart.wYear - 1900;
+ PostMessage(GetParent(hwnd), WM_USER + 0x600, mktime(&tm_sel), 0);
+ EndDialog(hwnd, 0);
+ }
+ }
+ return TRUE;
+
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+time_t CalendarTool_Show(HWND hwnd, int x, int y)
+{
+ HWND hwndCalendar = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_CALENDARTOOL), hwnd, CalendarToolDlgProc, MAKELONG(x, y));
+ ShowWindow(hwndCalendar, SW_SHOW);
+ return 0;
+}
diff --git a/plugins/NewStory/src/fonts.cpp b/plugins/NewStory/src/fonts.cpp
index 844f5d0f62..a85652fdb1 100644
--- a/plugins/NewStory/src/fonts.cpp
+++ b/plugins/NewStory/src/fonts.cpp
@@ -1,132 +1,132 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
-
-MyColourID g_colorTable[COLOR_COUNT] =
-{
- { LPGEN("Incoming name"), "ColorNickIn", RGB(0x00, 0x00, 0x00) },
- { LPGEN("Outgoing name"), "ColorNickOut", RGB(0x00, 0x00, 0x00) },
-
- { LPGEN("Incoming messages"), "ColorMsgIn", RGB(0xd6, 0xf5, 0xc0) },
- { LPGEN("Outgoing messages"), "ColorMsgOut", RGB(0xf5, 0xe7, 0xd8) },
-
- { LPGEN("Incoming files"), "ColorFileIn", RGB(0xe3, 0xee, 0x9b) },
- { LPGEN("Outgoing files"), "ColorFileOut", RGB(0xe3, 0xee, 0x9b) },
-
- { LPGEN("Status changes"), "ColorStatus", RGB(0xf0, 0xf0, 0xf0) },
-
- { LPGEN("Other incoming events"), "ColorIn", RGB(0xff, 0xff, 0xff) },
- { LPGEN("Other outgoing events"), "ColorOut", RGB(0xff, 0xff, 0xff) },
-
- { LPGEN("Selected item's text"), "ColorSelTxt", RGB(0xff, 0xff, 0xff) },
- { LPGEN("Selected item's background"), "ColorSel", GetSysColor(COLOR_HIGHLIGHT) },
- { LPGEN("Selected item's frame"), "ColorSelFrm", GetSysColor(COLOR_HIGHLIGHTTEXT) },
-
- { LPGEN("Highlighted messages"), "ColorHighlight", RGB(0xf0, 0xf0, 0xf0) },
- { LPGEN("Grid background"), "Background", RGB(0xff, 0xff, 0xff) },
- { LPGEN("Separator"), "Separator", RGB(0x60, 0x60, 0x60) },
- { LPGEN("Progress indicator"), "Progress", RGB(0xff, 0x00, 0x00) },
-};
-
-MyFontID g_fontTable[FONT_COUNT] =
-{
- { LPGEN("Incoming messages"), "FontMsgIn" },
- { LPGEN("Outgoing messages"), "FontMsgOut" },
-
- { LPGEN("Incoming files"), "FontFileIn" },
- { LPGEN("Outgoing files"), "FontFileOut" },
-
- { LPGEN("Status changes"), "FontStatus" },
- { LPGEN("Highlighted messages"), "FontHighlight", DBFONTF_BOLD, RGB(0x7f, 0, 0) },
-
- { LPGEN("Other incoming events"), "FontIn" },
- { LPGEN("Other outgoing events"), "FontOut" },
-};
-
-int evtFontsChanged(WPARAM, LPARAM)
-{
- for (auto &it : g_colorTable)
- it.cl = Colour_Get(MODULENAME, it.szName);
-
- DeleteObject(g_plugin.hBackBrush);
- g_plugin.hBackBrush = CreateSolidBrush(g_colorTable[COLOR_SELBACK].cl);
-
- for (auto &it : g_fontTable) {
- it.cl = (COLORREF)Font_Get(MODULENAME, it.szName, &it.lf);
-
- DeleteObject(it.hfnt);
- it.hfnt = CreateFontIndirectA(&it.lf);
- }
-
- WindowList_Broadcast(g_hNewstoryWindows, UM_REDRAWLISTH, 0, 0);
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void InitFonts()
-{
- HookEvent(ME_FONT_RELOAD, evtFontsChanged);
- HookEvent(ME_COLOUR_RELOAD, evtFontsChanged);
-
- ColourID cid = {};
- strncpy_s(cid.group, MODULENAME, _TRUNCATE);
- strncpy_s(cid.dbSettingsGroup, MODULENAME, _TRUNCATE);
-
- for (auto &it : g_colorTable) {
- cid.order = int(&it - g_colorTable);
- strncpy_s(cid.name, it.szName, _TRUNCATE);
- strncpy_s(cid.setting, it.szSetting, _TRUNCATE);
- cid.defcolour = it.defaultValue;
- g_plugin.addColor(&cid);
- it.cl = Colour_Get(cid.group, cid.name);
- }
-
- g_plugin.hBackBrush = CreateSolidBrush(g_colorTable[COLOR_SELBACK].cl);
-
- LOGFONT lfDefault;
- SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfDefault), &lfDefault, FALSE);
-
- FontID fontid = {};
- fontid.flags = FIDF_DEFAULTVALID;
- strncpy_s(fontid.group, MODULENAME, _TRUNCATE);
- strncpy_s(fontid.dbSettingsGroup, MODULENAME, _TRUNCATE);
- strncpy_s(fontid.deffontsettings.szFace, _T2A(lfDefault.lfFaceName), _TRUNCATE);
- fontid.deffontsettings.size = -12;
-
- for (auto &it : g_fontTable) {
- fontid.order = int(&it - g_fontTable);
- strncpy_s(fontid.name, it.szName, _TRUNCATE);
- strncpy_s(fontid.setting, it.szSetting, _TRUNCATE);
- fontid.deffontsettings.style = it.style;
- fontid.deffontsettings.colour = it.defaultValue;
- g_plugin.addFont(&fontid);
-
- it.cl = Font_Get(MODULENAME, it.szName, &it.lf);
- it.hfnt = CreateFontIndirectA(&it.lf);
- }
-}
-
-void DestroyFonts()
-{
- DeleteObject(g_plugin.hBackBrush);
-
- for (auto &it : g_fontTable)
- DeleteObject(it.hfnt);
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
+
+MyColourID g_colorTable[COLOR_COUNT] =
+{
+ { LPGEN("Incoming name"), "ColorNickIn", RGB(0x00, 0x00, 0x00) },
+ { LPGEN("Outgoing name"), "ColorNickOut", RGB(0x00, 0x00, 0x00) },
+
+ { LPGEN("Incoming messages"), "ColorMsgIn", RGB(0xd6, 0xf5, 0xc0) },
+ { LPGEN("Outgoing messages"), "ColorMsgOut", RGB(0xf5, 0xe7, 0xd8) },
+
+ { LPGEN("Incoming files"), "ColorFileIn", RGB(0xe3, 0xee, 0x9b) },
+ { LPGEN("Outgoing files"), "ColorFileOut", RGB(0xe3, 0xee, 0x9b) },
+
+ { LPGEN("Status changes"), "ColorStatus", RGB(0xf0, 0xf0, 0xf0) },
+
+ { LPGEN("Other incoming events"), "ColorIn", RGB(0xff, 0xff, 0xff) },
+ { LPGEN("Other outgoing events"), "ColorOut", RGB(0xff, 0xff, 0xff) },
+
+ { LPGEN("Selected item's text"), "ColorSelTxt", RGB(0xff, 0xff, 0xff) },
+ { LPGEN("Selected item's background"), "ColorSel", GetSysColor(COLOR_HIGHLIGHT) },
+ { LPGEN("Selected item's frame"), "ColorSelFrm", GetSysColor(COLOR_HIGHLIGHTTEXT) },
+
+ { LPGEN("Highlighted messages"), "ColorHighlight", RGB(0xf0, 0xf0, 0xf0) },
+ { LPGEN("Grid background"), "Background", RGB(0xff, 0xff, 0xff) },
+ { LPGEN("Separator"), "Separator", RGB(0x60, 0x60, 0x60) },
+ { LPGEN("Progress indicator"), "Progress", RGB(0xff, 0x00, 0x00) },
+};
+
+MyFontID g_fontTable[FONT_COUNT] =
+{
+ { LPGEN("Incoming messages"), "FontMsgIn" },
+ { LPGEN("Outgoing messages"), "FontMsgOut" },
+
+ { LPGEN("Incoming files"), "FontFileIn" },
+ { LPGEN("Outgoing files"), "FontFileOut" },
+
+ { LPGEN("Status changes"), "FontStatus" },
+ { LPGEN("Highlighted messages"), "FontHighlight", DBFONTF_BOLD, RGB(0x7f, 0, 0) },
+
+ { LPGEN("Other incoming events"), "FontIn" },
+ { LPGEN("Other outgoing events"), "FontOut" },
+};
+
+int evtFontsChanged(WPARAM, LPARAM)
+{
+ for (auto &it : g_colorTable)
+ it.cl = Colour_Get(MODULENAME, it.szName);
+
+ DeleteObject(g_plugin.hBackBrush);
+ g_plugin.hBackBrush = CreateSolidBrush(g_colorTable[COLOR_SELBACK].cl);
+
+ for (auto &it : g_fontTable) {
+ it.cl = (COLORREF)Font_Get(MODULENAME, it.szName, &it.lf);
+
+ DeleteObject(it.hfnt);
+ it.hfnt = CreateFontIndirectA(&it.lf);
+ }
+
+ WindowList_Broadcast(g_hNewstoryWindows, UM_REDRAWLISTH, 0, 0);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void InitFonts()
+{
+ HookEvent(ME_FONT_RELOAD, evtFontsChanged);
+ HookEvent(ME_COLOUR_RELOAD, evtFontsChanged);
+
+ ColourID cid = {};
+ strncpy_s(cid.group, MODULENAME, _TRUNCATE);
+ strncpy_s(cid.dbSettingsGroup, MODULENAME, _TRUNCATE);
+
+ for (auto &it : g_colorTable) {
+ cid.order = int(&it - g_colorTable);
+ strncpy_s(cid.name, it.szName, _TRUNCATE);
+ strncpy_s(cid.setting, it.szSetting, _TRUNCATE);
+ cid.defcolour = it.defaultValue;
+ g_plugin.addColor(&cid);
+ it.cl = Colour_Get(cid.group, cid.name);
+ }
+
+ g_plugin.hBackBrush = CreateSolidBrush(g_colorTable[COLOR_SELBACK].cl);
+
+ LOGFONT lfDefault;
+ SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfDefault), &lfDefault, FALSE);
+
+ FontID fontid = {};
+ fontid.flags = FIDF_DEFAULTVALID;
+ strncpy_s(fontid.group, MODULENAME, _TRUNCATE);
+ strncpy_s(fontid.dbSettingsGroup, MODULENAME, _TRUNCATE);
+ strncpy_s(fontid.deffontsettings.szFace, _T2A(lfDefault.lfFaceName), _TRUNCATE);
+ fontid.deffontsettings.size = -12;
+
+ for (auto &it : g_fontTable) {
+ fontid.order = int(&it - g_fontTable);
+ strncpy_s(fontid.name, it.szName, _TRUNCATE);
+ strncpy_s(fontid.setting, it.szSetting, _TRUNCATE);
+ fontid.deffontsettings.style = it.style;
+ fontid.deffontsettings.colour = it.defaultValue;
+ g_plugin.addFont(&fontid);
+
+ it.cl = Font_Get(MODULENAME, it.szName, &it.lf);
+ it.hfnt = CreateFontIndirectA(&it.lf);
+ }
+}
+
+void DestroyFonts()
+{
+ DeleteObject(g_plugin.hBackBrush);
+
+ for (auto &it : g_fontTable)
+ DeleteObject(it.hfnt);
+}
diff --git a/plugins/NewStory/src/fonts.h b/plugins/NewStory/src/fonts.h
index 0a441c1931..08183d0d01 100644
--- a/plugins/NewStory/src/fonts.h
+++ b/plugins/NewStory/src/fonts.h
@@ -1,58 +1,58 @@
-#ifndef __fonts_h__
-#define __fonts_h__
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-enum
-{
- COLOR_INNICK, COLOR_OUTNICK,
- COLOR_INMSG, COLOR_OUTMSG,
- COLOR_INFILE, COLOR_OUTFILE,
- COLOR_STATUS,
- COLOR_INOTHER, COLOR_OUTOTHER,
- COLOR_SELTEXT, COLOR_SELBACK, COLOR_SELFRAME,
- COLOR_HIGHLIGHT_BACK, COLOR_BACK, COLOR_FRAME, COLOR_PROGRESS,
- COLOR_COUNT
-};
-
-struct MyColourID
-{
- const char *szName, *szSetting;
- COLORREF defaultValue, cl;
-};
-
-extern MyColourID g_colorTable[COLOR_COUNT];
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-enum
-{
- FONT_INMSG,
- FONT_OUTMSG,
- FONT_INFILE,
- FONT_OUTFILE,
- FONT_STATUS,
- FONT_HIGHLIGHT,
- FONT_INOTHER,
- FONT_OUTOTHER,
- FONT_COUNT
-};
-
-struct MyFontID
-{
- const char *szName, *szSetting;
- uint8_t style;
-
- COLORREF defaultValue, cl;
- LOGFONTA lf;
- HFONT hfnt;
-};
-
-extern MyFontID g_fontTable[FONT_COUNT];
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void InitFonts();
-void DestroyFonts();
-
+#ifndef __fonts_h__
+#define __fonts_h__
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+enum
+{
+ COLOR_INNICK, COLOR_OUTNICK,
+ COLOR_INMSG, COLOR_OUTMSG,
+ COLOR_INFILE, COLOR_OUTFILE,
+ COLOR_STATUS,
+ COLOR_INOTHER, COLOR_OUTOTHER,
+ COLOR_SELTEXT, COLOR_SELBACK, COLOR_SELFRAME,
+ COLOR_HIGHLIGHT_BACK, COLOR_BACK, COLOR_FRAME, COLOR_PROGRESS,
+ COLOR_COUNT
+};
+
+struct MyColourID
+{
+ const char *szName, *szSetting;
+ COLORREF defaultValue, cl;
+};
+
+extern MyColourID g_colorTable[COLOR_COUNT];
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+enum
+{
+ FONT_INMSG,
+ FONT_OUTMSG,
+ FONT_INFILE,
+ FONT_OUTFILE,
+ FONT_STATUS,
+ FONT_HIGHLIGHT,
+ FONT_INOTHER,
+ FONT_OUTOTHER,
+ FONT_COUNT
+};
+
+struct MyFontID
+{
+ const char *szName, *szSetting;
+ uint8_t style;
+
+ COLORREF defaultValue, cl;
+ LOGFONTA lf;
+ HFONT hfnt;
+};
+
+extern MyFontID g_fontTable[FONT_COUNT];
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void InitFonts();
+void DestroyFonts();
+
#endif // __fonts_h__ \ No newline at end of file
diff --git a/plugins/NewStory/src/history.h b/plugins/NewStory/src/history.h
index f71bab9fed..ef1d9f5961 100644
--- a/plugins/NewStory/src/history.h
+++ b/plugins/NewStory/src/history.h
@@ -1,43 +1,43 @@
-#ifndef __history_h__
-#define __history_h__
-
-#define EVENTTYPE_OTHER 12345
-#define EVENTTYPE_STATUSCHANGE 25368
-#define EVENTTYPE_JABBER_PRESENCE 2001
-
-enum
-{
- UM_LOADCONTACT = WM_USER + 1,
-
- UM_REBUILDLIST,
- UM_FILTERLIST,
- UM_REDRAWLIST,
- UM_REDRAWLISTH,
-
- UM_ADDEVENT,
- UM_ADDEVENTFILTER,
- UM_REMOVEEVENT,
- UM_EDITEVENT,
-
- UM_SELECTED,
-
- UM_GETEVENTCOUNT,
- UM_GETEVENT,
- UM_GETEVENTTEXT,
- UM_GETEVENTCONTACT,
- UM_GETEVENTHANDLE,
-
- UM_BOOKMARKS = WM_USER + 0x601,
-};
-
-extern MWindowList g_hNewstoryWindows, g_hNewstoryLogs;
-
-void InitMenus();
-void InitHotkeys();
-
-INT_PTR svcShowNewstory(WPARAM, LPARAM);
-INT_PTR svcGlobalSearch(WPARAM, LPARAM);
-
-CSrmmLogWindow* __cdecl NewStory_Stub(CMsgDialog &pDlg);
-
+#ifndef __history_h__
+#define __history_h__
+
+#define EVENTTYPE_OTHER 12345
+#define EVENTTYPE_STATUSCHANGE 25368
+#define EVENTTYPE_JABBER_PRESENCE 2001
+
+enum
+{
+ UM_LOADCONTACT = WM_USER + 1,
+
+ UM_REBUILDLIST,
+ UM_FILTERLIST,
+ UM_REDRAWLIST,
+ UM_REDRAWLISTH,
+
+ UM_ADDEVENT,
+ UM_ADDEVENTFILTER,
+ UM_REMOVEEVENT,
+ UM_EDITEVENT,
+
+ UM_SELECTED,
+
+ UM_GETEVENTCOUNT,
+ UM_GETEVENT,
+ UM_GETEVENTTEXT,
+ UM_GETEVENTCONTACT,
+ UM_GETEVENTHANDLE,
+
+ UM_BOOKMARKS = WM_USER + 0x601,
+};
+
+extern MWindowList g_hNewstoryWindows, g_hNewstoryLogs;
+
+void InitMenus();
+void InitHotkeys();
+
+INT_PTR svcShowNewstory(WPARAM, LPARAM);
+INT_PTR svcGlobalSearch(WPARAM, LPARAM);
+
+CSrmmLogWindow* __cdecl NewStory_Stub(CMsgDialog &pDlg);
+
#endif // __history_h__ \ No newline at end of file
diff --git a/plugins/NewStory/src/history_array.cpp b/plugins/NewStory/src/history_array.cpp
index cfde628b0b..61209fa455 100644
--- a/plugins/NewStory/src/history_array.cpp
+++ b/plugins/NewStory/src/history_array.cpp
@@ -1,720 +1,720 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation version 2
-of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "stdafx.h"
-
-extern HANDLE htuLog;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Filters
-
-bool Filter::check(ItemData *item) const
-{
- if (!item) return false;
- if (!(flags & EVENTONLY)) {
- if (item->dbe.flags & DBEF_SENT) {
- if (!(flags & OUTGOING))
- return false;
- }
- else {
- if (!(flags & INCOMING))
- return false;
- }
- switch (item->dbe.eventType) {
- case EVENTTYPE_MESSAGE:
- if (!(flags & MESSAGES))
- return false;
- break;
- case EVENTTYPE_FILE:
- if (!(flags & FILES))
- return false;
- break;
- case EVENTTYPE_STATUSCHANGE:
- if (!(flags & STATUS))
- return false;
- break;
- default:
- if (!(flags & OTHER))
- return false;
- }
- }
-
- if (flags & (EVENTTEXT | EVENTONLY)) {
- if (item->m_bLoaded)
- return CheckFilter(item->wtext, text);
-
- if (!item->fetch())
- return false;
-
- return CheckFilter(ptrW(DbEvent_GetTextW(&item->dbe)), text);
- }
-
- return true;
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Event
-
-ItemData::ItemData()
-{
- memset(this, 0, sizeof(*this));
- m_grouping = g_plugin.bMsgGrouping ? -1 : GROUPING_NONE;
- savedHeight = -1;
-}
-
-ItemData::~ItemData()
-{
- mir_free(wtext);
- if (dbe.szReplyId)
- mir_free((char*)dbe.szReplyId);
- if (data)
- MTextDestroy(data);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static bool isEqual(const ItemData *p1, const ItemData *p2)
-{
- if (p1->hContact != p2->hContact)
- return false;
- if (p1->dbe.eventType != p2->dbe.eventType)
- return false;
- if ((p1->dbe.flags & DBEF_SENT) != (p2->dbe.flags & DBEF_SENT))
- return false;
- if (p1->dbe.timestamp / 86400 != p2->dbe.timestamp / 86400)
- return false;
- return true;
-}
-
-ItemData* ItemData::checkPrev(ItemData *pPrev, HWND hwnd)
-{
- m_grouping = GROUPING_NONE;
- if (!pPrev || !g_plugin.bMsgGrouping)
- return this;
-
- // we don't group anything but messages
- if (!fetch())
- return this;
-
- if (dbe.eventType != EVENTTYPE_MESSAGE)
- return this;
-
- if (isEqual(this, pPrev)) {
- if (pPrev->m_grouping == GROUPING_NONE) {
- pPrev->m_grouping = GROUPING_HEAD;
- if (pPrev->m_bLoaded)
- pPrev->setText(hwnd);
- }
- m_grouping = GROUPING_ITEM;
- }
- return this;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-ItemData* ItemData::checkNext(ItemData *pPrev, HWND hwnd)
-{
- m_grouping = GROUPING_NONE;
- if (!pPrev || !g_plugin.bMsgGrouping)
- return this;
-
- // we don't group anything but messages
- if (!fetch())
- return this;
-
- if (dbe.eventType != EVENTTYPE_MESSAGE)
- return this;
-
- pPrev->fetch();
- if (isEqual(this, pPrev)) {
- if (pPrev->m_grouping == GROUPING_NONE) {
- pPrev->m_grouping = GROUPING_HEAD;
- if (pPrev->m_bLoaded)
- pPrev->setText(hwnd);
- }
- m_grouping = GROUPING_ITEM;
- if (m_bLoaded)
- setText(hwnd);
- }
- return this;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static bool isEqualGC(const ItemData *p1, const ItemData *p2)
-{
- if (p1->dbe.eventType != p2->dbe.eventType)
- return false;
-
- if (!p1->wszNick || !p2->wszNick)
- return false;
-
- if (wcscmp(p1->wszNick, p2->wszNick))
- return false;
-
- if (p1->dbe.timestamp / 86400 != p2->dbe.timestamp / 86400)
- return false;
- return true;
-}
-
-ItemData* ItemData::checkPrevGC(ItemData *pPrev, HWND hwnd)
-{
- m_grouping = GROUPING_NONE;
- if (!pPrev || !g_plugin.bMsgGrouping)
- return this;
-
- if (dbe.eventType != EVENTTYPE_MESSAGE)
- return this;
-
- if (isEqualGC(this, pPrev)) {
- if (pPrev->m_grouping == GROUPING_NONE) {
- pPrev->m_grouping = GROUPING_HEAD;
- if (pPrev->m_bLoaded)
- pPrev->setText(hwnd);
- }
- m_grouping = GROUPING_ITEM;
- }
- return this;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void ItemData::checkCreate(HWND hwnd)
-{
- if (data == nullptr) {
- setText(hwnd);
- MTextSetParent(data, hwnd);
- MTextActivate(data, true);
- }
-}
-
-bool ItemData::isLink(HWND hwnd, POINT pt, CMStringW *pwszUrl) const
-{
- int cp = MTextSendMessage(0, data, EM_CHARFROMPOS, 0, LPARAM(&pt));
- if (cp == -1)
- return false;
-
- if (!isLinkChar(hwnd, cp))
- return false;
-
- if (pwszUrl) {
- CHARRANGE sel = { cp, cp };
- while (isLinkChar(hwnd, sel.cpMin-1))
- sel.cpMin--;
-
- while (isLinkChar(hwnd, sel.cpMax))
- sel.cpMax++;
-
- if (sel.cpMax > sel.cpMin) {
- pwszUrl->Truncate(sel.cpMax - sel.cpMin + 1);
-
- TEXTRANGE tr = { 0 };
- tr.chrg = sel;
- tr.lpstrText = pwszUrl->GetBuffer();
- int iRes = MTextSendMessage(0, data, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
- if (iRes > 0)
- pwszUrl->Trim();
- else
- pwszUrl->Empty();
- }
- }
- return true;
-}
-
-bool ItemData::isLinkChar(HWND hwnd, int idx) const
-{
- if (idx < 0)
- return false;
-
- CHARRANGE sel = { idx, idx + 1 };
- MTextSendMessage(hwnd, data, EM_EXSETSEL, 0, LPARAM(&sel));
-
- CHARFORMAT2 cf = {};
- cf.cbSize = sizeof(cf);
- cf.dwMask = CFM_LINK;
- uint32_t res = MTextSendMessage(hwnd, data, EM_GETCHARFORMAT, SCF_SELECTION, LPARAM(&cf));
- return ((res & CFM_LINK) && (cf.dwEffects & CFE_LINK)) || ((res & CFM_REVISED) && (cf.dwEffects & CFE_REVISED));
-}
-
-bool ItemData::fetch(void)
-{
- // if this event is virtual (for example, in group chats), don't try to laod it
- if (!hEvent)
- return false;
-
- if (!dbe) {
- if (!dbe.fetch(hEvent))
- return false;
-
- if (dbe.szReplyId)
- dbe.szReplyId = mir_strdup(dbe.szReplyId);
- }
- return true;
-}
-
-void ItemData::fill(int tmpl)
-{
- switch (tmpl) {
- case TPL_MESSAGE:
- dbe.eventType = EVENTTYPE_MESSAGE;
- break;
- case TPL_MSG_HEAD:
- m_grouping = GROUPING_HEAD;
- dbe.eventType = EVENTTYPE_MESSAGE;
- break;
- case TPL_MSG_GRP:
- m_grouping = GROUPING_ITEM;
- dbe.eventType = EVENTTYPE_MESSAGE;
- break;
- case TPL_FILE:
- dbe.eventType = EVENTTYPE_FILE;
- break;
- case TPL_SIGN:
- dbe.eventType = EVENTTYPE_STATUSCHANGE;
- break;
- case TPL_AUTH:
- dbe.eventType = EVENTTYPE_AUTHREQUEST;
- break;
- case TPL_ADDED:
- dbe.eventType = EVENTTYPE_ADDED;
- break;
- case TPL_PRESENCE:
- dbe.eventType = EVENTTYPE_JABBER_PRESENCE;
- break;
- default:
- dbe.eventType = 9247;
- break;
- }
-}
-
-int ItemData::getTemplate() const
-{
- switch (dbe.eventType) {
- case EVENTTYPE_MESSAGE:
- switch (m_grouping) {
- case GROUPING_HEAD: return TPL_MSG_HEAD;
- case GROUPING_ITEM: return TPL_MSG_GRP;
- }
- return TPL_MESSAGE;
-
- case EVENTTYPE_FILE: return TPL_FILE;
- case EVENTTYPE_STATUSCHANGE: return TPL_SIGN;
- case EVENTTYPE_AUTHREQUEST: return TPL_AUTH;
- case EVENTTYPE_ADDED: return TPL_ADDED;
- case EVENTTYPE_JABBER_PRESENCE: return TPL_PRESENCE;
- default:
- return TPL_OTHER;
- }
-}
-
-int ItemData::getCopyTemplate() const
-{
- switch (dbe.eventType) {
- case EVENTTYPE_MESSAGE: return TPL_COPY_MESSAGE;
- case EVENTTYPE_FILE: return TPL_COPY_FILE;
- case EVENTTYPE_STATUSCHANGE: return TPL_COPY_SIGN;
- case EVENTTYPE_AUTHREQUEST: return TPL_COPY_AUTH;
- case EVENTTYPE_ADDED: return TPL_COPY_ADDED;
- case EVENTTYPE_JABBER_PRESENCE: return TPL_COPY_PRESENCE;
- default:
- return TPL_COPY_OTHER;
- }
-}
-
-void ItemData::getFontColor(int &fontId, int &colorId) const
-{
- switch (dbe.eventType) {
- case EVENTTYPE_MESSAGE:
- fontId = !(dbe.flags & DBEF_SENT) ? FONT_INMSG : FONT_OUTMSG;
- colorId = !(dbe.flags & DBEF_SENT) ? COLOR_INMSG : COLOR_OUTMSG;
- break;
-
- case EVENTTYPE_FILE:
- fontId = !(dbe.flags & DBEF_SENT) ? FONT_INFILE : FONT_OUTFILE;
- colorId = !(dbe.flags & DBEF_SENT) ? COLOR_INFILE : COLOR_OUTFILE;
- break;
-
- case EVENTTYPE_STATUSCHANGE:
- fontId = FONT_STATUS;
- colorId = COLOR_STATUS;
- break;
-
- case EVENTTYPE_AUTHREQUEST:
- fontId = FONT_INOTHER;
- colorId = COLOR_INOTHER;
- break;
-
- case EVENTTYPE_ADDED:
- fontId = FONT_INOTHER;
- colorId = COLOR_INOTHER;
- break;
-
- case EVENTTYPE_JABBER_PRESENCE:
- fontId = !(dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER;
- colorId = !(dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER;
- break;
-
- default:
- fontId = !(dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER;
- colorId = !(dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER;
- break;
- }
-}
-
-void ItemData::load(bool bLoadAlways)
-{
- if (!bLoadAlways && m_bLoaded)
- return;
-
- if (!fetch())
- return;
-
- m_bLoaded = true;
- hContact = dbe.hContact; // save true contact
-
- switch (dbe.eventType) {
- case EVENTTYPE_MESSAGE:
- markRead();
- __fallthrough;
-
- case EVENTTYPE_STATUSCHANGE:
- wtext = mir_utf8decodeW((char *)dbe.pBlob);
- break;
-
- case EVENTTYPE_FILE:
- {
- DB::FILE_BLOB blob(dbe);
- if (blob.isOffline()) {
- m_bOfflineFile = true;
- if (blob.isCompleted())
- m_bOfflineDownloaded = 100;
- else
- m_bOfflineDownloaded = uint8_t(100.0 * blob.getTransferred() / blob.getSize());
-
- CMStringW buf;
- buf.Append(blob.getName() ? blob.getName() : TranslateT("Unnamed"));
-
- if (auto *pwszDescr = blob.getDescr()) {
- buf.Append(L" - ");
- buf.Append(pwszDescr);
- }
-
- if (uint32_t size = blob.getSize())
- buf.AppendFormat(TranslateT(" %u KB"), size < 1024 ? 1 : unsigned(blob.getSize() / 1024));
-
- wtext = buf.Detach();
- markRead();
- break;
- }
-
- wchar_t buf[MAX_PATH];
- File::GetReceivedFolder(hContact, buf, _countof(buf));
-
- CMStringW wszFileName = buf;
- wszFileName.Append(blob.getName());
-
- // if a filename contains spaces, URL will be broken
- if (wszFileName.Find(' ') != -1) {
- wchar_t wszShortPath[MAX_PATH];
- if (GetShortPathNameW(wszFileName, wszShortPath, _countof(wszShortPath))) {
- wszFileName = wszShortPath;
- wszFileName.MakeLower();
- }
- }
-
- wszFileName.Replace('\\', '/');
- wszFileName.Insert(0, L"file://");
- wtext = wszFileName.Detach();
- }
- break;
-
- default:
- wtext = DbEvent_GetTextW(&dbe);
- break;
- }
-
- if (dbe.szReplyId)
- if (MEVENT hReply = db_event_getById(dbe.szModule, dbe.szReplyId)) {
- DB::EventInfo dbei(hReply);
- if (dbei) {
- CMStringW str(L"> ");
-
- if (dbei.flags & DBEF_SENT) {
- if (char *szProto = Proto_GetBaseAccountName(hContact))
- str.AppendFormat(L"%s %s: ", ptrW(Contact::GetInfo(CNF_DISPLAY, 0, szProto)).get(), TranslateT("wrote"));
- }
- else str.AppendFormat(L"%s %s: ", Clist_GetContactDisplayName(hContact, 0), TranslateT("wrote"));
-
- ptrW wszText(DbEvent_GetTextW(&dbei));
- if (mir_wstrlen(wszText) > 43)
- wcscpy(wszText.get() + 40, L"...");
- str.Append(wszText);
- str.Append(L"\r\n");
- str.Append(wtext);
-
- mir_free(wtext);
- wtext = str.Detach();
- }
- }
-
- dbe.unload();
-}
-
-void ItemData::markRead()
-{
- if (!(dbe.flags & DBEF_SENT))
- dbe.wipeNotify(hEvent);
-}
-
-void ItemData::setText(HWND hwnd)
-{
- if (data)
- MTextDestroy(data);
-
- data = MTextCreateEx2(hwnd, htuLog, formatRtf().GetBuffer(), MTEXT_FLG_RTF);
- MTextSetProto(data, hContact);
- savedHeight = -1;
-}
-
-// Array
-HistoryArray::HistoryArray() :
- pages(50),
- strings(50, wcscmp)
-{
-}
-
-HistoryArray::~HistoryArray()
-{
- clear();
-}
-
-void HistoryArray::clear()
-{
- for (auto &str : strings)
- mir_free(str);
- strings.destroy();
-
- pages.destroy();
- iLastPageCounter = 0;
-}
-
-void HistoryArray::addChatEvent(SESSION_INFO *si, const LOGINFO *lin)
-{
- if (si == nullptr)
- return;
-
- int numItems = getCount();
- auto &p = allocateItem();
- p.hContact = si->hContact;
-
- if (si->pMI->bDatabase && lin->hEvent) {
- p.hEvent = lin->hEvent;
- p.load();
- }
- else {
- CMStringW wszText;
- bool bTextUsed = Chat_GetDefaultEventDescr(si, lin, wszText);
- if (!bTextUsed && lin->ptszText) {
- if (!wszText.IsEmpty())
- wszText.AppendChar(' ');
- wszText.Append(g_chatApi.RemoveFormatting(lin->ptszText));
- }
-
- p.wtext = wszText.Detach();
- p.m_bLoaded = true;
- p.m_bHighlighted = lin->bIsHighlighted;
- p.dbe.timestamp = lin->time;
- if (lin->bIsMe)
- p.dbe.flags |= DBEF_SENT;
-
- switch (lin->iType) {
- case GC_EVENT_MESSAGE:
- case GC_EVENT_INFORMATION:
- p.dbe.eventType = EVENTTYPE_MESSAGE;
- break;
-
- case GC_EVENT_SETCONTACTSTATUS:
- p.dbe.eventType = EVENTTYPE_STATUSCHANGE;
- break;
-
- case GC_EVENT_JOIN:
- case GC_EVENT_PART:
- case GC_EVENT_QUIT:
- p.dbe.eventType = EVENTTYPE_JABBER_PRESENCE;
- break;
-
- default:
- p.dbe.eventType = EVENTTYPE_OTHER;
- break;
- }
- }
-
- if (lin->ptszNick) {
- p.wszNick = strings.find(lin->ptszNick);
- if (p.wszNick == nullptr) {
- p.wszNick = mir_wstrdup(lin->ptszNick);
- strings.insert(p.wszNick);
- }
- p.checkPrevGC((numItems == 0) ? nullptr : get(numItems - 1), hwndOwner);
- }
-}
-
-bool HistoryArray::addEvent(MCONTACT hContact, MEVENT hEvent, int count)
-{
- if (count == -1)
- count = MAXINT;
-
- int numItems = getCount();
- auto *pPrev = (numItems == 0) ? nullptr : get(numItems - 1);
-
- if (count == 1) {
- auto &p = allocateItem();
- p.hContact = hContact;
- p.hEvent = hEvent;
- pPrev = p.checkPrev(pPrev, hwndOwner);
- }
- else {
- DB::ECPTR pCursor(DB::Events(hContact, hEvent));
- for (int i = 0; i < count; i++) {
- hEvent = pCursor.FetchNext();
- if (!hEvent)
- break;
-
- auto &p = allocateItem();
- p.hContact = hContact;
- p.hEvent = hEvent;
- pPrev = p.checkPrev(pPrev, hwndOwner);
- }
- }
-
- return true;
-}
-
-void HistoryArray::addResults(const OBJLIST<SearchResult> &pArray)
-{
- int numItems = getCount();
- auto *pPrev = (numItems == 0) ? nullptr : get(numItems - 1);
-
- for (auto &it : pArray) {
- auto &p = allocateItem();
- p.hContact = it->hContact;
- p.hEvent = it->hEvent;
- p.m_bIsResult = true;
- pPrev = p.checkPrev(pPrev, hwndOwner);
- }
-}
-
-ItemData& HistoryArray::allocateItem()
-{
- if (iLastPageCounter == HIST_BLOCK_SIZE) {
- pages.insert(new ItemBlock());
- iLastPageCounter = 0;
- }
- else if (pages.getCount() == 0)
- pages.insert(new ItemBlock);
-
- auto &p = pages[pages.getCount() - 1];
- return p.data[iLastPageCounter++];
-}
-
-int HistoryArray::find(MEVENT hEvent)
-{
- int i = 0;
- for (auto &it : pages)
- for (auto &p : it->data) {
- if (p.hEvent == hEvent)
- return i;
- i++;
- }
-
- return -1;
-}
-
-ItemData* HistoryArray::get(int id, bool bLoad) const
-{
- int pageNo = id / HIST_BLOCK_SIZE;
- if (pageNo >= pages.getCount())
- return nullptr;
-
- auto *p = &pages[pageNo].data[id % HIST_BLOCK_SIZE];
- if (bLoad && !p->m_bLoaded)
- p->load();
- return p;
-}
-
-int HistoryArray::getCount() const
-{
- int nPages = pages.getCount();
- return (nPages == 0) ? 0 : (nPages - 1) * HIST_BLOCK_SIZE + iLastPageCounter;
-}
-
-int HistoryArray::find(int id, int dir, const Filter &filter)
-{
- int count = getCount();
- for (int i = id + dir; i >= 0 && i < count; i += dir)
- if (filter.check(get(i)))
- return i;
-
- return -1;
-}
-
-ItemData* HistoryArray::insert(int pos)
-{
- int count = getCount();
- ItemData *pNew = &allocateItem();
- ItemData *pPrev = get(count-1, false);
-
- for (int i = count; i >= pos; i--) {
- memcpy(pNew, pPrev, sizeof(ItemData));
- pNew = pPrev;
- pPrev = get(i - 1, false);
- }
-
- ItemData tmp;
- memcpy(pNew, &tmp, sizeof(tmp));
- return pNew;
-}
-
-void HistoryArray::remove(int id)
-{
- int pageNo = id / HIST_BLOCK_SIZE;
- if (pageNo >= pages.getCount())
- return;
-
- auto &pPage = pages[pageNo];
- int offset = id % HIST_BLOCK_SIZE;
-
- ItemData tmp;
- memcpy(&tmp, pPage.data + offset, sizeof(ItemData));
-
- if (offset != HIST_BLOCK_SIZE - 1)
- memmove(&pPage.data[offset], &pPage.data[offset+1], sizeof(ItemData) * (HIST_BLOCK_SIZE - 1 - offset));
-
- for (int i = pageNo + 1; i < pages.getCount(); i++) {
- auto &prev = pages[i - 1], &curr = pages[i];
- memcpy(&prev.data[HIST_BLOCK_SIZE - 1], curr.data, sizeof(ItemData));
- memmove(&curr.data, &curr.data[1], sizeof(ItemData) * (HIST_BLOCK_SIZE - 1));
- memset(&curr.data[HIST_BLOCK_SIZE - 1], 0, sizeof(ItemData));
- }
-
- if (iLastPageCounter == 1) {
- pages.remove(pages.getCount() - 1);
- iLastPageCounter = HIST_BLOCK_SIZE;
- }
- else iLastPageCounter--;
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+extern HANDLE htuLog;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Filters
+
+bool Filter::check(ItemData *item) const
+{
+ if (!item) return false;
+ if (!(flags & EVENTONLY)) {
+ if (item->dbe.flags & DBEF_SENT) {
+ if (!(flags & OUTGOING))
+ return false;
+ }
+ else {
+ if (!(flags & INCOMING))
+ return false;
+ }
+ switch (item->dbe.eventType) {
+ case EVENTTYPE_MESSAGE:
+ if (!(flags & MESSAGES))
+ return false;
+ break;
+ case EVENTTYPE_FILE:
+ if (!(flags & FILES))
+ return false;
+ break;
+ case EVENTTYPE_STATUSCHANGE:
+ if (!(flags & STATUS))
+ return false;
+ break;
+ default:
+ if (!(flags & OTHER))
+ return false;
+ }
+ }
+
+ if (flags & (EVENTTEXT | EVENTONLY)) {
+ if (item->m_bLoaded)
+ return CheckFilter(item->wtext, text);
+
+ if (!item->fetch())
+ return false;
+
+ return CheckFilter(ptrW(DbEvent_GetTextW(&item->dbe)), text);
+ }
+
+ return true;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Event
+
+ItemData::ItemData()
+{
+ memset(this, 0, sizeof(*this));
+ m_grouping = g_plugin.bMsgGrouping ? -1 : GROUPING_NONE;
+ savedHeight = -1;
+}
+
+ItemData::~ItemData()
+{
+ mir_free(wtext);
+ if (dbe.szReplyId)
+ mir_free((char*)dbe.szReplyId);
+ if (data)
+ MTextDestroy(data);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static bool isEqual(const ItemData *p1, const ItemData *p2)
+{
+ if (p1->hContact != p2->hContact)
+ return false;
+ if (p1->dbe.eventType != p2->dbe.eventType)
+ return false;
+ if ((p1->dbe.flags & DBEF_SENT) != (p2->dbe.flags & DBEF_SENT))
+ return false;
+ if (p1->dbe.timestamp / 86400 != p2->dbe.timestamp / 86400)
+ return false;
+ return true;
+}
+
+ItemData* ItemData::checkPrev(ItemData *pPrev, HWND hwnd)
+{
+ m_grouping = GROUPING_NONE;
+ if (!pPrev || !g_plugin.bMsgGrouping)
+ return this;
+
+ // we don't group anything but messages
+ if (!fetch())
+ return this;
+
+ if (dbe.eventType != EVENTTYPE_MESSAGE)
+ return this;
+
+ if (isEqual(this, pPrev)) {
+ if (pPrev->m_grouping == GROUPING_NONE) {
+ pPrev->m_grouping = GROUPING_HEAD;
+ if (pPrev->m_bLoaded)
+ pPrev->setText(hwnd);
+ }
+ m_grouping = GROUPING_ITEM;
+ }
+ return this;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+ItemData* ItemData::checkNext(ItemData *pPrev, HWND hwnd)
+{
+ m_grouping = GROUPING_NONE;
+ if (!pPrev || !g_plugin.bMsgGrouping)
+ return this;
+
+ // we don't group anything but messages
+ if (!fetch())
+ return this;
+
+ if (dbe.eventType != EVENTTYPE_MESSAGE)
+ return this;
+
+ pPrev->fetch();
+ if (isEqual(this, pPrev)) {
+ if (pPrev->m_grouping == GROUPING_NONE) {
+ pPrev->m_grouping = GROUPING_HEAD;
+ if (pPrev->m_bLoaded)
+ pPrev->setText(hwnd);
+ }
+ m_grouping = GROUPING_ITEM;
+ if (m_bLoaded)
+ setText(hwnd);
+ }
+ return this;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static bool isEqualGC(const ItemData *p1, const ItemData *p2)
+{
+ if (p1->dbe.eventType != p2->dbe.eventType)
+ return false;
+
+ if (!p1->wszNick || !p2->wszNick)
+ return false;
+
+ if (wcscmp(p1->wszNick, p2->wszNick))
+ return false;
+
+ if (p1->dbe.timestamp / 86400 != p2->dbe.timestamp / 86400)
+ return false;
+ return true;
+}
+
+ItemData* ItemData::checkPrevGC(ItemData *pPrev, HWND hwnd)
+{
+ m_grouping = GROUPING_NONE;
+ if (!pPrev || !g_plugin.bMsgGrouping)
+ return this;
+
+ if (dbe.eventType != EVENTTYPE_MESSAGE)
+ return this;
+
+ if (isEqualGC(this, pPrev)) {
+ if (pPrev->m_grouping == GROUPING_NONE) {
+ pPrev->m_grouping = GROUPING_HEAD;
+ if (pPrev->m_bLoaded)
+ pPrev->setText(hwnd);
+ }
+ m_grouping = GROUPING_ITEM;
+ }
+ return this;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void ItemData::checkCreate(HWND hwnd)
+{
+ if (data == nullptr) {
+ setText(hwnd);
+ MTextSetParent(data, hwnd);
+ MTextActivate(data, true);
+ }
+}
+
+bool ItemData::isLink(HWND hwnd, POINT pt, CMStringW *pwszUrl) const
+{
+ int cp = MTextSendMessage(0, data, EM_CHARFROMPOS, 0, LPARAM(&pt));
+ if (cp == -1)
+ return false;
+
+ if (!isLinkChar(hwnd, cp))
+ return false;
+
+ if (pwszUrl) {
+ CHARRANGE sel = { cp, cp };
+ while (isLinkChar(hwnd, sel.cpMin-1))
+ sel.cpMin--;
+
+ while (isLinkChar(hwnd, sel.cpMax))
+ sel.cpMax++;
+
+ if (sel.cpMax > sel.cpMin) {
+ pwszUrl->Truncate(sel.cpMax - sel.cpMin + 1);
+
+ TEXTRANGE tr = { 0 };
+ tr.chrg = sel;
+ tr.lpstrText = pwszUrl->GetBuffer();
+ int iRes = MTextSendMessage(0, data, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ if (iRes > 0)
+ pwszUrl->Trim();
+ else
+ pwszUrl->Empty();
+ }
+ }
+ return true;
+}
+
+bool ItemData::isLinkChar(HWND hwnd, int idx) const
+{
+ if (idx < 0)
+ return false;
+
+ CHARRANGE sel = { idx, idx + 1 };
+ MTextSendMessage(hwnd, data, EM_EXSETSEL, 0, LPARAM(&sel));
+
+ CHARFORMAT2 cf = {};
+ cf.cbSize = sizeof(cf);
+ cf.dwMask = CFM_LINK;
+ uint32_t res = MTextSendMessage(hwnd, data, EM_GETCHARFORMAT, SCF_SELECTION, LPARAM(&cf));
+ return ((res & CFM_LINK) && (cf.dwEffects & CFE_LINK)) || ((res & CFM_REVISED) && (cf.dwEffects & CFE_REVISED));
+}
+
+bool ItemData::fetch(void)
+{
+ // if this event is virtual (for example, in group chats), don't try to laod it
+ if (!hEvent)
+ return false;
+
+ if (!dbe) {
+ if (!dbe.fetch(hEvent))
+ return false;
+
+ if (dbe.szReplyId)
+ dbe.szReplyId = mir_strdup(dbe.szReplyId);
+ }
+ return true;
+}
+
+void ItemData::fill(int tmpl)
+{
+ switch (tmpl) {
+ case TPL_MESSAGE:
+ dbe.eventType = EVENTTYPE_MESSAGE;
+ break;
+ case TPL_MSG_HEAD:
+ m_grouping = GROUPING_HEAD;
+ dbe.eventType = EVENTTYPE_MESSAGE;
+ break;
+ case TPL_MSG_GRP:
+ m_grouping = GROUPING_ITEM;
+ dbe.eventType = EVENTTYPE_MESSAGE;
+ break;
+ case TPL_FILE:
+ dbe.eventType = EVENTTYPE_FILE;
+ break;
+ case TPL_SIGN:
+ dbe.eventType = EVENTTYPE_STATUSCHANGE;
+ break;
+ case TPL_AUTH:
+ dbe.eventType = EVENTTYPE_AUTHREQUEST;
+ break;
+ case TPL_ADDED:
+ dbe.eventType = EVENTTYPE_ADDED;
+ break;
+ case TPL_PRESENCE:
+ dbe.eventType = EVENTTYPE_JABBER_PRESENCE;
+ break;
+ default:
+ dbe.eventType = 9247;
+ break;
+ }
+}
+
+int ItemData::getTemplate() const
+{
+ switch (dbe.eventType) {
+ case EVENTTYPE_MESSAGE:
+ switch (m_grouping) {
+ case GROUPING_HEAD: return TPL_MSG_HEAD;
+ case GROUPING_ITEM: return TPL_MSG_GRP;
+ }
+ return TPL_MESSAGE;
+
+ case EVENTTYPE_FILE: return TPL_FILE;
+ case EVENTTYPE_STATUSCHANGE: return TPL_SIGN;
+ case EVENTTYPE_AUTHREQUEST: return TPL_AUTH;
+ case EVENTTYPE_ADDED: return TPL_ADDED;
+ case EVENTTYPE_JABBER_PRESENCE: return TPL_PRESENCE;
+ default:
+ return TPL_OTHER;
+ }
+}
+
+int ItemData::getCopyTemplate() const
+{
+ switch (dbe.eventType) {
+ case EVENTTYPE_MESSAGE: return TPL_COPY_MESSAGE;
+ case EVENTTYPE_FILE: return TPL_COPY_FILE;
+ case EVENTTYPE_STATUSCHANGE: return TPL_COPY_SIGN;
+ case EVENTTYPE_AUTHREQUEST: return TPL_COPY_AUTH;
+ case EVENTTYPE_ADDED: return TPL_COPY_ADDED;
+ case EVENTTYPE_JABBER_PRESENCE: return TPL_COPY_PRESENCE;
+ default:
+ return TPL_COPY_OTHER;
+ }
+}
+
+void ItemData::getFontColor(int &fontId, int &colorId) const
+{
+ switch (dbe.eventType) {
+ case EVENTTYPE_MESSAGE:
+ fontId = !(dbe.flags & DBEF_SENT) ? FONT_INMSG : FONT_OUTMSG;
+ colorId = !(dbe.flags & DBEF_SENT) ? COLOR_INMSG : COLOR_OUTMSG;
+ break;
+
+ case EVENTTYPE_FILE:
+ fontId = !(dbe.flags & DBEF_SENT) ? FONT_INFILE : FONT_OUTFILE;
+ colorId = !(dbe.flags & DBEF_SENT) ? COLOR_INFILE : COLOR_OUTFILE;
+ break;
+
+ case EVENTTYPE_STATUSCHANGE:
+ fontId = FONT_STATUS;
+ colorId = COLOR_STATUS;
+ break;
+
+ case EVENTTYPE_AUTHREQUEST:
+ fontId = FONT_INOTHER;
+ colorId = COLOR_INOTHER;
+ break;
+
+ case EVENTTYPE_ADDED:
+ fontId = FONT_INOTHER;
+ colorId = COLOR_INOTHER;
+ break;
+
+ case EVENTTYPE_JABBER_PRESENCE:
+ fontId = !(dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER;
+ colorId = !(dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER;
+ break;
+
+ default:
+ fontId = !(dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER;
+ colorId = !(dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER;
+ break;
+ }
+}
+
+void ItemData::load(bool bLoadAlways)
+{
+ if (!bLoadAlways && m_bLoaded)
+ return;
+
+ if (!fetch())
+ return;
+
+ m_bLoaded = true;
+ hContact = dbe.hContact; // save true contact
+
+ switch (dbe.eventType) {
+ case EVENTTYPE_MESSAGE:
+ markRead();
+ __fallthrough;
+
+ case EVENTTYPE_STATUSCHANGE:
+ wtext = mir_utf8decodeW((char *)dbe.pBlob);
+ break;
+
+ case EVENTTYPE_FILE:
+ {
+ DB::FILE_BLOB blob(dbe);
+ if (blob.isOffline()) {
+ m_bOfflineFile = true;
+ if (blob.isCompleted())
+ m_bOfflineDownloaded = 100;
+ else
+ m_bOfflineDownloaded = uint8_t(100.0 * blob.getTransferred() / blob.getSize());
+
+ CMStringW buf;
+ buf.Append(blob.getName() ? blob.getName() : TranslateT("Unnamed"));
+
+ if (auto *pwszDescr = blob.getDescr()) {
+ buf.Append(L" - ");
+ buf.Append(pwszDescr);
+ }
+
+ if (uint32_t size = blob.getSize())
+ buf.AppendFormat(TranslateT(" %u KB"), size < 1024 ? 1 : unsigned(blob.getSize() / 1024));
+
+ wtext = buf.Detach();
+ markRead();
+ break;
+ }
+
+ wchar_t buf[MAX_PATH];
+ File::GetReceivedFolder(hContact, buf, _countof(buf));
+
+ CMStringW wszFileName = buf;
+ wszFileName.Append(blob.getName());
+
+ // if a filename contains spaces, URL will be broken
+ if (wszFileName.Find(' ') != -1) {
+ wchar_t wszShortPath[MAX_PATH];
+ if (GetShortPathNameW(wszFileName, wszShortPath, _countof(wszShortPath))) {
+ wszFileName = wszShortPath;
+ wszFileName.MakeLower();
+ }
+ }
+
+ wszFileName.Replace('\\', '/');
+ wszFileName.Insert(0, L"file://");
+ wtext = wszFileName.Detach();
+ }
+ break;
+
+ default:
+ wtext = DbEvent_GetTextW(&dbe);
+ break;
+ }
+
+ if (dbe.szReplyId)
+ if (MEVENT hReply = db_event_getById(dbe.szModule, dbe.szReplyId)) {
+ DB::EventInfo dbei(hReply);
+ if (dbei) {
+ CMStringW str(L"> ");
+
+ if (dbei.flags & DBEF_SENT) {
+ if (char *szProto = Proto_GetBaseAccountName(hContact))
+ str.AppendFormat(L"%s %s: ", ptrW(Contact::GetInfo(CNF_DISPLAY, 0, szProto)).get(), TranslateT("wrote"));
+ }
+ else str.AppendFormat(L"%s %s: ", Clist_GetContactDisplayName(hContact, 0), TranslateT("wrote"));
+
+ ptrW wszText(DbEvent_GetTextW(&dbei));
+ if (mir_wstrlen(wszText) > 43)
+ wcscpy(wszText.get() + 40, L"...");
+ str.Append(wszText);
+ str.Append(L"\r\n");
+ str.Append(wtext);
+
+ mir_free(wtext);
+ wtext = str.Detach();
+ }
+ }
+
+ dbe.unload();
+}
+
+void ItemData::markRead()
+{
+ if (!(dbe.flags & DBEF_SENT))
+ dbe.wipeNotify(hEvent);
+}
+
+void ItemData::setText(HWND hwnd)
+{
+ if (data)
+ MTextDestroy(data);
+
+ data = MTextCreateEx2(hwnd, htuLog, formatRtf().GetBuffer(), MTEXT_FLG_RTF);
+ MTextSetProto(data, hContact);
+ savedHeight = -1;
+}
+
+// Array
+HistoryArray::HistoryArray() :
+ pages(50),
+ strings(50, wcscmp)
+{
+}
+
+HistoryArray::~HistoryArray()
+{
+ clear();
+}
+
+void HistoryArray::clear()
+{
+ for (auto &str : strings)
+ mir_free(str);
+ strings.destroy();
+
+ pages.destroy();
+ iLastPageCounter = 0;
+}
+
+void HistoryArray::addChatEvent(SESSION_INFO *si, const LOGINFO *lin)
+{
+ if (si == nullptr)
+ return;
+
+ int numItems = getCount();
+ auto &p = allocateItem();
+ p.hContact = si->hContact;
+
+ if (si->pMI->bDatabase && lin->hEvent) {
+ p.hEvent = lin->hEvent;
+ p.load();
+ }
+ else {
+ CMStringW wszText;
+ bool bTextUsed = Chat_GetDefaultEventDescr(si, lin, wszText);
+ if (!bTextUsed && lin->ptszText) {
+ if (!wszText.IsEmpty())
+ wszText.AppendChar(' ');
+ wszText.Append(g_chatApi.RemoveFormatting(lin->ptszText));
+ }
+
+ p.wtext = wszText.Detach();
+ p.m_bLoaded = true;
+ p.m_bHighlighted = lin->bIsHighlighted;
+ p.dbe.timestamp = lin->time;
+ if (lin->bIsMe)
+ p.dbe.flags |= DBEF_SENT;
+
+ switch (lin->iType) {
+ case GC_EVENT_MESSAGE:
+ case GC_EVENT_INFORMATION:
+ p.dbe.eventType = EVENTTYPE_MESSAGE;
+ break;
+
+ case GC_EVENT_SETCONTACTSTATUS:
+ p.dbe.eventType = EVENTTYPE_STATUSCHANGE;
+ break;
+
+ case GC_EVENT_JOIN:
+ case GC_EVENT_PART:
+ case GC_EVENT_QUIT:
+ p.dbe.eventType = EVENTTYPE_JABBER_PRESENCE;
+ break;
+
+ default:
+ p.dbe.eventType = EVENTTYPE_OTHER;
+ break;
+ }
+ }
+
+ if (lin->ptszNick) {
+ p.wszNick = strings.find(lin->ptszNick);
+ if (p.wszNick == nullptr) {
+ p.wszNick = mir_wstrdup(lin->ptszNick);
+ strings.insert(p.wszNick);
+ }
+ p.checkPrevGC((numItems == 0) ? nullptr : get(numItems - 1), hwndOwner);
+ }
+}
+
+bool HistoryArray::addEvent(MCONTACT hContact, MEVENT hEvent, int count)
+{
+ if (count == -1)
+ count = MAXINT;
+
+ int numItems = getCount();
+ auto *pPrev = (numItems == 0) ? nullptr : get(numItems - 1);
+
+ if (count == 1) {
+ auto &p = allocateItem();
+ p.hContact = hContact;
+ p.hEvent = hEvent;
+ pPrev = p.checkPrev(pPrev, hwndOwner);
+ }
+ else {
+ DB::ECPTR pCursor(DB::Events(hContact, hEvent));
+ for (int i = 0; i < count; i++) {
+ hEvent = pCursor.FetchNext();
+ if (!hEvent)
+ break;
+
+ auto &p = allocateItem();
+ p.hContact = hContact;
+ p.hEvent = hEvent;
+ pPrev = p.checkPrev(pPrev, hwndOwner);
+ }
+ }
+
+ return true;
+}
+
+void HistoryArray::addResults(const OBJLIST<SearchResult> &pArray)
+{
+ int numItems = getCount();
+ auto *pPrev = (numItems == 0) ? nullptr : get(numItems - 1);
+
+ for (auto &it : pArray) {
+ auto &p = allocateItem();
+ p.hContact = it->hContact;
+ p.hEvent = it->hEvent;
+ p.m_bIsResult = true;
+ pPrev = p.checkPrev(pPrev, hwndOwner);
+ }
+}
+
+ItemData& HistoryArray::allocateItem()
+{
+ if (iLastPageCounter == HIST_BLOCK_SIZE) {
+ pages.insert(new ItemBlock());
+ iLastPageCounter = 0;
+ }
+ else if (pages.getCount() == 0)
+ pages.insert(new ItemBlock);
+
+ auto &p = pages[pages.getCount() - 1];
+ return p.data[iLastPageCounter++];
+}
+
+int HistoryArray::find(MEVENT hEvent)
+{
+ int i = 0;
+ for (auto &it : pages)
+ for (auto &p : it->data) {
+ if (p.hEvent == hEvent)
+ return i;
+ i++;
+ }
+
+ return -1;
+}
+
+ItemData* HistoryArray::get(int id, bool bLoad) const
+{
+ int pageNo = id / HIST_BLOCK_SIZE;
+ if (pageNo >= pages.getCount())
+ return nullptr;
+
+ auto *p = &pages[pageNo].data[id % HIST_BLOCK_SIZE];
+ if (bLoad && !p->m_bLoaded)
+ p->load();
+ return p;
+}
+
+int HistoryArray::getCount() const
+{
+ int nPages = pages.getCount();
+ return (nPages == 0) ? 0 : (nPages - 1) * HIST_BLOCK_SIZE + iLastPageCounter;
+}
+
+int HistoryArray::find(int id, int dir, const Filter &filter)
+{
+ int count = getCount();
+ for (int i = id + dir; i >= 0 && i < count; i += dir)
+ if (filter.check(get(i)))
+ return i;
+
+ return -1;
+}
+
+ItemData* HistoryArray::insert(int pos)
+{
+ int count = getCount();
+ ItemData *pNew = &allocateItem();
+ ItemData *pPrev = get(count-1, false);
+
+ for (int i = count; i >= pos; i--) {
+ memcpy(pNew, pPrev, sizeof(ItemData));
+ pNew = pPrev;
+ pPrev = get(i - 1, false);
+ }
+
+ ItemData tmp;
+ memcpy(pNew, &tmp, sizeof(tmp));
+ return pNew;
+}
+
+void HistoryArray::remove(int id)
+{
+ int pageNo = id / HIST_BLOCK_SIZE;
+ if (pageNo >= pages.getCount())
+ return;
+
+ auto &pPage = pages[pageNo];
+ int offset = id % HIST_BLOCK_SIZE;
+
+ ItemData tmp;
+ memcpy(&tmp, pPage.data + offset, sizeof(ItemData));
+
+ if (offset != HIST_BLOCK_SIZE - 1)
+ memmove(&pPage.data[offset], &pPage.data[offset+1], sizeof(ItemData) * (HIST_BLOCK_SIZE - 1 - offset));
+
+ for (int i = pageNo + 1; i < pages.getCount(); i++) {
+ auto &prev = pages[i - 1], &curr = pages[i];
+ memcpy(&prev.data[HIST_BLOCK_SIZE - 1], curr.data, sizeof(ItemData));
+ memmove(&curr.data, &curr.data[1], sizeof(ItemData) * (HIST_BLOCK_SIZE - 1));
+ memset(&curr.data[HIST_BLOCK_SIZE - 1], 0, sizeof(ItemData));
+ }
+
+ if (iLastPageCounter == 1) {
+ pages.remove(pages.getCount() - 1);
+ iLastPageCounter = HIST_BLOCK_SIZE;
+ }
+ else iLastPageCounter--;
+}
diff --git a/plugins/NewStory/src/history_array.h b/plugins/NewStory/src/history_array.h
index afeb93e54c..df0142308f 100644
--- a/plugins/NewStory/src/history_array.h
+++ b/plugins/NewStory/src/history_array.h
@@ -1,164 +1,164 @@
-#ifndef __history_array__
-#define __history_array__
-
-enum
-{
- GROUPING_NONE = 0,
- GROUPING_HEAD = 1,
- GROUPING_ITEM = 2,
-};
-
-CMStringW TplFormatString(int tpl, MCONTACT hContact, ItemData *item);
-
-struct ItemData
-{
- MCONTACT hContact;
- MEVENT hEvent;
-
- bool m_bSelected, m_bHighlighted;
- bool m_bLoaded, m_bIsResult;
- bool m_bOfflineFile;
- uint8_t m_grouping, m_bOfflineDownloaded;
-
- int savedTop, savedHeight;
-
- DB::EventInfo dbe;
- wchar_t *wtext;
- wchar_t *wszNick;
-
- HText data;
-
- ItemData();
- ~ItemData();
-
- ItemData* checkNext(ItemData *pPrev, HWND hwnd);
- ItemData* checkPrev(ItemData *pPrev, HWND hwnd);
- ItemData* checkPrevGC(ItemData *pPrev, HWND hwnd);
- void checkCreate(HWND hwnd);
- void markRead();
- void setText(HWND hwnd);
-
- bool completed() const { return m_bOfflineDownloaded == 100; }
- bool fetch(void);
- void fill(int tmpl);
- void load(bool bLoad = false);
- bool isLink(HWND, POINT pt, CMStringW *url = nullptr) const;
- bool isLinkChar(HWND, int idx) const;
-
- int getTemplate() const;
- int getCopyTemplate() const;
- void getFontColor(int &fontId, int &colorId) const;
-
- CMStringA formatRtf(const wchar_t *pwszStr = 0);
- CMStringW formatString() { return TplFormatString(getTemplate(), hContact, this); }
- CMStringW formatStringEx(wchar_t *sztpl);
-
- inline wchar_t* getWBuf()
- {
- load();
- return wtext;
- }
-};
-
-class Filter
-{
- uint16_t flags;
- ptrW text;
-
-public:
- enum
- {
- INCOMING = 0x001,
- OUTGOING = 0x002,
- MESSAGES = 0x004,
- FILES = 0x008,
- STATUS = 0x020,
- OTHER = 0x040,
- EVENTTEXT = 0x080,
- EVENTONLY = 0x100,
- };
-
- __forceinline Filter(uint16_t aFlags, const wchar_t *wText) :
- flags(aFlags),
- text(mir_wstrdup(wText))
- {
- }
-
- bool check(ItemData *item) const;
-};
-
-enum
-{
- FILTER_TIME = 0x01,
- FILTER_TYPE = 0x02,
- FILTER_DIRECTION = 0x04,
- FILTER_TEXT = 0x08,
- FILTER_UNICODE = 0x10,
-
- FTYPE_MESSAGE = 0x01,
- FTYPE_FILE = 0x02,
- FTYPE_URL = 0x04,
- FTYPE_STATUS = 0x08,
- FTYPE_OTHER = 0x10,
- FTYPE_INCOMING = 0x20,
- FTYPE_OUTGOING = 0x40
-};
-
-#define HIST_BLOCK_SIZE 1000
-
-struct ItemBlock : public MZeroedObject
-{
- ItemData data[HIST_BLOCK_SIZE];
-};
-
-struct SearchResult
-{
- SearchResult(MCONTACT _1, MEVENT _2, uint32_t _3) :
- hContact(_1),
- hEvent(_2),
- ts(_3)
- {}
-
- MCONTACT hContact;
- MEVENT hEvent;
- uint32_t ts;
-};
-
-class HistoryArray
-{
- LIST<wchar_t> strings;
- OBJLIST<ItemBlock> pages;
- int iLastPageCounter = 0;
- MWindow hwndOwner = 0;
-
- ItemData& allocateItem(void);
-
-public:
- HistoryArray();
- ~HistoryArray();
-
- bool addEvent(MCONTACT hContact, MEVENT hEvent, int count);
- void addChatEvent(SESSION_INFO *si, const LOGINFO *pEvent);
- void addResults(const OBJLIST<SearchResult> &pArray);
- void clear();
- int find(MEVENT hEvent);
- int find(int id, int dir, const Filter &filter);
- int getCount() const;
- void remove(int idx);
- void reset()
- {
- clear();
- pages.insert(new ItemBlock());
- }
- void setOwner(MWindow hwnd) {
- hwndOwner = hwnd;
- }
-
- ItemData* get(int id, bool bLoad = false) const;
- ItemData* insert(int idx);
-
- __forceinline int FindNext(int id, const Filter &filter) { return find(id, +1, filter); }
- __forceinline int FindPrev(int id, const Filter &filter) { return find(id, -1, filter); }
-};
-
-#endif // __history_array__
+#ifndef __history_array__
+#define __history_array__
+
+enum
+{
+ GROUPING_NONE = 0,
+ GROUPING_HEAD = 1,
+ GROUPING_ITEM = 2,
+};
+
+CMStringW TplFormatString(int tpl, MCONTACT hContact, ItemData *item);
+
+struct ItemData
+{
+ MCONTACT hContact;
+ MEVENT hEvent;
+
+ bool m_bSelected, m_bHighlighted;
+ bool m_bLoaded, m_bIsResult;
+ bool m_bOfflineFile;
+ uint8_t m_grouping, m_bOfflineDownloaded;
+
+ int savedTop, savedHeight;
+
+ DB::EventInfo dbe;
+ wchar_t *wtext;
+ wchar_t *wszNick;
+
+ HText data;
+
+ ItemData();
+ ~ItemData();
+
+ ItemData* checkNext(ItemData *pPrev, HWND hwnd);
+ ItemData* checkPrev(ItemData *pPrev, HWND hwnd);
+ ItemData* checkPrevGC(ItemData *pPrev, HWND hwnd);
+ void checkCreate(HWND hwnd);
+ void markRead();
+ void setText(HWND hwnd);
+
+ bool completed() const { return m_bOfflineDownloaded == 100; }
+ bool fetch(void);
+ void fill(int tmpl);
+ void load(bool bLoad = false);
+ bool isLink(HWND, POINT pt, CMStringW *url = nullptr) const;
+ bool isLinkChar(HWND, int idx) const;
+
+ int getTemplate() const;
+ int getCopyTemplate() const;
+ void getFontColor(int &fontId, int &colorId) const;
+
+ CMStringA formatRtf(const wchar_t *pwszStr = 0);
+ CMStringW formatString() { return TplFormatString(getTemplate(), hContact, this); }
+ CMStringW formatStringEx(wchar_t *sztpl);
+
+ inline wchar_t* getWBuf()
+ {
+ load();
+ return wtext;
+ }
+};
+
+class Filter
+{
+ uint16_t flags;
+ ptrW text;
+
+public:
+ enum
+ {
+ INCOMING = 0x001,
+ OUTGOING = 0x002,
+ MESSAGES = 0x004,
+ FILES = 0x008,
+ STATUS = 0x020,
+ OTHER = 0x040,
+ EVENTTEXT = 0x080,
+ EVENTONLY = 0x100,
+ };
+
+ __forceinline Filter(uint16_t aFlags, const wchar_t *wText) :
+ flags(aFlags),
+ text(mir_wstrdup(wText))
+ {
+ }
+
+ bool check(ItemData *item) const;
+};
+
+enum
+{
+ FILTER_TIME = 0x01,
+ FILTER_TYPE = 0x02,
+ FILTER_DIRECTION = 0x04,
+ FILTER_TEXT = 0x08,
+ FILTER_UNICODE = 0x10,
+
+ FTYPE_MESSAGE = 0x01,
+ FTYPE_FILE = 0x02,
+ FTYPE_URL = 0x04,
+ FTYPE_STATUS = 0x08,
+ FTYPE_OTHER = 0x10,
+ FTYPE_INCOMING = 0x20,
+ FTYPE_OUTGOING = 0x40
+};
+
+#define HIST_BLOCK_SIZE 1000
+
+struct ItemBlock : public MZeroedObject
+{
+ ItemData data[HIST_BLOCK_SIZE];
+};
+
+struct SearchResult
+{
+ SearchResult(MCONTACT _1, MEVENT _2, uint32_t _3) :
+ hContact(_1),
+ hEvent(_2),
+ ts(_3)
+ {}
+
+ MCONTACT hContact;
+ MEVENT hEvent;
+ uint32_t ts;
+};
+
+class HistoryArray
+{
+ LIST<wchar_t> strings;
+ OBJLIST<ItemBlock> pages;
+ int iLastPageCounter = 0;
+ MWindow hwndOwner = 0;
+
+ ItemData& allocateItem(void);
+
+public:
+ HistoryArray();
+ ~HistoryArray();
+
+ bool addEvent(MCONTACT hContact, MEVENT hEvent, int count);
+ void addChatEvent(SESSION_INFO *si, const LOGINFO *pEvent);
+ void addResults(const OBJLIST<SearchResult> &pArray);
+ void clear();
+ int find(MEVENT hEvent);
+ int find(int id, int dir, const Filter &filter);
+ int getCount() const;
+ void remove(int idx);
+ void reset()
+ {
+ clear();
+ pages.insert(new ItemBlock());
+ }
+ void setOwner(MWindow hwnd) {
+ hwndOwner = hwnd;
+ }
+
+ ItemData* get(int id, bool bLoad = false) const;
+ ItemData* insert(int idx);
+
+ __forceinline int FindNext(int id, const Filter &filter) { return find(id, +1, filter); }
+ __forceinline int FindPrev(int id, const Filter &filter) { return find(id, -1, filter); }
+};
+
+#endif // __history_array__
diff --git a/plugins/NewStory/src/history_control.cpp b/plugins/NewStory/src/history_control.cpp
index 25779b0e7d..d035320ddb 100644
--- a/plugins/NewStory/src/history_control.cpp
+++ b/plugins/NewStory/src/history_control.cpp
@@ -1,1541 +1,1541 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation version 2
-of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "stdafx.h"
-
-#define AVERAGE_ITEM_HEIGHT 100
-
-HANDLE htuLog = 0;
-
-void InitHotkeys()
-{
- HOTKEYDESC hkd = {};
- hkd.szSection.a = MODULENAME;
-
- hkd.szDescription.a = LPGEN("Toggle bookmark");
- hkd.pszName = "ns_bookmark";
- hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, 'B') | HKF_MIRANDA_LOCAL;
- hkd.lParam = HOTKEY_BOOKMARK;
- g_plugin.addHotkey(&hkd);
-
- hkd.szDescription.a = LPGEN("Search");
- hkd.pszName = "ns_search";
- hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, 'F') | HKF_MIRANDA_LOCAL;
- hkd.lParam = HOTKEY_SEARCH;
- g_plugin.addHotkey(&hkd);
-
- hkd.szDescription.a = LPGEN("Search forward");
- hkd.pszName = "ns_seek_forward";
- hkd.DefHotKey = HOTKEYCODE(0, VK_F3) | HKF_MIRANDA_LOCAL;
- hkd.lParam = HOTKEY_SEEK_FORWARD;
- g_plugin.addHotkey(&hkd);
-
- hkd.szDescription.a = LPGEN("Search backward");
- hkd.pszName = "ns_seek_back";
- hkd.DefHotKey = HOTKEYCODE(HOTKEYF_SHIFT, VK_F3) | HKF_MIRANDA_LOCAL;
- hkd.lParam = HOTKEY_SEEK_BACK;
- g_plugin.addHotkey(&hkd);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Control utilities, types and constants
-
-NewstoryListData::NewstoryListData(HWND _1) :
- m_hwnd(_1),
- redrawTimer(Miranda_GetSystemWindow(), LPARAM(this))
-{
- items.setOwner(_1);
-
- bSortAscending = g_plugin.bSortAscending;
-
- redrawTimer.OnEvent = Callback(this, &NewstoryListData::onTimer_Draw);
-}
-
-void NewstoryListData::onTimer_Draw(CTimer *pTimer)
-{
- pTimer->Stop();
-
- if (bWasAtBottom)
- EnsureVisible(totalCount - 1);
-
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-void NewstoryListData::OnContextMenu(int index, POINT pt)
-{
- HMENU hMenu = NSMenu_Build(this, (index == -1) ? 0 : LoadItem(index));
-
- if (pMsgDlg != nullptr && pMsgDlg->isChat())
- Chat_CreateMenu(hMenu, pMsgDlg->getChat(), nullptr);
-
- TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, m_hwnd, nullptr);
- Menu_DestroyNestedMenu(hMenu);
-}
-
-void NewstoryListData::OnResize(int newWidth, int newHeight)
-{
- bool bDraw = false;
- if (newWidth != cachedWindowWidth) {
- cachedWindowWidth = newWidth;
- for (int i = 0; i < totalCount; i++)
- GetItem(i)->savedHeight = -1;
- bDraw = true;
- }
-
- if (newHeight != cachedWindowHeight) {
- cachedWindowHeight = newHeight;
- FixScrollPosition(true);
- bDraw = true;
- }
-
- if (bDraw)
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-void NewstoryListData::AddChatEvent(SESSION_INFO *si, const LOGINFO *lin)
-{
- ScheduleDraw();
- items.addChatEvent(si, lin);
- totalCount++;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void NewstoryListData::AddEvent(MCONTACT hContact, MEVENT hFirstEvent, int iCount)
-{
- ScheduleDraw();
- items.addEvent(hContact, hFirstEvent, iCount);
- totalCount = items.getCount();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void NewstoryListData::AddResults(const OBJLIST<SearchResult> &results)
-{
- ScheduleDraw();
- items.addResults(results);
- totalCount = items.getCount();
-}
-
-void NewstoryListData::AddSelection(int iFirst, int iLast)
-{
- int start = min(totalCount - 1, iFirst);
- int end = min(totalCount - 1, max(0, iLast));
- if (start > end)
- std::swap(start, end);
-
- for (int i = start; i <= end; ++i)
- if (auto *p = GetItem(i))
- p->m_bSelected = true;
-
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-bool NewstoryListData::AtBottom(void) const
-{
- if (scrollTopItem > cachedMaxTopItem)
- return true;
-
- if (scrollTopItem == cachedMaxTopItem && cachedMaxTopPixel >= scrollTopPixel)
- return true;
-
- return false;
-}
-
-bool NewstoryListData::AtTop(void) const
-{
- if (scrollTopItem < 0)
- return true;
-
- if (scrollTopItem == 0 && scrollTopPixel == 0)
- return true;
-
- return false;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Edit box window procedure
-
-static LRESULT CALLBACK HistoryEditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- auto *pData = (NewstoryListData *)GetWindowLongPtr(GetParent(hwnd), 0);
-
- switch (msg) {
- case WM_KEYDOWN:
- switch (wParam) {
- case VK_RETURN:
- pData->EndEditItem(true);
- return 0;
- case VK_ESCAPE:
- pData->EndEditItem(false);
- return 0;
- }
- break;
-
- case WM_GETDLGCODE:
- if (lParam) {
- MSG *msg2 = (MSG *)lParam;
- if (msg2->message == WM_KEYDOWN && msg2->wParam == VK_TAB)
- return 0;
- if (msg2->message == WM_CHAR && msg2->wParam == '\t')
- return 0;
- }
- return DLGC_WANTMESSAGE;
-
- case WM_KILLFOCUS:
- pData->EndEditItem(false);
- return 0;
- }
-
- return mir_callNextSubclass(hwnd, HistoryEditWndProc, msg, wParam, lParam);
-}
-
-void NewstoryListData::BeginEditItem()
-{
- if (hwndEditBox)
- EndEditItem(false);
-
- if (scrollTopItem > caret)
- return;
-
- ItemData *item = LoadItem(caret);
- if (item->dbe.eventType != EVENTTYPE_MESSAGE)
- return;
-
- RECT rc; GetClientRect(m_hwnd, &rc);
- int height = rc.bottom - rc.top;
-
- int top = scrollTopPixel;
- int idx = scrollTopItem;
- int itemHeight = GetItemHeight(idx);
- while (top < height) {
- if (idx == caret)
- break;
-
- top += itemHeight;
- idx++;
- itemHeight = GetItemHeight(idx);
- }
-
- int fontid, colorid;
- item->getFontColor(fontid, colorid);
-
- // #4012 make sure that both single & double CRLF are now double
- CMStringW wszText(item->getWBuf());
- wszText.Replace(L"\r\n", L"\n");
- wszText.Replace(L"\n", L"\r\n");
-
- uint32_t dwStyle = WS_CHILD | WS_BORDER | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL;
- hwndEditBox = CreateWindow(L"EDIT", wszText, dwStyle, 0, top, rc.right - rc.left, itemHeight, m_hwnd, NULL, g_plugin.getInst(), NULL);
- mir_subclassWindow(hwndEditBox, HistoryEditWndProc);
- SendMessage(hwndEditBox, WM_SETFONT, (WPARAM)g_fontTable[fontid].hfnt, 0);
- SendMessage(hwndEditBox, EM_SETMARGINS, EC_RIGHTMARGIN, 100);
- ShowWindow(hwndEditBox, SW_SHOW);
- SetFocus(hwndEditBox);
- SetForegroundWindow(hwndEditBox);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void NewstoryListData::CalcBottom()
-{
- int maxTopItem = totalCount, tmp = 0;
- while (maxTopItem > 0 && tmp < cachedWindowHeight)
- tmp += GetItemHeight(--maxTopItem);
- cachedMaxTopItem = maxTopItem;
- cachedMaxTopPixel = (cachedWindowHeight < tmp) ? cachedWindowHeight - tmp : 0;
-}
-
-void NewstoryListData::Clear()
-{
- items.clear();
- totalCount = 0;
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-void NewstoryListData::ClearSelection(int iFirst, int iLast)
-{
- int start = min(0, iFirst);
- int end = (iLast <= 0) ? totalCount - 1 : iLast;
- if (start > end)
- std::swap(start, end);
-
- for (int i = start; i <= end; ++i)
- if (auto *pItem = GetItem(i))
- pItem->m_bSelected = false;
-
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-void NewstoryListData::Copy(bool bTextOnly)
-{
- Utils_ClipboardCopy(GatherSelected(bTextOnly));
-}
-
-void NewstoryListData::CopyPath()
-{
- if (auto *pItem = GetItem(caret))
- if (pItem->completed()) {
- DB::EventInfo dbei(pItem->hEvent);
- DB::FILE_BLOB blob(dbei);
- Utils_ClipboardCopy(blob.getLocalName());
- }
-}
-
-void NewstoryListData::CopyUrl()
-{
- if (auto *pItem = GetItem(caret))
- Srmm_DownloadOfflineFile(pItem->hContact, pItem->hEvent, OFD_COPYURL);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Delete events dialog
-
-class CDeleteEventsDlg : public CDlgBase
-{
- MCONTACT m_hContact;
- CCtrlCheck chkDelHistory, chkForEveryone;
-
-public:
- bool bDelHistory = false, bForEveryone = false;
-
- CDeleteEventsDlg(MCONTACT hContact) :
- CDlgBase(g_plugin, IDD_EMPTYHISTORY),
- chkDelHistory(this, IDC_DELSERVERHISTORY),
- chkForEveryone(this, IDC_BOTH)
- {
- if (char *szProto = Proto_GetBaseAccountName(hContact)) {
- bDelHistory = ProtoServiceExists(szProto, PS_EMPTY_SRV_HISTORY);
- bForEveryone = (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_DELETEFORALL) != 0;
- }
- }
-
- bool OnInitDialog() override
- {
- chkDelHistory.SetState(bDelHistory);
- chkDelHistory.Enable(bDelHistory);
-
- bool bEnabled = bDelHistory && bForEveryone;
- chkForEveryone.SetState(!bEnabled);
- chkForEveryone.Enable(bEnabled);
-
- LOGFONT lf;
- HFONT hFont = (HFONT)SendDlgItemMessage(m_hwnd, IDOK, WM_GETFONT, 0, 0);
- GetObject(hFont, sizeof(lf), &lf);
- lf.lfWeight = FW_BOLD;
- SendDlgItemMessage(m_hwnd, IDC_TOPLINE, WM_SETFONT, (WPARAM)CreateFontIndirect(&lf), 0);
-
- wchar_t szFormat[256], szFinal[256];
- GetDlgItemText(m_hwnd, IDC_TOPLINE, szFormat, _countof(szFormat));
- mir_snwprintf(szFinal, szFormat, Clist_GetContactDisplayName(m_hContact));
- SetDlgItemText(m_hwnd, IDC_TOPLINE, szFinal);
-
- SetFocus(GetDlgItem(m_hwnd, IDNO));
- SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
- return true;
- }
-
- bool OnApply() override
- {
- bDelHistory = chkDelHistory.IsChecked();
- bForEveryone = chkForEveryone.IsChecked();
- return true;
- }
-
- void OnDestroy() override
- {
- DeleteObject((HFONT)SendDlgItemMessage(m_hwnd, IDC_TOPLINE, WM_GETFONT, 0, 0));
- }
-};
-
-void NewstoryListData::DeleteItems(void)
-{
- CDeleteEventsDlg dlg(m_hContact);
- if (IDOK != dlg.DoModal())
- return;
-
- g_plugin.bDisableDelete = true;
-
- int firstSel = -1, flags = 0;
- if (dlg.bDelHistory)
- flags |= CDF_DEL_HISTORY;
- if (dlg.bForEveryone)
- flags |= CDF_FOR_EVERYONE;
-
- for (int i = totalCount - 1; i >= 0; i--) {
- auto *p = GetItem(i);
- if (!p->m_bSelected)
- continue;
-
- if (p->hEvent)
- db_event_delete(p->hEvent, flags);
- items.remove(i);
- totalCount--;
- firstSel = i;
- }
-
- g_plugin.bDisableDelete = false;
-
- if (firstSel != -1) {
- SetCaret(firstSel, false);
- SetSelection(firstSel, firstSel);
- FixScrollPosition(true);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void NewstoryListData::Download(int options)
-{
- if (auto *p = LoadItem(caret))
- Srmm_DownloadOfflineFile(p->hContact, p->hEvent, options);
-}
-
-void NewstoryListData::EndEditItem(bool bAccept)
-{
- if (hwndEditBox == nullptr)
- return;
-
- if (bAccept) {
- if ((GetWindowLong(hwndEditBox, GWL_STYLE) & ES_READONLY) == 0) {
- auto *pItem = GetItem(caret);
-
- int iTextLen = GetWindowTextLengthW(hwndEditBox);
- mir_free(pItem->wtext);
- pItem->wtext = (wchar_t *)mir_alloc((iTextLen + 1) * sizeof(wchar_t));
- GetWindowTextW(hwndEditBox, pItem->wtext, iTextLen+1);
- pItem->wtext[iTextLen] = 0;
-
- if (pItem->hContact && pItem->hEvent) {
- DBEVENTINFO dbei = pItem->dbe;
-
- ptrA szUtf(mir_utf8encodeW(pItem->wtext));
- dbei.cbBlob = (int)mir_strlen(szUtf) + 1;
- dbei.pBlob = szUtf.get();
- db_event_edit(pItem->hEvent, &dbei);
- }
-
- MTextDestroy(pItem->data); pItem->data = 0;
- pItem->savedHeight = -1;
- pItem->checkCreate(m_hwnd);
- }
- }
-
- DestroyWindow(hwndEditBox);
- hwndEditBox = nullptr;
-}
-
-void NewstoryListData::EnsureVisible(int item)
-{
- if (scrollTopItem >= item) {
- scrollTopItem = item;
- scrollTopPixel = 0;
- }
- else {
- RECT rc;
- GetClientRect(m_hwnd, &rc);
- int height = rc.bottom - rc.top;
- int idx = scrollTopItem;
- int itemHeight = GetItemHeight(idx);
- int top = itemHeight + scrollTopPixel;
- bool found = false;
- while (top < height) {
- if (idx == item) {
- itemHeight = GetItemHeight(idx);
- found = true;
- break;
- }
- top += itemHeight;
- idx++;
- itemHeight = GetItemHeight(idx);
- }
- if (!found) {
- scrollTopItem = item;
- scrollTopPixel = 0;
- }
- }
- FixScrollPosition();
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-int NewstoryListData::FindNext(const wchar_t *pwszText)
-{
- int idx = items.FindNext(caret, Filter(Filter::EVENTONLY, pwszText));
- if (idx == -1 && caret > 0)
- idx = items.FindNext(-1, Filter(Filter::EVENTONLY, pwszText));
-
- if (idx >= 0) {
- SetSelection(idx, idx);
- SetCaret(idx);
- }
- return idx;
-}
-
-int NewstoryListData::FindPrev(const wchar_t *pwszText)
-{
- int idx = items.FindPrev(caret, Filter(Filter::EVENTONLY, pwszText));
- if (idx == -1 && caret != totalCount - 1)
- idx = items.FindPrev(totalCount, Filter(Filter::EVENTONLY, pwszText));
-
- if (idx >= 0) {
- SetSelection(idx, idx);
- SetCaret(idx);
- }
- return idx;
-}
-
-void NewstoryListData::FixScrollPosition(bool bForce)
-{
- EndEditItem(false);
-
- if (bForce || cachedMaxTopItem != scrollTopItem)
- CalcBottom();
-
- if (scrollTopItem < 0)
- scrollTopItem = 0;
-
- if (bForce || scrollTopItem > cachedMaxTopItem || (scrollTopItem == cachedMaxTopItem && scrollTopPixel < cachedMaxTopPixel)) {
- scrollTopItem = cachedMaxTopItem;
- scrollTopPixel = cachedMaxTopPixel;
- }
-}
-
-CMStringW NewstoryListData::GatherSelected(bool bTextOnly)
-{
- CMStringW ret;
-
- int eventCount = totalCount;
- for (int i = 0; i < eventCount; i++) {
- ItemData *p = GetItem(i);
- if (!p->m_bSelected)
- continue;
-
- CMStringW wszText(bTextOnly ? p->wtext : p->formatString());
- RemoveBbcodes(wszText);
- ret.Append(wszText);
- ret.Append(L"\r\n");
- }
-
- return ret;
-}
-
-ItemData* NewstoryListData::GetItem(int idx) const
-{
- if (totalCount == 0)
- return nullptr;
-
- return (bSortAscending) ? items.get(idx, false) : items.get(totalCount - 1 - idx, false);
-}
-
-int NewstoryListData::GetItemFromPixel(int yPos)
-{
- if (!totalCount)
- return -1;
-
- RECT rc;
- GetClientRect(m_hwnd, &rc);
-
- int height = rc.bottom - rc.top;
- int current = scrollTopItem;
- int top = scrollTopPixel;
- int bottom = top + GetItemHeight(current);
- while (top <= height) {
- if (yPos >= top && yPos <= bottom)
- return current;
- if (++current >= totalCount)
- break;
- top = bottom;
- bottom = top + GetItemHeight(current);
- }
-
- return -1;
-}
-
-int NewstoryListData::GetItemHeight(int index)
-{
- if (auto *pItem = LoadItem(index))
- return GetItemHeight(pItem);
- return 0;
-}
-
-int NewstoryListData::GetItemHeight(ItemData *pItem)
-{
- if (pItem->savedHeight == -1) {
- HDC hdc = GetDC(m_hwnd);
- pItem->savedHeight = PaintItem(hdc, pItem, 0, cachedWindowWidth, false);
- ReleaseDC(m_hwnd, hdc);
- }
-
- return pItem->savedHeight;
-}
-
-bool NewstoryListData::HasSelection() const
-{
- for (int i = 0; i < totalCount; i++)
- if (auto *p = GetItem(i))
- if (p->m_bSelected)
- return true;
-
- return false;
-}
-
-void NewstoryListData::HitTotal(int yCurr, int yTotal)
-{
- int i = 0, y = yCurr;
- while (i < totalCount && y > 0) {
- auto *pItem = GetItem(i++);
- if (!pItem->m_bLoaded) {
- i = totalCount * (double(yCurr) / double(yTotal));
- y = 0;
- break;
- }
- else y -= GetItemHeight(pItem);
- }
-
- scrollTopItem = i;
- scrollTopPixel = y;
- FixScrollPosition();
-}
-
-ItemData* NewstoryListData::LoadItem(int idx)
-{
- if (totalCount == 0)
- return nullptr;
-
- mir_cslock lck(m_csItems);
- return (bSortAscending) ? items.get(idx, true) : items.get(totalCount - 1 - idx, true);
-}
-
-void NewstoryListData::OpenFolder()
-{
- if (auto *pItem = GetItem(caret)) {
- if (pItem->completed()) {
- DB::EventInfo dbei(pItem->hEvent);
- DB::FILE_BLOB blob(dbei);
- CMStringW wszFile(blob.getLocalName());
- int idx = wszFile.ReverseFind('\\');
- if (idx != -1)
- wszFile.Truncate(idx);
- ::ShellExecute(nullptr, L"open", wszFile, nullptr, nullptr, SW_SHOWNORMAL);
- }
- }
-}
-
-int NewstoryListData::PaintItem(HDC hdc, ItemData *pItem, int top, int width, bool bDraw)
-{
- // remove any selections that might be created by the BBCodes parser
- MTextSendMessage(m_hwnd, pItem->data, EM_SETSEL, 0, 0);
-
- // LOGFONT lfText;
- COLORREF clText, clBack, clLine;
- int fontid, colorid;
- pItem->getFontColor(fontid, colorid);
-
- clText = g_fontTable[fontid].cl;
- if (pItem->m_bHighlighted) {
- clText = g_fontTable[FONT_HIGHLIGHT].cl;
- clBack = g_colorTable[COLOR_HIGHLIGHT_BACK].cl;
- clLine = g_colorTable[COLOR_FRAME].cl;
- }
- else if (pItem->m_bSelected) {
- clText = g_colorTable[COLOR_SELTEXT].cl;
- clBack = g_colorTable[COLOR_SELBACK].cl;
- clLine = g_colorTable[COLOR_SELFRAME].cl;
- }
- else {
- clLine = g_colorTable[COLOR_FRAME].cl;
- clBack = g_colorTable[colorid].cl;
- }
-
- pItem->checkCreate(m_hwnd);
-
- SIZE sz;
- sz.cx = width - 2;
-
- POINT pos;
- pos.x = 2;
- pos.y = top + 2;
-
- if (g_plugin.bShowType) // Message type icon
- pos.x += 18;
-
- if (g_plugin.bShowDirecction) // Message direction icon
- pos.x += 18;
-
- if (pItem->dbe.flags & DBEF_BOOKMARK) // Bookmark icon
- pos.x += 18;
-
- sz.cx -= pos.x;
- if (pItem->m_bOfflineDownloaded != 0) // Download completed icon
- sz.cx -= 18;
-
- HFONT hfnt = (HFONT)SelectObject(hdc, g_fontTable[fontid].hfnt);
- MTextMeasure(hdc, &sz, pItem->data);
- SelectObject(hdc, hfnt);
-
- int height = sz.cy + 5;
- if (!bDraw)
- return height;
-
- HBRUSH hbr = CreateSolidBrush(clBack);
- RECT rc = { 0, top, width, top + height };
- FillRect(hdc, &rc, hbr);
- DeleteObject(hbr);
-
- SetTextColor(hdc, clText);
- SetBkMode(hdc, TRANSPARENT);
-
- pos.x = 2;
- HICON hIcon;
-
- // Message type icon
- if (g_plugin.bShowType) {
- switch (pItem->dbe.eventType) {
- case EVENTTYPE_MESSAGE:
- hIcon = g_plugin.getIcon(IDI_SENDMSG);
- break;
- case EVENTTYPE_FILE:
- hIcon = Skin_LoadIcon(SKINICON_EVENT_FILE);
- break;
- case EVENTTYPE_STATUSCHANGE:
- hIcon = g_plugin.getIcon(IDI_SIGNIN);
- break;
- default:
- hIcon = g_plugin.getIcon(IDI_UNKNOWN);
- break;
- }
- DrawIconEx(hdc, pos.x, pos.y, hIcon, 16, 16, 0, 0, DI_NORMAL);
- pos.x += 18;
- }
-
- // Direction icon
- if (g_plugin.bShowDirecction) {
- if (pItem->dbe.flags & DBEF_SENT)
- hIcon = g_plugin.getIcon(IDI_MSGOUT);
- else
- hIcon = g_plugin.getIcon(IDI_MSGIN);
- DrawIconEx(hdc, pos.x, pos.y, hIcon, 16, 16, 0, 0, DI_NORMAL);
- pos.x += 18;
- }
-
- // Bookmark icon
- if (pItem->dbe.flags & DBEF_BOOKMARK) {
- DrawIconEx(hdc, pos.x, pos.y, g_plugin.getIcon(IDI_BOOKMARK), 16, 16, 0, 0, DI_NORMAL);
- pos.x += 18;
- }
-
- // Finished icon
- if (pItem->m_bOfflineDownloaded != 0) {
- if (pItem->completed())
- DrawIconEx(hdc, width - 20, pos.y, g_plugin.getIcon(IDI_OK), 16, 16, 0, 0, DI_NORMAL);
- else {
- HPEN hpn = (HPEN)SelectObject(hdc, CreatePen(PS_SOLID, 4, g_colorTable[COLOR_PROGRESS].cl));
- MoveToEx(hdc, rc.left, rc.bottom - 4, 0);
- LineTo(hdc, rc.left + (rc.right - rc.left) * int(pItem->m_bOfflineDownloaded) / 100, rc.bottom - 4);
- DeleteObject(SelectObject(hdc, hpn));
- }
- }
-
- hfnt = (HFONT)SelectObject(hdc, g_fontTable[fontid].hfnt);
- MTextDisplay(hdc, pos, sz, pItem->data);
- SelectObject(hdc, hfnt);
-
- HPEN hpn = (HPEN)SelectObject(hdc, CreatePen(PS_SOLID, 1, clLine));
- MoveToEx(hdc, rc.left, rc.bottom - 1, 0);
- LineTo(hdc, rc.right, rc.bottom - 1);
- DeleteObject(SelectObject(hdc, hpn));
- return height;
-}
-
-void NewstoryListData::RecalcScrollBar()
-{
- if (totalCount == 0)
- return;
-
- int yTotal = 0, yTop = 0, numRec = 0;
- for (int i = 0; i < totalCount; i++) {
- if (i == scrollTopItem)
- yTop = yTotal - scrollTopPixel;
-
- auto *pItem = GetItem(i);
- if (pItem->m_bLoaded) {
- yTotal += GetItemHeight(pItem);
- numRec++;
- }
- }
-
- if (numRec != totalCount) {
- double averageH = double(yTotal) / double(numRec);
- yTotal = totalCount * averageH;
- yTop = scrollTopItem * averageH;
- }
-
- SCROLLINFO si = {};
- si.cbSize = sizeof(si);
- si.fMask = SIF_ALL;
- si.nMin = 0;
- si.nMax = yTotal;
- si.nPage = cachedWindowHeight;
- si.nPos = yTop;
-
- if (si.nPos != cachedScrollbarPos || si.nMax != cachedScrollbarMax) {
- cachedScrollbarPos = si.nPos;
- cachedScrollbarMax = si.nMax;
- SetScrollInfo(m_hwnd, SB_VERT, &si, TRUE);
- }
-}
-
-void NewstoryListData::Quote()
-{
- if (pMsgDlg) {
- CMStringW wszText(GatherSelected(true));
- RemoveBbcodes(wszText);
- pMsgDlg->SetMessageText(Srmm_Quote(wszText));
-
- SetFocus(pMsgDlg->GetInput());
- }
-}
-
-void NewstoryListData::Reply()
-{
- if (pMsgDlg)
- if (auto *pItem = GetItem(caret))
- pMsgDlg->SetQuoteEvent(pItem->hEvent);
-}
-
-void NewstoryListData::ScheduleDraw()
-{
- bWasAtBottom = AtBottom();
-
- redrawTimer.Stop();
- redrawTimer.Start(30);
-}
-
-void NewstoryListData::SetCaret(int idx, bool bEnsureVisible)
-{
- if (idx < totalCount) {
- caret = idx;
- if (bEnsureVisible)
- EnsureVisible(idx);
- }
-}
-
-void NewstoryListData::SetContact(MCONTACT hContact)
-{
- m_hContact = hContact;
-
- WindowList_Add(g_hNewstoryLogs, m_hwnd, hContact);
-}
-
-void NewstoryListData::SetDialog(CSrmmBaseDialog *pDlg)
-{
- if (pMsgDlg = pDlg)
- SetContact(pDlg->m_hContact);
-}
-
-void NewstoryListData::SetPos(int pos)
-{
- SetSelection((selStart == -1) ? pos : selStart, pos);
- SetCaret(pos);
-}
-
-void NewstoryListData::SetSelection(int iFirst, int iLast)
-{
- int start = min(totalCount - 1, iFirst);
- int end = min(totalCount - 1, max(0, iLast));
- if (start > end)
- std::swap(start, end);
-
- int count = totalCount;
- for (int i = 0; i < count; ++i) {
- auto *p = GetItem(i);
- if (i >= start && i <= end)
- p->m_bSelected = true;
- else
- p->m_bSelected = false;
- }
-
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-void NewstoryListData::ToggleBookmark()
-{
- int eventCount = totalCount;
- for (int i = 0; i < eventCount; i++) {
- ItemData *p = GetItem(i);
- if (!p->m_bSelected)
- continue;
-
- if (p->dbe.flags & DBEF_BOOKMARK)
- p->dbe.flags &= ~DBEF_BOOKMARK;
- else
- p->dbe.flags |= DBEF_BOOKMARK;
- db_event_edit(p->hEvent, &p->dbe);
-
- p->setText(m_hwnd);
- }
-
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-void NewstoryListData::ToggleSelection(int iFirst, int iLast)
-{
- int start = min(totalCount - 1, iFirst);
- int end = min(totalCount - 1, max(0, iLast));
- if (start > end)
- std::swap(start, end);
-
- for (int i = start; i <= end; ++i) {
- auto *p = GetItem(i);
- p->m_bSelected = !p->m_bSelected;
- }
-
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-void NewstoryListData::TryUp(int iCount)
-{
- if (totalCount == 0)
- return;
-
- auto *pTop = GetItem(0);
- MCONTACT hContact = pTop->hContact;
- if (pTop->hEvent == 0 || hContact == 0)
- return;
-
- int i;
- for (i = 0; i < iCount; i++) {
- MEVENT hPrev = db_event_prev(hContact, pTop->hEvent);
- if (hPrev == 0)
- break;
-
- auto *p = items.insert(0);
- p->hContact = hContact;
- p->hEvent = hPrev;
- totalCount++;
- }
-
- ItemData *pPrev = nullptr;
- for (int j = 0; j < i + 1; j++) {
- auto *pItem = GetItem(j);
- pPrev = pItem->checkNext(pPrev, m_hwnd);
- }
-
- caret = 0;
- CalcBottom();
- FixScrollPosition();
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Navigation by coordinates
-
-void NewstoryListData::LineUp()
-{
- if (AtTop())
- TryUp(1);
- else
- ScrollUp(10);
-}
-
-void NewstoryListData::LineDown()
-{
- if (!AtBottom())
- ScrollDown(10);
-}
-
-void NewstoryListData::PageUp()
-{
- if (AtTop())
- TryUp(10);
- else
- ScrollUp(cachedWindowHeight);
-}
-
-void NewstoryListData::PageDown()
-{
- if (!AtBottom())
- ScrollDown(cachedWindowHeight);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Navigation by events
-
-void NewstoryListData::EventUp()
-{
- if (caret == 0)
- TryUp(1);
- else
- SetPos(caret - 1);
-}
-
-void NewstoryListData::EventDown()
-{
- if (caret < totalCount-1)
- SetPos(caret + 1);
-}
-
-void NewstoryListData::EventPageUp()
-{
- if (caret >= 10)
- SetPos(caret - 10);
- else
- TryUp(caret == 10 ? 1 : 10 - caret);
-}
-
-void NewstoryListData::EventPageDown()
-{
- if (caret < totalCount - 10)
- SetPos(caret + 10);
- else
- SetPos(totalCount - 1);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Common navigation functions
-
-void NewstoryListData::ScrollBottom()
-{
- if (!totalCount)
- return;
-
- scrollTopItem = cachedMaxTopItem;
- scrollTopPixel = cachedMaxTopPixel;
- FixScrollPosition(true);
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-void NewstoryListData::ScrollDown(int deltaY)
-{
- int iHeight = GetItemHeight(scrollTopItem) + scrollTopPixel;
- if (iHeight > deltaY)
- scrollTopPixel -= deltaY;
- else {
- deltaY -= iHeight;
-
- bool bFound = false;
- for (int i = scrollTopItem + 1; i < totalCount; i++) {
- iHeight = GetItemHeight(i);
- if (iHeight > deltaY) {
- scrollTopPixel = -deltaY;
- scrollTopItem = i;
- bFound = true;
- break;
- }
- deltaY -= iHeight;
- }
- if (!bFound)
- scrollTopItem = scrollTopPixel = 0;
- }
-
- FixScrollPosition();
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-void NewstoryListData::ScrollTop()
-{
- scrollTopItem = scrollTopPixel = 0;
- FixScrollPosition();
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-void NewstoryListData::ScrollUp(int deltaY)
-{
- int reserveY = -scrollTopPixel; // distance in pixels between the top event beginning and the window top coordinate
-
- if (reserveY >= deltaY)
- scrollTopPixel += deltaY; // stay on the same event, just move up
- else {
- deltaY -= reserveY; // move to the appropriate event first, then calculate the gap
-
- bool bFound = false;
- for (int i = scrollTopItem - 1; i >= 0; i--) {
- int iHeight = GetItemHeight(i);
- if (iHeight > deltaY) {
- scrollTopPixel = deltaY - iHeight;
- scrollTopItem = i;
- bFound = true;
- break;
- }
- deltaY -= iHeight;
- }
-
- if (!bFound)
- scrollTopItem = scrollTopPixel = 0;
- }
-
- FixScrollPosition();
- InvalidateRect(m_hwnd, 0, FALSE);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// NewStory history control window procedure
-
-LRESULT CALLBACK NewstoryListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- int idx;
- POINT pt;
- NewstoryListData *data = (NewstoryListData *)GetWindowLongPtr(hwnd, 0);
-
- MSG message = { hwnd, msg, wParam, lParam };
- switch (Hotkey_Check(&message, MODULENAME)) {
- case HOTKEY_SEEK_FORWARD:
- PostMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(IDOK, BN_CLICKED), 1);
- break;
- case HOTKEY_SEEK_BACK:
- PostMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(IDC_FINDPREV, BN_CLICKED), 1);
- break;
- case HOTKEY_SEARCH:
- PostMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(IDC_SEARCH, BN_CLICKED), 1);
- break;
- case HOTKEY_BOOKMARK:
- data->ToggleBookmark();
- return 0;
- }
-
- switch (msg) {
- case WM_CREATE:
- data = new NewstoryListData(hwnd);
- SetWindowLongPtr(hwnd, 0, (LONG_PTR)data);
- if (!g_plugin.bOptVScroll)
- SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VSCROLL);
- break;
-
- // History list control messages
- case NSM_SELECTITEMS:
- data->AddSelection(wParam, lParam);
- return 0;
-
- case NSM_SEEKTIME:
- {
- int eventCount = data->totalCount;
- for (int i = 0; i < eventCount; i++) {
- auto *p = data->GetItem(i);
- if (p->dbe.timestamp >= wParam) {
- data->SetSelection(i, i);
- data->SetCaret(i);
- break;
- }
-
- if (i == eventCount - 1) {
- data->SetSelection(i, i);
- data->SetCaret(i);
- }
- }
- }
- return TRUE;
-
- case NSM_ADDEVENT:
- data->AddEvent(wParam, lParam, 1);
- break;
-
- case NSM_SET_OPTIONS:
- data->bSortAscending = g_plugin.bSortAscending;
- data->scrollTopPixel = 0;
- data->FixScrollPosition(true);
- InvalidateRect(hwnd, 0, FALSE);
- break;
-
- case UM_ADDEVENT:
- if (data->pMsgDlg == nullptr)
- data->AddEvent(wParam, lParam, 1);
- break;
-
- case UM_EDITEVENT:
- idx = data->items.find(lParam);
- if (idx != -1) {
- auto *p = data->GetItem(idx);
- p->load(true);
- p->setText(data->m_hwnd);
- InvalidateRect(hwnd, 0, FALSE);
- }
- break;
-
- case UM_REMOVEEVENT:
- idx = data->items.find(lParam);
- if (idx != -1) {
- data->items.remove(idx);
- data->totalCount--;
- data->FixScrollPosition(true);
- InvalidateRect(hwnd, 0, FALSE);
- }
- break;
-
- case WM_SIZE:
- data->OnResize(LOWORD(lParam), HIWORD(lParam));
- break;
-
- case WM_COMMAND:
- if (NSMenu_Process(LOWORD(wParam), data))
- return 1;
- break;
-
- case WM_ERASEBKGND:
- return 1;
-
- case WM_PAINT:
- /* we get so many InvalidateRect()'s that there is no point painting,
- Windows in theory shouldn't queue up WM_PAINTs in this case but it does so
- we'll just ignore them */
- if (IsWindowVisible(hwnd)) {
- PAINTSTRUCT ps;
- HDC hdcWindow = BeginPaint(hwnd, &ps);
-
- RECT rc;
- GetClientRect(hwnd, &rc);
-
- HDC hdc = CreateCompatibleDC(hdcWindow);
- HBITMAP hbmSave = (HBITMAP)SelectObject(hdc, CreateCompatibleBitmap(hdcWindow, rc.right - rc.left, rc.bottom - rc.top));
-
- int height = rc.bottom - rc.top;
- int width = rc.right - rc.left;
- int top = data->scrollTopPixel;
-
- for (idx = data->scrollTopItem; top < height && idx < data->totalCount; idx++)
- top += data->PaintItem(hdc, data->LoadItem(idx), top, width, !data->hwndEditBox || data->caret != idx);
-
- data->cachedMaxDrawnItem = idx;
-
- if (top <= height) {
- RECT rc2;
- SetRect(&rc2, 0, top, width, height);
-
- HBRUSH hbr = CreateSolidBrush(g_colorTable[COLOR_BACK].cl);
- FillRect(hdc, &rc2, hbr);
- DeleteObject(hbr);
- }
-
- if (g_plugin.bOptVScroll)
- data->RecalcScrollBar();
- if (g_plugin.bDrawEdge)
- DrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
-
- BitBlt(hdcWindow, 0, 0, rc.right, rc.bottom, hdc, 0, 0, SRCCOPY);
- DeleteObject(SelectObject(hdc, hbmSave));
- DeleteDC(hdc);
- EndPaint(hwnd, &ps);
- }
- break;
-
- case WM_CONTEXTMENU:
- pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
- if (pt.x == -1 && pt.y == -1)
- GetCursorPos(&pt);
-
- POINT pt2 = pt;
- ScreenToClient(hwnd, &pt2);
-
- idx = data->GetItemFromPixel(pt2.y);
- if (idx != -1) {
- if (data->caret != idx)
- data->EndEditItem(false);
- data->SetCaret(idx);
- if (!data->HasSelection())
- data->SetSelection(idx, idx);
- }
- data->OnContextMenu(idx, pt);
- break;
-
- case WM_KILLFOCUS:
- if (wParam && (HWND)wParam != data->hwndEditBox)
- data->EndEditItem(false);
- if (data->pMsgDlg && ((HWND)wParam == data->pMsgDlg->GetInput() || (HWND)wParam == data->pMsgDlg->GetHwnd()))
- data->ClearSelection(0, -1);
- return 0;
-
- case WM_SETFOCUS:
- return 0;
-
- case WM_GETDLGCODE:
- if (lParam) {
- MSG *msg2 = (MSG *)lParam;
- if (msg2->message == WM_KEYDOWN) {
- if (msg2->wParam == VK_TAB)
- return 0;
- if (msg2->wParam == VK_ESCAPE && !data->hwndEditBox)
- return 0;
- }
- else if (msg2->message == WM_CHAR) {
- if (msg2->wParam == '\t')
- return 0;
- if (msg2->wParam == 27 && !data->hwndEditBox)
- return 0;
- }
- }
- return DLGC_WANTMESSAGE;
-
- case WM_KEYDOWN:
- {
- bool isShift = (GetKeyState(VK_SHIFT) & 0x80) != 0;
- bool isCtrl = (GetKeyState(VK_CONTROL) & 0x80) != 0;
-
- if (!data->bWasShift && isShift)
- data->selStart = data->caret;
- else if (data->bWasShift && !isShift)
- data->selStart = -1;
- data->bWasShift = isShift;
-
- switch (wParam) {
- case VK_UP:
- if (g_plugin.bHppCompat)
- data->EventUp();
- else
- data->LineUp();
- break;
-
- case VK_DOWN:
- if (g_plugin.bHppCompat)
- data->EventDown();
- else
- data->LineDown();
- break;
-
- case VK_PRIOR:
- if (isCtrl)
- data->ScrollTop();
- else if (g_plugin.bHppCompat)
- data->EventPageUp();
- else
- data->PageUp();
- break;
-
- case VK_NEXT:
- if (isCtrl)
- data->ScrollBottom();
- else if (g_plugin.bHppCompat)
- data->EventPageDown();
- else
- data->PageDown();
- break;
-
- case VK_HOME:
- data->ScrollTop();
- break;
-
- case VK_END:
- data->ScrollBottom();
- break;
-
- case VK_F2:
- data->BeginEditItem();
- break;
-
- case VK_ESCAPE:
- if (data->hwndEditBox)
- data->EndEditItem(false);
- break;
-
- case VK_DELETE:
- data->DeleteItems();
- break;
-
- case VK_INSERT:
- case 'C':
- if (isCtrl)
- data->Copy();
- break;
-
- case 'A':
- if (isCtrl)
- data->AddSelection(0, data->totalCount);
- break;
- }
- }
- break;
-
- case WM_LBUTTONDOWN:
- pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
- idx = data->GetItemFromPixel(pt.y);
- if (idx >= 0) {
- if (data->caret != idx)
- data->EndEditItem(false);
-
- auto *pItem = data->LoadItem(idx);
-
- if (wParam & MK_CONTROL) {
- data->ToggleSelection(idx, idx);
- data->SetCaret(idx);
- }
- else if (wParam & MK_SHIFT) {
- data->AddSelection(data->caret, idx);
- data->SetCaret(idx);
- }
- else {
- pt.y -= pItem->savedTop;
-
- CMStringW wszUrl;
- if (pItem->isLink(hwnd, pt, &wszUrl)) {
- Utils_OpenUrlW(wszUrl);
- return 0;
- }
-
- data->selStart = idx;
- data->SetSelection(idx, idx);
- data->SetCaret(idx);
- }
- }
- SetFocus(hwnd);
- return 0;
-
- case WM_LBUTTONUP:
- pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
- idx = data->GetItemFromPixel(pt.y);
- if (idx >= 0)
- data->selStart = -1;
- break;
-
- case WM_LBUTTONDBLCLK:
- pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
- idx = data->GetItemFromPixel(pt.y);
- if (idx >= 0) {
- if (data->caret != idx)
- data->EndEditItem(false);
-
- auto *pItem = data->LoadItem(idx);
- pt.y -= pItem->savedTop;
-
- if (pItem->m_bOfflineFile) {
- Srmm_DownloadOfflineFile(pItem->hContact, pItem->hEvent, OFD_DOWNLOAD | OFD_RUN);
- return 0;
- }
-
- if (data->caret == idx) {
- data->BeginEditItem();
- return 0;
- }
- }
-
- SetFocus(hwnd);
- return 0;
-
- case WM_MOUSEMOVE:
- pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
- idx = data->GetItemFromPixel(pt.y);
- if (idx >= 0) {
- auto *pItem = data->LoadItem(idx);
- MTextSendMessage(hwnd, pItem->data, msg, wParam, lParam);
-
- HCURSOR hOldCursor = GetCursor();
- HCURSOR hNewCursor = LoadCursor(0, (pItem->isLink(hwnd, pt) || pItem->m_bOfflineFile) ? IDC_HAND : IDC_ARROW);
- if (hOldCursor != hNewCursor)
- SetCursor(hNewCursor);
-
- if (data->selStart != -1) {
- data->SetSelection(data->selStart, idx);
- InvalidateRect(hwnd, 0, FALSE);
- }
- }
- break;
-
- case WM_MOUSEWHEEL:
- if ((short)HIWORD(wParam) < 0)
- data->LineDown();
- else
- data->LineUp();
- return TRUE;
-
- case WM_VSCROLL:
- {
- int s_scrollTopItem = data->scrollTopItem;
- int s_scrollTopPixel = data->scrollTopPixel;
-
- switch (LOWORD(wParam)) {
- case SB_LINEUP:
- if (g_plugin.bHppCompat)
- data->EventUp();
- else
- data->LineUp();
- break;
-
- case SB_LINEDOWN:
- if (g_plugin.bHppCompat)
- data->EventDown();
- else
- data->LineDown();
- break;
-
- case SB_PAGEUP:
- if (g_plugin.bHppCompat)
- data->EventPageUp();
- else
- data->PageUp();
- break;
-
- case SB_PAGEDOWN:
- if (g_plugin.bHppCompat)
- data->EventPageDown();
- else
- data->PageDown();
- break;
-
- case SB_BOTTOM:
- data->ScrollBottom();
- break;
-
- case SB_TOP:
- data->ScrollTop();
- break;
-
- case SB_THUMBTRACK:
- SCROLLINFO si;
- si.cbSize = sizeof(si);
- si.fMask = SIF_ALL;
- GetScrollInfo(hwnd, SB_VERT, &si);
- data->HitTotal(si.nTrackPos, si.nMax);
- break;
-
- default:
- return 0;
- }
-
- if (s_scrollTopItem != data->scrollTopItem || s_scrollTopPixel != data->scrollTopPixel)
- InvalidateRect(hwnd, 0, FALSE);
- }
- break;
-
- case WM_CTLCOLORSTATIC:
- case WM_CTLCOLOREDIT:
- if (lParam == INT_PTR(data->hwndEditBox)) {
- SetBkColor((HDC)wParam, g_colorTable[COLOR_SELBACK].cl);
- return (LRESULT)g_plugin.hBackBrush;
- }
- break;
-
- case WM_DESTROY:
- WindowList_Add(g_hNewstoryLogs, hwnd);
- delete data;
- SetWindowLongPtr(hwnd, 0, 0);
- break;
- }
-
- return DefWindowProc(hwnd, msg, wParam, lParam);
-}
-
-void InitNewstoryControl()
-{
- htuLog = MTextRegister("Newstory", MTEXT_FANCY_DEFAULT | MTEXT_SYSTEM_HICONS | MTEXT_FANCY_SMILEYS);
-
- WNDCLASS wndclass = {};
- wndclass.style = /*CS_HREDRAW | CS_VREDRAW | */CS_DBLCLKS | CS_GLOBALCLASS;
- wndclass.lpfnWndProc = NewstoryListWndProc;
- wndclass.cbWndExtra = sizeof(void *);
- wndclass.hInstance = g_plugin.getInst();
- wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
- wndclass.lpszClassName = _T(NEWSTORYLIST_CLASS);
- RegisterClass(&wndclass);
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+#define AVERAGE_ITEM_HEIGHT 100
+
+HANDLE htuLog = 0;
+
+void InitHotkeys()
+{
+ HOTKEYDESC hkd = {};
+ hkd.szSection.a = MODULENAME;
+
+ hkd.szDescription.a = LPGEN("Toggle bookmark");
+ hkd.pszName = "ns_bookmark";
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, 'B') | HKF_MIRANDA_LOCAL;
+ hkd.lParam = HOTKEY_BOOKMARK;
+ g_plugin.addHotkey(&hkd);
+
+ hkd.szDescription.a = LPGEN("Search");
+ hkd.pszName = "ns_search";
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL, 'F') | HKF_MIRANDA_LOCAL;
+ hkd.lParam = HOTKEY_SEARCH;
+ g_plugin.addHotkey(&hkd);
+
+ hkd.szDescription.a = LPGEN("Search forward");
+ hkd.pszName = "ns_seek_forward";
+ hkd.DefHotKey = HOTKEYCODE(0, VK_F3) | HKF_MIRANDA_LOCAL;
+ hkd.lParam = HOTKEY_SEEK_FORWARD;
+ g_plugin.addHotkey(&hkd);
+
+ hkd.szDescription.a = LPGEN("Search backward");
+ hkd.pszName = "ns_seek_back";
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_SHIFT, VK_F3) | HKF_MIRANDA_LOCAL;
+ hkd.lParam = HOTKEY_SEEK_BACK;
+ g_plugin.addHotkey(&hkd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Control utilities, types and constants
+
+NewstoryListData::NewstoryListData(HWND _1) :
+ m_hwnd(_1),
+ redrawTimer(Miranda_GetSystemWindow(), LPARAM(this))
+{
+ items.setOwner(_1);
+
+ bSortAscending = g_plugin.bSortAscending;
+
+ redrawTimer.OnEvent = Callback(this, &NewstoryListData::onTimer_Draw);
+}
+
+void NewstoryListData::onTimer_Draw(CTimer *pTimer)
+{
+ pTimer->Stop();
+
+ if (bWasAtBottom)
+ EnsureVisible(totalCount - 1);
+
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+void NewstoryListData::OnContextMenu(int index, POINT pt)
+{
+ HMENU hMenu = NSMenu_Build(this, (index == -1) ? 0 : LoadItem(index));
+
+ if (pMsgDlg != nullptr && pMsgDlg->isChat())
+ Chat_CreateMenu(hMenu, pMsgDlg->getChat(), nullptr);
+
+ TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, m_hwnd, nullptr);
+ Menu_DestroyNestedMenu(hMenu);
+}
+
+void NewstoryListData::OnResize(int newWidth, int newHeight)
+{
+ bool bDraw = false;
+ if (newWidth != cachedWindowWidth) {
+ cachedWindowWidth = newWidth;
+ for (int i = 0; i < totalCount; i++)
+ GetItem(i)->savedHeight = -1;
+ bDraw = true;
+ }
+
+ if (newHeight != cachedWindowHeight) {
+ cachedWindowHeight = newHeight;
+ FixScrollPosition(true);
+ bDraw = true;
+ }
+
+ if (bDraw)
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+void NewstoryListData::AddChatEvent(SESSION_INFO *si, const LOGINFO *lin)
+{
+ ScheduleDraw();
+ items.addChatEvent(si, lin);
+ totalCount++;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NewstoryListData::AddEvent(MCONTACT hContact, MEVENT hFirstEvent, int iCount)
+{
+ ScheduleDraw();
+ items.addEvent(hContact, hFirstEvent, iCount);
+ totalCount = items.getCount();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NewstoryListData::AddResults(const OBJLIST<SearchResult> &results)
+{
+ ScheduleDraw();
+ items.addResults(results);
+ totalCount = items.getCount();
+}
+
+void NewstoryListData::AddSelection(int iFirst, int iLast)
+{
+ int start = min(totalCount - 1, iFirst);
+ int end = min(totalCount - 1, max(0, iLast));
+ if (start > end)
+ std::swap(start, end);
+
+ for (int i = start; i <= end; ++i)
+ if (auto *p = GetItem(i))
+ p->m_bSelected = true;
+
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+bool NewstoryListData::AtBottom(void) const
+{
+ if (scrollTopItem > cachedMaxTopItem)
+ return true;
+
+ if (scrollTopItem == cachedMaxTopItem && cachedMaxTopPixel >= scrollTopPixel)
+ return true;
+
+ return false;
+}
+
+bool NewstoryListData::AtTop(void) const
+{
+ if (scrollTopItem < 0)
+ return true;
+
+ if (scrollTopItem == 0 && scrollTopPixel == 0)
+ return true;
+
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Edit box window procedure
+
+static LRESULT CALLBACK HistoryEditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ auto *pData = (NewstoryListData *)GetWindowLongPtr(GetParent(hwnd), 0);
+
+ switch (msg) {
+ case WM_KEYDOWN:
+ switch (wParam) {
+ case VK_RETURN:
+ pData->EndEditItem(true);
+ return 0;
+ case VK_ESCAPE:
+ pData->EndEditItem(false);
+ return 0;
+ }
+ break;
+
+ case WM_GETDLGCODE:
+ if (lParam) {
+ MSG *msg2 = (MSG *)lParam;
+ if (msg2->message == WM_KEYDOWN && msg2->wParam == VK_TAB)
+ return 0;
+ if (msg2->message == WM_CHAR && msg2->wParam == '\t')
+ return 0;
+ }
+ return DLGC_WANTMESSAGE;
+
+ case WM_KILLFOCUS:
+ pData->EndEditItem(false);
+ return 0;
+ }
+
+ return mir_callNextSubclass(hwnd, HistoryEditWndProc, msg, wParam, lParam);
+}
+
+void NewstoryListData::BeginEditItem()
+{
+ if (hwndEditBox)
+ EndEditItem(false);
+
+ if (scrollTopItem > caret)
+ return;
+
+ ItemData *item = LoadItem(caret);
+ if (item->dbe.eventType != EVENTTYPE_MESSAGE)
+ return;
+
+ RECT rc; GetClientRect(m_hwnd, &rc);
+ int height = rc.bottom - rc.top;
+
+ int top = scrollTopPixel;
+ int idx = scrollTopItem;
+ int itemHeight = GetItemHeight(idx);
+ while (top < height) {
+ if (idx == caret)
+ break;
+
+ top += itemHeight;
+ idx++;
+ itemHeight = GetItemHeight(idx);
+ }
+
+ int fontid, colorid;
+ item->getFontColor(fontid, colorid);
+
+ // #4012 make sure that both single & double CRLF are now double
+ CMStringW wszText(item->getWBuf());
+ wszText.Replace(L"\r\n", L"\n");
+ wszText.Replace(L"\n", L"\r\n");
+
+ uint32_t dwStyle = WS_CHILD | WS_BORDER | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL;
+ hwndEditBox = CreateWindow(L"EDIT", wszText, dwStyle, 0, top, rc.right - rc.left, itemHeight, m_hwnd, NULL, g_plugin.getInst(), NULL);
+ mir_subclassWindow(hwndEditBox, HistoryEditWndProc);
+ SendMessage(hwndEditBox, WM_SETFONT, (WPARAM)g_fontTable[fontid].hfnt, 0);
+ SendMessage(hwndEditBox, EM_SETMARGINS, EC_RIGHTMARGIN, 100);
+ ShowWindow(hwndEditBox, SW_SHOW);
+ SetFocus(hwndEditBox);
+ SetForegroundWindow(hwndEditBox);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NewstoryListData::CalcBottom()
+{
+ int maxTopItem = totalCount, tmp = 0;
+ while (maxTopItem > 0 && tmp < cachedWindowHeight)
+ tmp += GetItemHeight(--maxTopItem);
+ cachedMaxTopItem = maxTopItem;
+ cachedMaxTopPixel = (cachedWindowHeight < tmp) ? cachedWindowHeight - tmp : 0;
+}
+
+void NewstoryListData::Clear()
+{
+ items.clear();
+ totalCount = 0;
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+void NewstoryListData::ClearSelection(int iFirst, int iLast)
+{
+ int start = min(0, iFirst);
+ int end = (iLast <= 0) ? totalCount - 1 : iLast;
+ if (start > end)
+ std::swap(start, end);
+
+ for (int i = start; i <= end; ++i)
+ if (auto *pItem = GetItem(i))
+ pItem->m_bSelected = false;
+
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+void NewstoryListData::Copy(bool bTextOnly)
+{
+ Utils_ClipboardCopy(GatherSelected(bTextOnly));
+}
+
+void NewstoryListData::CopyPath()
+{
+ if (auto *pItem = GetItem(caret))
+ if (pItem->completed()) {
+ DB::EventInfo dbei(pItem->hEvent);
+ DB::FILE_BLOB blob(dbei);
+ Utils_ClipboardCopy(blob.getLocalName());
+ }
+}
+
+void NewstoryListData::CopyUrl()
+{
+ if (auto *pItem = GetItem(caret))
+ Srmm_DownloadOfflineFile(pItem->hContact, pItem->hEvent, OFD_COPYURL);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Delete events dialog
+
+class CDeleteEventsDlg : public CDlgBase
+{
+ MCONTACT m_hContact;
+ CCtrlCheck chkDelHistory, chkForEveryone;
+
+public:
+ bool bDelHistory = false, bForEveryone = false;
+
+ CDeleteEventsDlg(MCONTACT hContact) :
+ CDlgBase(g_plugin, IDD_EMPTYHISTORY),
+ chkDelHistory(this, IDC_DELSERVERHISTORY),
+ chkForEveryone(this, IDC_BOTH)
+ {
+ if (char *szProto = Proto_GetBaseAccountName(hContact)) {
+ bDelHistory = ProtoServiceExists(szProto, PS_EMPTY_SRV_HISTORY);
+ bForEveryone = (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_DELETEFORALL) != 0;
+ }
+ }
+
+ bool OnInitDialog() override
+ {
+ chkDelHistory.SetState(bDelHistory);
+ chkDelHistory.Enable(bDelHistory);
+
+ bool bEnabled = bDelHistory && bForEveryone;
+ chkForEveryone.SetState(!bEnabled);
+ chkForEveryone.Enable(bEnabled);
+
+ LOGFONT lf;
+ HFONT hFont = (HFONT)SendDlgItemMessage(m_hwnd, IDOK, WM_GETFONT, 0, 0);
+ GetObject(hFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ SendDlgItemMessage(m_hwnd, IDC_TOPLINE, WM_SETFONT, (WPARAM)CreateFontIndirect(&lf), 0);
+
+ wchar_t szFormat[256], szFinal[256];
+ GetDlgItemText(m_hwnd, IDC_TOPLINE, szFormat, _countof(szFormat));
+ mir_snwprintf(szFinal, szFormat, Clist_GetContactDisplayName(m_hContact));
+ SetDlgItemText(m_hwnd, IDC_TOPLINE, szFinal);
+
+ SetFocus(GetDlgItem(m_hwnd, IDNO));
+ SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ bDelHistory = chkDelHistory.IsChecked();
+ bForEveryone = chkForEveryone.IsChecked();
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ DeleteObject((HFONT)SendDlgItemMessage(m_hwnd, IDC_TOPLINE, WM_GETFONT, 0, 0));
+ }
+};
+
+void NewstoryListData::DeleteItems(void)
+{
+ CDeleteEventsDlg dlg(m_hContact);
+ if (IDOK != dlg.DoModal())
+ return;
+
+ g_plugin.bDisableDelete = true;
+
+ int firstSel = -1, flags = 0;
+ if (dlg.bDelHistory)
+ flags |= CDF_DEL_HISTORY;
+ if (dlg.bForEveryone)
+ flags |= CDF_FOR_EVERYONE;
+
+ for (int i = totalCount - 1; i >= 0; i--) {
+ auto *p = GetItem(i);
+ if (!p->m_bSelected)
+ continue;
+
+ if (p->hEvent)
+ db_event_delete(p->hEvent, flags);
+ items.remove(i);
+ totalCount--;
+ firstSel = i;
+ }
+
+ g_plugin.bDisableDelete = false;
+
+ if (firstSel != -1) {
+ SetCaret(firstSel, false);
+ SetSelection(firstSel, firstSel);
+ FixScrollPosition(true);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NewstoryListData::Download(int options)
+{
+ if (auto *p = LoadItem(caret))
+ Srmm_DownloadOfflineFile(p->hContact, p->hEvent, options);
+}
+
+void NewstoryListData::EndEditItem(bool bAccept)
+{
+ if (hwndEditBox == nullptr)
+ return;
+
+ if (bAccept) {
+ if ((GetWindowLong(hwndEditBox, GWL_STYLE) & ES_READONLY) == 0) {
+ auto *pItem = GetItem(caret);
+
+ int iTextLen = GetWindowTextLengthW(hwndEditBox);
+ mir_free(pItem->wtext);
+ pItem->wtext = (wchar_t *)mir_alloc((iTextLen + 1) * sizeof(wchar_t));
+ GetWindowTextW(hwndEditBox, pItem->wtext, iTextLen+1);
+ pItem->wtext[iTextLen] = 0;
+
+ if (pItem->hContact && pItem->hEvent) {
+ DBEVENTINFO dbei = pItem->dbe;
+
+ ptrA szUtf(mir_utf8encodeW(pItem->wtext));
+ dbei.cbBlob = (int)mir_strlen(szUtf) + 1;
+ dbei.pBlob = szUtf.get();
+ db_event_edit(pItem->hEvent, &dbei);
+ }
+
+ MTextDestroy(pItem->data); pItem->data = 0;
+ pItem->savedHeight = -1;
+ pItem->checkCreate(m_hwnd);
+ }
+ }
+
+ DestroyWindow(hwndEditBox);
+ hwndEditBox = nullptr;
+}
+
+void NewstoryListData::EnsureVisible(int item)
+{
+ if (scrollTopItem >= item) {
+ scrollTopItem = item;
+ scrollTopPixel = 0;
+ }
+ else {
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ int height = rc.bottom - rc.top;
+ int idx = scrollTopItem;
+ int itemHeight = GetItemHeight(idx);
+ int top = itemHeight + scrollTopPixel;
+ bool found = false;
+ while (top < height) {
+ if (idx == item) {
+ itemHeight = GetItemHeight(idx);
+ found = true;
+ break;
+ }
+ top += itemHeight;
+ idx++;
+ itemHeight = GetItemHeight(idx);
+ }
+ if (!found) {
+ scrollTopItem = item;
+ scrollTopPixel = 0;
+ }
+ }
+ FixScrollPosition();
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+int NewstoryListData::FindNext(const wchar_t *pwszText)
+{
+ int idx = items.FindNext(caret, Filter(Filter::EVENTONLY, pwszText));
+ if (idx == -1 && caret > 0)
+ idx = items.FindNext(-1, Filter(Filter::EVENTONLY, pwszText));
+
+ if (idx >= 0) {
+ SetSelection(idx, idx);
+ SetCaret(idx);
+ }
+ return idx;
+}
+
+int NewstoryListData::FindPrev(const wchar_t *pwszText)
+{
+ int idx = items.FindPrev(caret, Filter(Filter::EVENTONLY, pwszText));
+ if (idx == -1 && caret != totalCount - 1)
+ idx = items.FindPrev(totalCount, Filter(Filter::EVENTONLY, pwszText));
+
+ if (idx >= 0) {
+ SetSelection(idx, idx);
+ SetCaret(idx);
+ }
+ return idx;
+}
+
+void NewstoryListData::FixScrollPosition(bool bForce)
+{
+ EndEditItem(false);
+
+ if (bForce || cachedMaxTopItem != scrollTopItem)
+ CalcBottom();
+
+ if (scrollTopItem < 0)
+ scrollTopItem = 0;
+
+ if (bForce || scrollTopItem > cachedMaxTopItem || (scrollTopItem == cachedMaxTopItem && scrollTopPixel < cachedMaxTopPixel)) {
+ scrollTopItem = cachedMaxTopItem;
+ scrollTopPixel = cachedMaxTopPixel;
+ }
+}
+
+CMStringW NewstoryListData::GatherSelected(bool bTextOnly)
+{
+ CMStringW ret;
+
+ int eventCount = totalCount;
+ for (int i = 0; i < eventCount; i++) {
+ ItemData *p = GetItem(i);
+ if (!p->m_bSelected)
+ continue;
+
+ CMStringW wszText(bTextOnly ? p->wtext : p->formatString());
+ RemoveBbcodes(wszText);
+ ret.Append(wszText);
+ ret.Append(L"\r\n");
+ }
+
+ return ret;
+}
+
+ItemData* NewstoryListData::GetItem(int idx) const
+{
+ if (totalCount == 0)
+ return nullptr;
+
+ return (bSortAscending) ? items.get(idx, false) : items.get(totalCount - 1 - idx, false);
+}
+
+int NewstoryListData::GetItemFromPixel(int yPos)
+{
+ if (!totalCount)
+ return -1;
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+
+ int height = rc.bottom - rc.top;
+ int current = scrollTopItem;
+ int top = scrollTopPixel;
+ int bottom = top + GetItemHeight(current);
+ while (top <= height) {
+ if (yPos >= top && yPos <= bottom)
+ return current;
+ if (++current >= totalCount)
+ break;
+ top = bottom;
+ bottom = top + GetItemHeight(current);
+ }
+
+ return -1;
+}
+
+int NewstoryListData::GetItemHeight(int index)
+{
+ if (auto *pItem = LoadItem(index))
+ return GetItemHeight(pItem);
+ return 0;
+}
+
+int NewstoryListData::GetItemHeight(ItemData *pItem)
+{
+ if (pItem->savedHeight == -1) {
+ HDC hdc = GetDC(m_hwnd);
+ pItem->savedHeight = PaintItem(hdc, pItem, 0, cachedWindowWidth, false);
+ ReleaseDC(m_hwnd, hdc);
+ }
+
+ return pItem->savedHeight;
+}
+
+bool NewstoryListData::HasSelection() const
+{
+ for (int i = 0; i < totalCount; i++)
+ if (auto *p = GetItem(i))
+ if (p->m_bSelected)
+ return true;
+
+ return false;
+}
+
+void NewstoryListData::HitTotal(int yCurr, int yTotal)
+{
+ int i = 0, y = yCurr;
+ while (i < totalCount && y > 0) {
+ auto *pItem = GetItem(i++);
+ if (!pItem->m_bLoaded) {
+ i = totalCount * (double(yCurr) / double(yTotal));
+ y = 0;
+ break;
+ }
+ else y -= GetItemHeight(pItem);
+ }
+
+ scrollTopItem = i;
+ scrollTopPixel = y;
+ FixScrollPosition();
+}
+
+ItemData* NewstoryListData::LoadItem(int idx)
+{
+ if (totalCount == 0)
+ return nullptr;
+
+ mir_cslock lck(m_csItems);
+ return (bSortAscending) ? items.get(idx, true) : items.get(totalCount - 1 - idx, true);
+}
+
+void NewstoryListData::OpenFolder()
+{
+ if (auto *pItem = GetItem(caret)) {
+ if (pItem->completed()) {
+ DB::EventInfo dbei(pItem->hEvent);
+ DB::FILE_BLOB blob(dbei);
+ CMStringW wszFile(blob.getLocalName());
+ int idx = wszFile.ReverseFind('\\');
+ if (idx != -1)
+ wszFile.Truncate(idx);
+ ::ShellExecute(nullptr, L"open", wszFile, nullptr, nullptr, SW_SHOWNORMAL);
+ }
+ }
+}
+
+int NewstoryListData::PaintItem(HDC hdc, ItemData *pItem, int top, int width, bool bDraw)
+{
+ // remove any selections that might be created by the BBCodes parser
+ MTextSendMessage(m_hwnd, pItem->data, EM_SETSEL, 0, 0);
+
+ // LOGFONT lfText;
+ COLORREF clText, clBack, clLine;
+ int fontid, colorid;
+ pItem->getFontColor(fontid, colorid);
+
+ clText = g_fontTable[fontid].cl;
+ if (pItem->m_bHighlighted) {
+ clText = g_fontTable[FONT_HIGHLIGHT].cl;
+ clBack = g_colorTable[COLOR_HIGHLIGHT_BACK].cl;
+ clLine = g_colorTable[COLOR_FRAME].cl;
+ }
+ else if (pItem->m_bSelected) {
+ clText = g_colorTable[COLOR_SELTEXT].cl;
+ clBack = g_colorTable[COLOR_SELBACK].cl;
+ clLine = g_colorTable[COLOR_SELFRAME].cl;
+ }
+ else {
+ clLine = g_colorTable[COLOR_FRAME].cl;
+ clBack = g_colorTable[colorid].cl;
+ }
+
+ pItem->checkCreate(m_hwnd);
+
+ SIZE sz;
+ sz.cx = width - 2;
+
+ POINT pos;
+ pos.x = 2;
+ pos.y = top + 2;
+
+ if (g_plugin.bShowType) // Message type icon
+ pos.x += 18;
+
+ if (g_plugin.bShowDirecction) // Message direction icon
+ pos.x += 18;
+
+ if (pItem->dbe.flags & DBEF_BOOKMARK) // Bookmark icon
+ pos.x += 18;
+
+ sz.cx -= pos.x;
+ if (pItem->m_bOfflineDownloaded != 0) // Download completed icon
+ sz.cx -= 18;
+
+ HFONT hfnt = (HFONT)SelectObject(hdc, g_fontTable[fontid].hfnt);
+ MTextMeasure(hdc, &sz, pItem->data);
+ SelectObject(hdc, hfnt);
+
+ int height = sz.cy + 5;
+ if (!bDraw)
+ return height;
+
+ HBRUSH hbr = CreateSolidBrush(clBack);
+ RECT rc = { 0, top, width, top + height };
+ FillRect(hdc, &rc, hbr);
+ DeleteObject(hbr);
+
+ SetTextColor(hdc, clText);
+ SetBkMode(hdc, TRANSPARENT);
+
+ pos.x = 2;
+ HICON hIcon;
+
+ // Message type icon
+ if (g_plugin.bShowType) {
+ switch (pItem->dbe.eventType) {
+ case EVENTTYPE_MESSAGE:
+ hIcon = g_plugin.getIcon(IDI_SENDMSG);
+ break;
+ case EVENTTYPE_FILE:
+ hIcon = Skin_LoadIcon(SKINICON_EVENT_FILE);
+ break;
+ case EVENTTYPE_STATUSCHANGE:
+ hIcon = g_plugin.getIcon(IDI_SIGNIN);
+ break;
+ default:
+ hIcon = g_plugin.getIcon(IDI_UNKNOWN);
+ break;
+ }
+ DrawIconEx(hdc, pos.x, pos.y, hIcon, 16, 16, 0, 0, DI_NORMAL);
+ pos.x += 18;
+ }
+
+ // Direction icon
+ if (g_plugin.bShowDirecction) {
+ if (pItem->dbe.flags & DBEF_SENT)
+ hIcon = g_plugin.getIcon(IDI_MSGOUT);
+ else
+ hIcon = g_plugin.getIcon(IDI_MSGIN);
+ DrawIconEx(hdc, pos.x, pos.y, hIcon, 16, 16, 0, 0, DI_NORMAL);
+ pos.x += 18;
+ }
+
+ // Bookmark icon
+ if (pItem->dbe.flags & DBEF_BOOKMARK) {
+ DrawIconEx(hdc, pos.x, pos.y, g_plugin.getIcon(IDI_BOOKMARK), 16, 16, 0, 0, DI_NORMAL);
+ pos.x += 18;
+ }
+
+ // Finished icon
+ if (pItem->m_bOfflineDownloaded != 0) {
+ if (pItem->completed())
+ DrawIconEx(hdc, width - 20, pos.y, g_plugin.getIcon(IDI_OK), 16, 16, 0, 0, DI_NORMAL);
+ else {
+ HPEN hpn = (HPEN)SelectObject(hdc, CreatePen(PS_SOLID, 4, g_colorTable[COLOR_PROGRESS].cl));
+ MoveToEx(hdc, rc.left, rc.bottom - 4, 0);
+ LineTo(hdc, rc.left + (rc.right - rc.left) * int(pItem->m_bOfflineDownloaded) / 100, rc.bottom - 4);
+ DeleteObject(SelectObject(hdc, hpn));
+ }
+ }
+
+ hfnt = (HFONT)SelectObject(hdc, g_fontTable[fontid].hfnt);
+ MTextDisplay(hdc, pos, sz, pItem->data);
+ SelectObject(hdc, hfnt);
+
+ HPEN hpn = (HPEN)SelectObject(hdc, CreatePen(PS_SOLID, 1, clLine));
+ MoveToEx(hdc, rc.left, rc.bottom - 1, 0);
+ LineTo(hdc, rc.right, rc.bottom - 1);
+ DeleteObject(SelectObject(hdc, hpn));
+ return height;
+}
+
+void NewstoryListData::RecalcScrollBar()
+{
+ if (totalCount == 0)
+ return;
+
+ int yTotal = 0, yTop = 0, numRec = 0;
+ for (int i = 0; i < totalCount; i++) {
+ if (i == scrollTopItem)
+ yTop = yTotal - scrollTopPixel;
+
+ auto *pItem = GetItem(i);
+ if (pItem->m_bLoaded) {
+ yTotal += GetItemHeight(pItem);
+ numRec++;
+ }
+ }
+
+ if (numRec != totalCount) {
+ double averageH = double(yTotal) / double(numRec);
+ yTotal = totalCount * averageH;
+ yTop = scrollTopItem * averageH;
+ }
+
+ SCROLLINFO si = {};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_ALL;
+ si.nMin = 0;
+ si.nMax = yTotal;
+ si.nPage = cachedWindowHeight;
+ si.nPos = yTop;
+
+ if (si.nPos != cachedScrollbarPos || si.nMax != cachedScrollbarMax) {
+ cachedScrollbarPos = si.nPos;
+ cachedScrollbarMax = si.nMax;
+ SetScrollInfo(m_hwnd, SB_VERT, &si, TRUE);
+ }
+}
+
+void NewstoryListData::Quote()
+{
+ if (pMsgDlg) {
+ CMStringW wszText(GatherSelected(true));
+ RemoveBbcodes(wszText);
+ pMsgDlg->SetMessageText(Srmm_Quote(wszText));
+
+ SetFocus(pMsgDlg->GetInput());
+ }
+}
+
+void NewstoryListData::Reply()
+{
+ if (pMsgDlg)
+ if (auto *pItem = GetItem(caret))
+ pMsgDlg->SetQuoteEvent(pItem->hEvent);
+}
+
+void NewstoryListData::ScheduleDraw()
+{
+ bWasAtBottom = AtBottom();
+
+ redrawTimer.Stop();
+ redrawTimer.Start(30);
+}
+
+void NewstoryListData::SetCaret(int idx, bool bEnsureVisible)
+{
+ if (idx < totalCount) {
+ caret = idx;
+ if (bEnsureVisible)
+ EnsureVisible(idx);
+ }
+}
+
+void NewstoryListData::SetContact(MCONTACT hContact)
+{
+ m_hContact = hContact;
+
+ WindowList_Add(g_hNewstoryLogs, m_hwnd, hContact);
+}
+
+void NewstoryListData::SetDialog(CSrmmBaseDialog *pDlg)
+{
+ if (pMsgDlg = pDlg)
+ SetContact(pDlg->m_hContact);
+}
+
+void NewstoryListData::SetPos(int pos)
+{
+ SetSelection((selStart == -1) ? pos : selStart, pos);
+ SetCaret(pos);
+}
+
+void NewstoryListData::SetSelection(int iFirst, int iLast)
+{
+ int start = min(totalCount - 1, iFirst);
+ int end = min(totalCount - 1, max(0, iLast));
+ if (start > end)
+ std::swap(start, end);
+
+ int count = totalCount;
+ for (int i = 0; i < count; ++i) {
+ auto *p = GetItem(i);
+ if (i >= start && i <= end)
+ p->m_bSelected = true;
+ else
+ p->m_bSelected = false;
+ }
+
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+void NewstoryListData::ToggleBookmark()
+{
+ int eventCount = totalCount;
+ for (int i = 0; i < eventCount; i++) {
+ ItemData *p = GetItem(i);
+ if (!p->m_bSelected)
+ continue;
+
+ if (p->dbe.flags & DBEF_BOOKMARK)
+ p->dbe.flags &= ~DBEF_BOOKMARK;
+ else
+ p->dbe.flags |= DBEF_BOOKMARK;
+ db_event_edit(p->hEvent, &p->dbe);
+
+ p->setText(m_hwnd);
+ }
+
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+void NewstoryListData::ToggleSelection(int iFirst, int iLast)
+{
+ int start = min(totalCount - 1, iFirst);
+ int end = min(totalCount - 1, max(0, iLast));
+ if (start > end)
+ std::swap(start, end);
+
+ for (int i = start; i <= end; ++i) {
+ auto *p = GetItem(i);
+ p->m_bSelected = !p->m_bSelected;
+ }
+
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+void NewstoryListData::TryUp(int iCount)
+{
+ if (totalCount == 0)
+ return;
+
+ auto *pTop = GetItem(0);
+ MCONTACT hContact = pTop->hContact;
+ if (pTop->hEvent == 0 || hContact == 0)
+ return;
+
+ int i;
+ for (i = 0; i < iCount; i++) {
+ MEVENT hPrev = db_event_prev(hContact, pTop->hEvent);
+ if (hPrev == 0)
+ break;
+
+ auto *p = items.insert(0);
+ p->hContact = hContact;
+ p->hEvent = hPrev;
+ totalCount++;
+ }
+
+ ItemData *pPrev = nullptr;
+ for (int j = 0; j < i + 1; j++) {
+ auto *pItem = GetItem(j);
+ pPrev = pItem->checkNext(pPrev, m_hwnd);
+ }
+
+ caret = 0;
+ CalcBottom();
+ FixScrollPosition();
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Navigation by coordinates
+
+void NewstoryListData::LineUp()
+{
+ if (AtTop())
+ TryUp(1);
+ else
+ ScrollUp(10);
+}
+
+void NewstoryListData::LineDown()
+{
+ if (!AtBottom())
+ ScrollDown(10);
+}
+
+void NewstoryListData::PageUp()
+{
+ if (AtTop())
+ TryUp(10);
+ else
+ ScrollUp(cachedWindowHeight);
+}
+
+void NewstoryListData::PageDown()
+{
+ if (!AtBottom())
+ ScrollDown(cachedWindowHeight);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Navigation by events
+
+void NewstoryListData::EventUp()
+{
+ if (caret == 0)
+ TryUp(1);
+ else
+ SetPos(caret - 1);
+}
+
+void NewstoryListData::EventDown()
+{
+ if (caret < totalCount-1)
+ SetPos(caret + 1);
+}
+
+void NewstoryListData::EventPageUp()
+{
+ if (caret >= 10)
+ SetPos(caret - 10);
+ else
+ TryUp(caret == 10 ? 1 : 10 - caret);
+}
+
+void NewstoryListData::EventPageDown()
+{
+ if (caret < totalCount - 10)
+ SetPos(caret + 10);
+ else
+ SetPos(totalCount - 1);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Common navigation functions
+
+void NewstoryListData::ScrollBottom()
+{
+ if (!totalCount)
+ return;
+
+ scrollTopItem = cachedMaxTopItem;
+ scrollTopPixel = cachedMaxTopPixel;
+ FixScrollPosition(true);
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+void NewstoryListData::ScrollDown(int deltaY)
+{
+ int iHeight = GetItemHeight(scrollTopItem) + scrollTopPixel;
+ if (iHeight > deltaY)
+ scrollTopPixel -= deltaY;
+ else {
+ deltaY -= iHeight;
+
+ bool bFound = false;
+ for (int i = scrollTopItem + 1; i < totalCount; i++) {
+ iHeight = GetItemHeight(i);
+ if (iHeight > deltaY) {
+ scrollTopPixel = -deltaY;
+ scrollTopItem = i;
+ bFound = true;
+ break;
+ }
+ deltaY -= iHeight;
+ }
+ if (!bFound)
+ scrollTopItem = scrollTopPixel = 0;
+ }
+
+ FixScrollPosition();
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+void NewstoryListData::ScrollTop()
+{
+ scrollTopItem = scrollTopPixel = 0;
+ FixScrollPosition();
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+void NewstoryListData::ScrollUp(int deltaY)
+{
+ int reserveY = -scrollTopPixel; // distance in pixels between the top event beginning and the window top coordinate
+
+ if (reserveY >= deltaY)
+ scrollTopPixel += deltaY; // stay on the same event, just move up
+ else {
+ deltaY -= reserveY; // move to the appropriate event first, then calculate the gap
+
+ bool bFound = false;
+ for (int i = scrollTopItem - 1; i >= 0; i--) {
+ int iHeight = GetItemHeight(i);
+ if (iHeight > deltaY) {
+ scrollTopPixel = deltaY - iHeight;
+ scrollTopItem = i;
+ bFound = true;
+ break;
+ }
+ deltaY -= iHeight;
+ }
+
+ if (!bFound)
+ scrollTopItem = scrollTopPixel = 0;
+ }
+
+ FixScrollPosition();
+ InvalidateRect(m_hwnd, 0, FALSE);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// NewStory history control window procedure
+
+LRESULT CALLBACK NewstoryListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int idx;
+ POINT pt;
+ NewstoryListData *data = (NewstoryListData *)GetWindowLongPtr(hwnd, 0);
+
+ MSG message = { hwnd, msg, wParam, lParam };
+ switch (Hotkey_Check(&message, MODULENAME)) {
+ case HOTKEY_SEEK_FORWARD:
+ PostMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(IDOK, BN_CLICKED), 1);
+ break;
+ case HOTKEY_SEEK_BACK:
+ PostMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(IDC_FINDPREV, BN_CLICKED), 1);
+ break;
+ case HOTKEY_SEARCH:
+ PostMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(IDC_SEARCH, BN_CLICKED), 1);
+ break;
+ case HOTKEY_BOOKMARK:
+ data->ToggleBookmark();
+ return 0;
+ }
+
+ switch (msg) {
+ case WM_CREATE:
+ data = new NewstoryListData(hwnd);
+ SetWindowLongPtr(hwnd, 0, (LONG_PTR)data);
+ if (!g_plugin.bOptVScroll)
+ SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_VSCROLL);
+ break;
+
+ // History list control messages
+ case NSM_SELECTITEMS:
+ data->AddSelection(wParam, lParam);
+ return 0;
+
+ case NSM_SEEKTIME:
+ {
+ int eventCount = data->totalCount;
+ for (int i = 0; i < eventCount; i++) {
+ auto *p = data->GetItem(i);
+ if (p->dbe.timestamp >= wParam) {
+ data->SetSelection(i, i);
+ data->SetCaret(i);
+ break;
+ }
+
+ if (i == eventCount - 1) {
+ data->SetSelection(i, i);
+ data->SetCaret(i);
+ }
+ }
+ }
+ return TRUE;
+
+ case NSM_ADDEVENT:
+ data->AddEvent(wParam, lParam, 1);
+ break;
+
+ case NSM_SET_OPTIONS:
+ data->bSortAscending = g_plugin.bSortAscending;
+ data->scrollTopPixel = 0;
+ data->FixScrollPosition(true);
+ InvalidateRect(hwnd, 0, FALSE);
+ break;
+
+ case UM_ADDEVENT:
+ if (data->pMsgDlg == nullptr)
+ data->AddEvent(wParam, lParam, 1);
+ break;
+
+ case UM_EDITEVENT:
+ idx = data->items.find(lParam);
+ if (idx != -1) {
+ auto *p = data->GetItem(idx);
+ p->load(true);
+ p->setText(data->m_hwnd);
+ InvalidateRect(hwnd, 0, FALSE);
+ }
+ break;
+
+ case UM_REMOVEEVENT:
+ idx = data->items.find(lParam);
+ if (idx != -1) {
+ data->items.remove(idx);
+ data->totalCount--;
+ data->FixScrollPosition(true);
+ InvalidateRect(hwnd, 0, FALSE);
+ }
+ break;
+
+ case WM_SIZE:
+ data->OnResize(LOWORD(lParam), HIWORD(lParam));
+ break;
+
+ case WM_COMMAND:
+ if (NSMenu_Process(LOWORD(wParam), data))
+ return 1;
+ break;
+
+ case WM_ERASEBKGND:
+ return 1;
+
+ case WM_PAINT:
+ /* we get so many InvalidateRect()'s that there is no point painting,
+ Windows in theory shouldn't queue up WM_PAINTs in this case but it does so
+ we'll just ignore them */
+ if (IsWindowVisible(hwnd)) {
+ PAINTSTRUCT ps;
+ HDC hdcWindow = BeginPaint(hwnd, &ps);
+
+ RECT rc;
+ GetClientRect(hwnd, &rc);
+
+ HDC hdc = CreateCompatibleDC(hdcWindow);
+ HBITMAP hbmSave = (HBITMAP)SelectObject(hdc, CreateCompatibleBitmap(hdcWindow, rc.right - rc.left, rc.bottom - rc.top));
+
+ int height = rc.bottom - rc.top;
+ int width = rc.right - rc.left;
+ int top = data->scrollTopPixel;
+
+ for (idx = data->scrollTopItem; top < height && idx < data->totalCount; idx++)
+ top += data->PaintItem(hdc, data->LoadItem(idx), top, width, !data->hwndEditBox || data->caret != idx);
+
+ data->cachedMaxDrawnItem = idx;
+
+ if (top <= height) {
+ RECT rc2;
+ SetRect(&rc2, 0, top, width, height);
+
+ HBRUSH hbr = CreateSolidBrush(g_colorTable[COLOR_BACK].cl);
+ FillRect(hdc, &rc2, hbr);
+ DeleteObject(hbr);
+ }
+
+ if (g_plugin.bOptVScroll)
+ data->RecalcScrollBar();
+ if (g_plugin.bDrawEdge)
+ DrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
+
+ BitBlt(hdcWindow, 0, 0, rc.right, rc.bottom, hdc, 0, 0, SRCCOPY);
+ DeleteObject(SelectObject(hdc, hbmSave));
+ DeleteDC(hdc);
+ EndPaint(hwnd, &ps);
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ if (pt.x == -1 && pt.y == -1)
+ GetCursorPos(&pt);
+
+ POINT pt2 = pt;
+ ScreenToClient(hwnd, &pt2);
+
+ idx = data->GetItemFromPixel(pt2.y);
+ if (idx != -1) {
+ if (data->caret != idx)
+ data->EndEditItem(false);
+ data->SetCaret(idx);
+ if (!data->HasSelection())
+ data->SetSelection(idx, idx);
+ }
+ data->OnContextMenu(idx, pt);
+ break;
+
+ case WM_KILLFOCUS:
+ if (wParam && (HWND)wParam != data->hwndEditBox)
+ data->EndEditItem(false);
+ if (data->pMsgDlg && ((HWND)wParam == data->pMsgDlg->GetInput() || (HWND)wParam == data->pMsgDlg->GetHwnd()))
+ data->ClearSelection(0, -1);
+ return 0;
+
+ case WM_SETFOCUS:
+ return 0;
+
+ case WM_GETDLGCODE:
+ if (lParam) {
+ MSG *msg2 = (MSG *)lParam;
+ if (msg2->message == WM_KEYDOWN) {
+ if (msg2->wParam == VK_TAB)
+ return 0;
+ if (msg2->wParam == VK_ESCAPE && !data->hwndEditBox)
+ return 0;
+ }
+ else if (msg2->message == WM_CHAR) {
+ if (msg2->wParam == '\t')
+ return 0;
+ if (msg2->wParam == 27 && !data->hwndEditBox)
+ return 0;
+ }
+ }
+ return DLGC_WANTMESSAGE;
+
+ case WM_KEYDOWN:
+ {
+ bool isShift = (GetKeyState(VK_SHIFT) & 0x80) != 0;
+ bool isCtrl = (GetKeyState(VK_CONTROL) & 0x80) != 0;
+
+ if (!data->bWasShift && isShift)
+ data->selStart = data->caret;
+ else if (data->bWasShift && !isShift)
+ data->selStart = -1;
+ data->bWasShift = isShift;
+
+ switch (wParam) {
+ case VK_UP:
+ if (g_plugin.bHppCompat)
+ data->EventUp();
+ else
+ data->LineUp();
+ break;
+
+ case VK_DOWN:
+ if (g_plugin.bHppCompat)
+ data->EventDown();
+ else
+ data->LineDown();
+ break;
+
+ case VK_PRIOR:
+ if (isCtrl)
+ data->ScrollTop();
+ else if (g_plugin.bHppCompat)
+ data->EventPageUp();
+ else
+ data->PageUp();
+ break;
+
+ case VK_NEXT:
+ if (isCtrl)
+ data->ScrollBottom();
+ else if (g_plugin.bHppCompat)
+ data->EventPageDown();
+ else
+ data->PageDown();
+ break;
+
+ case VK_HOME:
+ data->ScrollTop();
+ break;
+
+ case VK_END:
+ data->ScrollBottom();
+ break;
+
+ case VK_F2:
+ data->BeginEditItem();
+ break;
+
+ case VK_ESCAPE:
+ if (data->hwndEditBox)
+ data->EndEditItem(false);
+ break;
+
+ case VK_DELETE:
+ data->DeleteItems();
+ break;
+
+ case VK_INSERT:
+ case 'C':
+ if (isCtrl)
+ data->Copy();
+ break;
+
+ case 'A':
+ if (isCtrl)
+ data->AddSelection(0, data->totalCount);
+ break;
+ }
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ idx = data->GetItemFromPixel(pt.y);
+ if (idx >= 0) {
+ if (data->caret != idx)
+ data->EndEditItem(false);
+
+ auto *pItem = data->LoadItem(idx);
+
+ if (wParam & MK_CONTROL) {
+ data->ToggleSelection(idx, idx);
+ data->SetCaret(idx);
+ }
+ else if (wParam & MK_SHIFT) {
+ data->AddSelection(data->caret, idx);
+ data->SetCaret(idx);
+ }
+ else {
+ pt.y -= pItem->savedTop;
+
+ CMStringW wszUrl;
+ if (pItem->isLink(hwnd, pt, &wszUrl)) {
+ Utils_OpenUrlW(wszUrl);
+ return 0;
+ }
+
+ data->selStart = idx;
+ data->SetSelection(idx, idx);
+ data->SetCaret(idx);
+ }
+ }
+ SetFocus(hwnd);
+ return 0;
+
+ case WM_LBUTTONUP:
+ pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ idx = data->GetItemFromPixel(pt.y);
+ if (idx >= 0)
+ data->selStart = -1;
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ idx = data->GetItemFromPixel(pt.y);
+ if (idx >= 0) {
+ if (data->caret != idx)
+ data->EndEditItem(false);
+
+ auto *pItem = data->LoadItem(idx);
+ pt.y -= pItem->savedTop;
+
+ if (pItem->m_bOfflineFile) {
+ Srmm_DownloadOfflineFile(pItem->hContact, pItem->hEvent, OFD_DOWNLOAD | OFD_RUN);
+ return 0;
+ }
+
+ if (data->caret == idx) {
+ data->BeginEditItem();
+ return 0;
+ }
+ }
+
+ SetFocus(hwnd);
+ return 0;
+
+ case WM_MOUSEMOVE:
+ pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ idx = data->GetItemFromPixel(pt.y);
+ if (idx >= 0) {
+ auto *pItem = data->LoadItem(idx);
+ MTextSendMessage(hwnd, pItem->data, msg, wParam, lParam);
+
+ HCURSOR hOldCursor = GetCursor();
+ HCURSOR hNewCursor = LoadCursor(0, (pItem->isLink(hwnd, pt) || pItem->m_bOfflineFile) ? IDC_HAND : IDC_ARROW);
+ if (hOldCursor != hNewCursor)
+ SetCursor(hNewCursor);
+
+ if (data->selStart != -1) {
+ data->SetSelection(data->selStart, idx);
+ InvalidateRect(hwnd, 0, FALSE);
+ }
+ }
+ break;
+
+ case WM_MOUSEWHEEL:
+ if ((short)HIWORD(wParam) < 0)
+ data->LineDown();
+ else
+ data->LineUp();
+ return TRUE;
+
+ case WM_VSCROLL:
+ {
+ int s_scrollTopItem = data->scrollTopItem;
+ int s_scrollTopPixel = data->scrollTopPixel;
+
+ switch (LOWORD(wParam)) {
+ case SB_LINEUP:
+ if (g_plugin.bHppCompat)
+ data->EventUp();
+ else
+ data->LineUp();
+ break;
+
+ case SB_LINEDOWN:
+ if (g_plugin.bHppCompat)
+ data->EventDown();
+ else
+ data->LineDown();
+ break;
+
+ case SB_PAGEUP:
+ if (g_plugin.bHppCompat)
+ data->EventPageUp();
+ else
+ data->PageUp();
+ break;
+
+ case SB_PAGEDOWN:
+ if (g_plugin.bHppCompat)
+ data->EventPageDown();
+ else
+ data->PageDown();
+ break;
+
+ case SB_BOTTOM:
+ data->ScrollBottom();
+ break;
+
+ case SB_TOP:
+ data->ScrollTop();
+ break;
+
+ case SB_THUMBTRACK:
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_ALL;
+ GetScrollInfo(hwnd, SB_VERT, &si);
+ data->HitTotal(si.nTrackPos, si.nMax);
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (s_scrollTopItem != data->scrollTopItem || s_scrollTopPixel != data->scrollTopPixel)
+ InvalidateRect(hwnd, 0, FALSE);
+ }
+ break;
+
+ case WM_CTLCOLORSTATIC:
+ case WM_CTLCOLOREDIT:
+ if (lParam == INT_PTR(data->hwndEditBox)) {
+ SetBkColor((HDC)wParam, g_colorTable[COLOR_SELBACK].cl);
+ return (LRESULT)g_plugin.hBackBrush;
+ }
+ break;
+
+ case WM_DESTROY:
+ WindowList_Add(g_hNewstoryLogs, hwnd);
+ delete data;
+ SetWindowLongPtr(hwnd, 0, 0);
+ break;
+ }
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+void InitNewstoryControl()
+{
+ htuLog = MTextRegister("Newstory", MTEXT_FANCY_DEFAULT | MTEXT_SYSTEM_HICONS | MTEXT_FANCY_SMILEYS);
+
+ WNDCLASS wndclass = {};
+ wndclass.style = /*CS_HREDRAW | CS_VREDRAW | */CS_DBLCLKS | CS_GLOBALCLASS;
+ wndclass.lpfnWndProc = NewstoryListWndProc;
+ wndclass.cbWndExtra = sizeof(void *);
+ wndclass.hInstance = g_plugin.getInst();
+ wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndclass.lpszClassName = _T(NEWSTORYLIST_CLASS);
+ RegisterClass(&wndclass);
+}
diff --git a/plugins/NewStory/src/history_control.h b/plugins/NewStory/src/history_control.h
index eb14bf6cc9..34821bd1e4 100644
--- a/plugins/NewStory/src/history_control.h
+++ b/plugins/NewStory/src/history_control.h
@@ -1,98 +1,98 @@
-#ifndef __history_control_h__
-#define __history_control_h__
-
-#define NEWSTORYLIST_CLASS "NewstoryList"
-
-struct NewstoryListData : public MZeroedObject
-{
- NewstoryListData(HWND);
-
- mir_cs m_csItems;
- HistoryArray items;
-
- int scrollTopItem; // topmost item
- int scrollTopPixel; // y coord of topmost item, this should be negative or zero
- int caret;
- int selStart = -1;
- int cachedMaxTopItem; // the largest ID of top item to avoid empty space
- int cachedMaxTopPixel;
- int cachedWindowWidth = -1, cachedWindowHeight = -1;
- int cachedMaxDrawnItem = -1;
- int cachedScrollbarPos = -1, cachedScrollbarMax = -1;
- int totalCount;
-
- RECT rcLastPaint;
- MCONTACT m_hContact = INVALID_CONTACT_ID;
-
- bool bWasShift, bSortAscending, bWasAtBottom;
-
- HWND m_hwnd;
- HWND hwndEditBox;
-
- CTimer redrawTimer;
- CSrmmBaseDialog *pMsgDlg = nullptr;
-
- void OnContextMenu(int index, POINT pt);
- void OnResize(int newWidth, int newHeight);
-
- void onTimer_Draw(CTimer *pTimer);
-
- void AddChatEvent(SESSION_INFO *si, const LOGINFO *lin);
- void AddEvent(MCONTACT hContact, MEVENT hFirstEvent, int iCount);
- void AddResults(const OBJLIST<SearchResult> &results);
- void AddSelection(int iFirst, int iLast);
- bool AtBottom(void) const;
- bool AtTop(void) const;
- void BeginEditItem();
- void CalcBottom();
- void Clear();
- void ClearSelection(int iFirst, int iLast);
- void Copy(bool bTextOnly = false);
- void CopyPath();
- void CopyUrl();
- void DeleteItems(void);
- void Download(int iOptions);
- void EndEditItem(bool bAccept);
- void EnsureVisible(int item);
- void EventUp();
- void EventDown();
- void EventPageUp();
- void EventPageDown();
- int FindNext(const wchar_t *pwszText);
- int FindPrev(const wchar_t *pwszText);
- void FixScrollPosition(bool bForce = false);
- CMStringW GatherSelected(bool bTextOnly);
- ItemData* GetItem(int idx) const;
- int GetItemFromPixel(int yPos);
- int GetItemHeight(int index);
- int GetItemHeight(ItemData *pItem);
- bool HasSelection() const;
- void HitTotal(int yCurr, int yTotal);
- void LineUp();
- void LineDown();
- ItemData* LoadItem(int idx);
- void OpenFolder();
- void PageUp();
- void PageDown();
- int PaintItem(HDC hdc, ItemData* pItem, int top, int width, bool bDraw);
- void Quote();
- void RecalcScrollBar();
- void Reply();
- void ScheduleDraw();
- void ScrollBottom();
- void ScrollDown(int deltaY);
- void ScrollTop();
- void ScrollUp(int deltaY);
- void SetCaret(int idx, bool bEnsureVisible = true);
- void SetContact(MCONTACT hContact);
- void SetDialog(CSrmmBaseDialog *pDialog);
- void SetPos(int pos);
- void SetSelection(int iFirst, int iLast);
- void ToggleBookmark();
- void ToggleSelection(int iFirst, int iLast);
- void TryUp(int iCount);
-};
-
-void InitNewstoryControl();
-
-#endif // __history_control_h__
+#ifndef __history_control_h__
+#define __history_control_h__
+
+#define NEWSTORYLIST_CLASS "NewstoryList"
+
+struct NewstoryListData : public MZeroedObject
+{
+ NewstoryListData(HWND);
+
+ mir_cs m_csItems;
+ HistoryArray items;
+
+ int scrollTopItem; // topmost item
+ int scrollTopPixel; // y coord of topmost item, this should be negative or zero
+ int caret;
+ int selStart = -1;
+ int cachedMaxTopItem; // the largest ID of top item to avoid empty space
+ int cachedMaxTopPixel;
+ int cachedWindowWidth = -1, cachedWindowHeight = -1;
+ int cachedMaxDrawnItem = -1;
+ int cachedScrollbarPos = -1, cachedScrollbarMax = -1;
+ int totalCount;
+
+ RECT rcLastPaint;
+ MCONTACT m_hContact = INVALID_CONTACT_ID;
+
+ bool bWasShift, bSortAscending, bWasAtBottom;
+
+ HWND m_hwnd;
+ HWND hwndEditBox;
+
+ CTimer redrawTimer;
+ CSrmmBaseDialog *pMsgDlg = nullptr;
+
+ void OnContextMenu(int index, POINT pt);
+ void OnResize(int newWidth, int newHeight);
+
+ void onTimer_Draw(CTimer *pTimer);
+
+ void AddChatEvent(SESSION_INFO *si, const LOGINFO *lin);
+ void AddEvent(MCONTACT hContact, MEVENT hFirstEvent, int iCount);
+ void AddResults(const OBJLIST<SearchResult> &results);
+ void AddSelection(int iFirst, int iLast);
+ bool AtBottom(void) const;
+ bool AtTop(void) const;
+ void BeginEditItem();
+ void CalcBottom();
+ void Clear();
+ void ClearSelection(int iFirst, int iLast);
+ void Copy(bool bTextOnly = false);
+ void CopyPath();
+ void CopyUrl();
+ void DeleteItems(void);
+ void Download(int iOptions);
+ void EndEditItem(bool bAccept);
+ void EnsureVisible(int item);
+ void EventUp();
+ void EventDown();
+ void EventPageUp();
+ void EventPageDown();
+ int FindNext(const wchar_t *pwszText);
+ int FindPrev(const wchar_t *pwszText);
+ void FixScrollPosition(bool bForce = false);
+ CMStringW GatherSelected(bool bTextOnly);
+ ItemData* GetItem(int idx) const;
+ int GetItemFromPixel(int yPos);
+ int GetItemHeight(int index);
+ int GetItemHeight(ItemData *pItem);
+ bool HasSelection() const;
+ void HitTotal(int yCurr, int yTotal);
+ void LineUp();
+ void LineDown();
+ ItemData* LoadItem(int idx);
+ void OpenFolder();
+ void PageUp();
+ void PageDown();
+ int PaintItem(HDC hdc, ItemData* pItem, int top, int width, bool bDraw);
+ void Quote();
+ void RecalcScrollBar();
+ void Reply();
+ void ScheduleDraw();
+ void ScrollBottom();
+ void ScrollDown(int deltaY);
+ void ScrollTop();
+ void ScrollUp(int deltaY);
+ void SetCaret(int idx, bool bEnsureVisible = true);
+ void SetContact(MCONTACT hContact);
+ void SetDialog(CSrmmBaseDialog *pDialog);
+ void SetPos(int pos);
+ void SetSelection(int iFirst, int iLast);
+ void ToggleBookmark();
+ void ToggleSelection(int iFirst, int iLast);
+ void TryUp(int iCount);
+};
+
+void InitNewstoryControl();
+
+#endif // __history_control_h__
diff --git a/plugins/NewStory/src/history_dlg.cpp b/plugins/NewStory/src/history_dlg.cpp
index 4ce49fbd20..0ace485a79 100644
--- a/plugins/NewStory/src/history_dlg.cpp
+++ b/plugins/NewStory/src/history_dlg.cpp
@@ -1,1223 +1,1223 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation version 2
-of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "stdafx.h"
-
-static CMOption<int> g_splitter(MODULENAME, "SplitterY", 100);
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Main history dialog
-
-enum
-{
- HIST_SHOW_IN = 0x001,
- HIST_SHOW_OUT = 0x002,
- HIST_SHOW_MSGS = 0x004,
- HIST_SHOW_FILES = 0x008,
- HIST_SHOW_URLS = 0x010,
- HIST_SHOW_STATUS = 0x020,
- HIST_SHOW_OTHER = 0x040,
- HIST_AUTO_FILTER = 0x080,
-};
-
-enum
-{
- WND_OPT_TIMETREE = 0x01,
- WND_OPT_SEARCHBAR = 0x02,
- WND_OPT_FILTERBAR = 0x04,
- WND_OPT_BOOKMARKS = 0x08,
-};
-
-enum
-{
- WND_SPACING = 4,
- TBTN_SIZE = 25,
-};
-
-struct InfoBarEvents
-{
- HWND hwndIco, hwndIcoIn, hwndIcoOut;
- HWND hwndTxt, hwndTxtIn, hwndTxtOut;
-};
-
-void LayoutFilterBar(HDWP hDwp, int x, int y, int w, InfoBarEvents *ib)
-{
- hDwp = DeferWindowPos(hDwp, ib->hwndIco, 0,
- x, y, 16, 16, SWP_NOZORDER);
- hDwp = DeferWindowPos(hDwp, ib->hwndTxt, 0,
- x + 16 + WND_SPACING, y, w - 16 - WND_SPACING, 16, SWP_NOZORDER);
-
- hDwp = DeferWindowPos(hDwp, ib->hwndIcoIn, 0,
- x + 16, y + 16 + WND_SPACING, 16, 16, SWP_NOZORDER);
- hDwp = DeferWindowPos(hDwp, ib->hwndTxtIn, 0,
- x + 32 + WND_SPACING, y + 16 + WND_SPACING, w - WND_SPACING - 32, 16, SWP_NOZORDER);
-
- hDwp = DeferWindowPos(hDwp, ib->hwndIcoOut, 0,
- x + 16, y + (16 + WND_SPACING) * 2, 16, 16, SWP_NOZORDER);
- DeferWindowPos(hDwp, ib->hwndTxtOut, 0,
- x + 32 + WND_SPACING, y + (16 + WND_SPACING) * 2, w - WND_SPACING - 32, 16, SWP_NOZORDER);
-}
-
-class CHistoryDlg : public CDlgBase
-{
- HMENU m_hMenu;
- uint16_t showFlags;
- bool m_bSearchChanged = false;
- MCONTACT m_hContact;
- int lastYear = -1, lastMonth = -1, lastDay = -1;
- HTREEITEM hLastYear = 0, hLastMonth = 0, hLastDay = 0;
- bool disableTimeTreeChange = false;
- bool bAppendOnly;
-
- // window flags
- uint32_t m_dwOptions = 0;
-
- // toolbar buttons
- struct Button
- {
- enum { RIGHT = 1, SPACED = 2 };
-
- Button(CCtrlMButton &_1, int _2 = 0) :
- hwnd(_1),
- options(_2)
- {}
-
- CCtrlMButton &hwnd;
- int options;
- };
- std::vector<Button> m_toolbar;
-
- // main controls
- int m_iSplitter;
-
- CCtrlBase m_histWindow;
- NewstoryListData *m_histCtrl;
-
- // bookmarks
- HIMAGELIST hBookmarksIcons = 0;
- std::map<MCONTACT, std::vector<MEVENT>> m_events; // for filtered events
-
- // searchbar
- HWND m_hwndBtnCloseSearch;
-
- // statusbar
- HWND m_hwndStatus;
-
- // filter bar
- HWND m_hwndChkDateFrom, m_hwndChkDateTo;
- HWND m_hwndDateFrom, m_hwndDateTo;
- InfoBarEvents ibMessages, ibFiles, ibUrls, ibTotal;
-
- OBJLIST<SearchResult> m_arResults;
-
- static int stubSortResults(const void *p1, const void *p2)
- {
- return int((*(SearchResult **)p1)->ts) - int((*(SearchResult **)p2)->ts);
- }
-
- void DoGlobalSearch()
- {
- ptrW wszPattern(edtSearchText.GetText());
- if (!mir_wstrlen(wszPattern))
- return;
-
- // clear messages array first
- m_histCtrl->Clear();
-
- CharLowerW(wszPattern);
- DoSearchContact(0, wszPattern);
- for (auto &hContact : Contacts())
- DoSearchContact(hContact, wszPattern);
-
- qsort(m_arResults.getArray(), m_arResults.getCount(), sizeof(void *), stubSortResults);
- m_histCtrl->AddResults(m_arResults);
- m_arResults.destroy();
-
- BuildTimeTree();
- }
-
- void DoSearchContact(MCONTACT hContact, const wchar_t *pwszPattern)
- {
- DB::ECPTR pCursor(DB::Events(hContact));
- while (MEVENT hDbEvent = pCursor.FetchNext()) {
- DB::EventInfo dbei(hDbEvent);
- if (!dbei)
- continue;
-
- ptrW pwszText(DbEvent_GetTextW(&dbei));
- if (!mir_wstrlen(pwszText))
- continue;
-
- CharLowerW(pwszText);
- if (wcsstr(pwszText, pwszPattern))
- m_arResults.insert(new SearchResult(hContact, hDbEvent, dbei.timestamp));
- }
- }
-
- void ShowHideControls()
- {
- int cmd = (m_dwOptions & WND_OPT_FILTERBAR) ? SW_SHOW : SW_HIDE;
- ShowWindow(ibMessages.hwndIco, cmd);
- ShowWindow(ibMessages.hwndIcoIn, cmd);
- ShowWindow(ibMessages.hwndIcoOut, cmd);
- ShowWindow(ibMessages.hwndTxt, cmd);
- ShowWindow(ibMessages.hwndTxtIn, cmd);
- ShowWindow(ibMessages.hwndTxtOut, cmd);
- ShowWindow(ibFiles.hwndIco, cmd);
- ShowWindow(ibFiles.hwndIcoIn, cmd);
- ShowWindow(ibFiles.hwndIcoOut, cmd);
- ShowWindow(ibFiles.hwndTxt, cmd);
- ShowWindow(ibFiles.hwndTxtIn, cmd);
- ShowWindow(ibFiles.hwndTxtOut, cmd);
- ShowWindow(ibUrls.hwndIco, cmd);
- ShowWindow(ibUrls.hwndIcoIn, cmd);
- ShowWindow(ibUrls.hwndIcoOut, cmd);
- ShowWindow(ibUrls.hwndTxt, cmd);
- ShowWindow(ibUrls.hwndTxtIn, cmd);
- ShowWindow(ibUrls.hwndTxtOut, cmd);
- ShowWindow(ibTotal.hwndIco, cmd);
- ShowWindow(ibTotal.hwndIcoIn, cmd);
- ShowWindow(ibTotal.hwndIcoOut, cmd);
- ShowWindow(ibTotal.hwndTxt, cmd);
- ShowWindow(ibTotal.hwndTxtIn, cmd);
- ShowWindow(ibTotal.hwndTxtOut, cmd);
- ShowWindow(m_hwndDateFrom, cmd);
- ShowWindow(m_hwndDateTo, cmd);
- ShowWindow(m_hwndChkDateFrom, cmd);
- ShowWindow(m_hwndChkDateTo, cmd);
- ShowWindow(GetDlgItem(m_hwnd, IDC_IB_SEPARATOR), cmd);
-
- cmd = (m_dwOptions & WND_OPT_SEARCHBAR) ? SW_SHOW : SW_HIDE;
- ShowWindow(m_hwndBtnCloseSearch, cmd);
- ShowWindow(btnFindNext.GetHwnd(), cmd);
- ShowWindow(btnFindPrev.GetHwnd(), cmd);
- ShowWindow(edtSearchText.GetHwnd(), cmd);
- if (cmd)
- SetFocus(edtSearchText.GetHwnd());
-
- bool bShowTime = (m_dwOptions & WND_OPT_TIMETREE) != 0;
- bool bShowBookmarks = (m_dwOptions & WND_OPT_BOOKMARKS) != 0;
- m_timeTree.Show(bShowTime);
- m_bookmarks.Show(bShowBookmarks);
- splitTime.Show(bShowTime || bShowBookmarks);
- }
-
- void UpdateTitle()
- {
- switch (m_hContact) {
- case INVALID_CONTACT_ID:
- SetWindowText(m_hwnd, TranslateT("Global history search"));
- break;
-
- case 0:
- SetWindowText(m_hwnd, TranslateT("System history"));
- break;
-
- default:
- SetWindowText(m_hwnd, TplFormatString(TPL_TITLE, m_hContact, 0));
- }
- }
-
- void BuildBookmarksList()
- {
- if (!(m_dwOptions & WND_OPT_BOOKMARKS))
- return;
-
- m_events.clear();
- m_bookmarks.DeleteAllItems();
- if (hBookmarksIcons)
- ImageList_Destroy(hBookmarksIcons);
-
- hBookmarksIcons = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 1, 0);
- m_bookmarks.SetImageList(hBookmarksIcons, LVSIL_SMALL);
-
- if (m_hContact == INVALID_CONTACT_ID) {
- auto *pDB = db_get_current();
-
- int iIconId = ImageList_ReplaceIcon(hBookmarksIcons, -1, g_plugin.getIcon(IDI_USERS));
- m_bookmarks.AddItem(TranslateT("All results"), iIconId, INVALID_CONTACT_ID);
-
- std::map<char *, int> protoMap;
-
- for (auto &cc : Contacts()) {
- bool bAdded = false;
- DB::ECPTR pCursor(pDB->EventCursor(cc, 0));
- while (MEVENT hEvent = pCursor.FetchNext()) {
- DB::EventInfo dbei(hEvent, false);
- if (dbei && dbei.flags & DBEF_BOOKMARK) {
- if (!bAdded) {
- auto *pa = Proto_GetContactAccount(cc);
- char *szProto = (pa == nullptr) ? "SomeShit" : pa->szModuleName;
- auto pProto = protoMap.find(szProto);
-
- if (pProto == protoMap.end()) {
- iIconId = ImageList_ReplaceIcon(hBookmarksIcons, -1, Skin_LoadProtoIcon(szProto, ID_STATUS_ONLINE));
- protoMap[szProto] = iIconId;
- }
- else iIconId = (*pProto).second;
-
- m_bookmarks.AddItem(Clist_GetContactDisplayName(cc, 0), iIconId, cc);
- bAdded = true;
- }
-
- m_events[cc].push_back(hEvent);
- }
- }
- }
- }
- else {
- ImageList_ReplaceIcon(hBookmarksIcons, -1, g_plugin.getIcon(IDI_BOOKMARK));
-
- int numItems = m_histCtrl->totalCount;
-
- for (int i = 0; i < numItems; i++) {
- auto *pItem = m_histCtrl->GetItem(i);
- if (pItem->fetch())
- if (pItem->dbe.flags & DBEF_BOOKMARK)
- m_bookmarks.AddItem(pItem->getWBuf(), 0, i);
- }
- }
- }
-
- void BuildTimeTree()
- {
- if (!(m_dwOptions & WND_OPT_TIMETREE))
- return;
-
- m_timeTree.DeleteAllItems();
- auto &pArray = m_histCtrl->items;
- int numItems = pArray.getCount();
-
- int CurYear = 0, CurMonth = 0, CurDay = 0, PrevYear = -1, PrevMonth = -1, PrevDay = -1;
- HTREEITEM hCurYear = 0, hCurMonth = 0, hCurDay = 0;
- for (int i = 0; i < numItems; i++) {
- auto *pItem = pArray.get(i, false);
- if (!pItem->fetch())
- continue;
-
- if (pItem->dbe.timestamp == 0)
- continue;
-
- struct tm ts = { 0 };
- time_t timestamp = pItem->dbe.timestamp;
- errno_t err = localtime_s(&ts, &timestamp); /* statically alloced, local time correction */
- if (err != 0)
- return;
-
- CurYear = ts.tm_year + 1900;
- CurMonth = ts.tm_mon + 1;
- CurDay = ts.tm_mday;
- wchar_t buf[50];
- TVINSERTSTRUCT tvi;
- tvi.hParent = nullptr;
- tvi.item.mask = TVIF_TEXT | TVIF_PARAM;
- if (CurYear != PrevYear) {
- _itow(CurYear, buf, 10);
- tvi.item.pszText = buf;
- tvi.item.lParam = 0;
- hCurYear = TreeView_InsertItem(m_timeTree.GetHwnd(), &tvi);
- PrevYear = CurYear;
- }
- if (CurMonth != PrevMonth) {
- tvi.hParent = hCurYear;
- tvi.item.pszText = TranslateW(months[CurMonth - 1]);
- tvi.item.lParam = CurMonth;
- hCurMonth = TreeView_InsertItem(m_timeTree.GetHwnd(), &tvi);
- PrevMonth = CurMonth;
- }
- if (CurDay != PrevDay) {
- _itow(CurDay, buf, 10);
- tvi.hParent = hCurMonth;
- tvi.item.pszText = buf;
- tvi.item.lParam = 0;
- hCurDay = TreeView_InsertItem(m_timeTree.GetHwnd(), &tvi);
- PrevDay = CurDay;
- }
- }
- disableTimeTreeChange = true;
- HTREEITEM root = m_timeTree.GetRoot();
- m_timeTree.SelectItem(root);
- }
-
- CSplitter splitTime;
- CCtrlEdit edtSearchText;
- CCtrlMButton btnUserInfo, btnSendMsg, btnUserMenu, btnCopy, btnOptions, btnFilter;
- CCtrlMButton btnCalendar, btnSearch, btnExport, btnFindNext, btnFindPrev, btnDelete, btnTimeTree, btnBookmarks;
- CCtrlListView m_bookmarks;
- CCtrlTreeView m_timeTree;
-
-public:
- CHistoryDlg(MCONTACT _hContact) :
- CDlgBase(g_plugin, IDD_HISTORY),
- m_arResults(10000),
- m_hContact(_hContact),
- m_iSplitter(g_splitter),
- m_bookmarks(this, IDC_BOOKMARKSLIST),
- m_timeTree(this, IDC_TIMETREEVIEW),
- m_histWindow(this, IDC_HISTORYCONTROL),
- splitTime(this, IDC_SPLITTERY),
- edtSearchText(this, IDC_SEARCHTEXT),
- btnCopy(this, IDC_COPY, g_plugin.getIcon(IDI_COPY), LPGEN("Copy")),
- btnExport(this, IDC_EXPORT, g_plugin.getIcon(IDI_EXPORT), LPGEN("Export...")),
- btnDelete(this, IDC_DELETE, SKINICON_OTHER_DELETE, LPGEN("Delete...")),
- btnFilter(this, IDC_FILTER, g_plugin.getIcon(IDI_FILTER), LPGEN("Filter")),
- btnSearch(this, IDC_SEARCH, g_plugin.getIcon(IDI_SEARCH), LPGEN("Search...")),
- btnOptions(this, IDC_LOGOPTIONS, g_plugin.getIcon(IDI_OPTIONS), LPGEN("Options")),
- btnSendMsg(this, IDC_MESSAGE, g_plugin.getIcon(IDI_SENDMSG), LPGEN("Send message")),
- btnCalendar(this, IDC_DATEPOPUP, g_plugin.getIcon(IDI_CALENDAR), LPGEN("Jump to date")),
- btnUserInfo(this, IDC_USERINFO, g_plugin.getIcon(IDI_USERINFO), LPGEN("User info")),
- btnUserMenu(this, IDC_USERMENU, g_plugin.getIcon(IDI_USERMENU), LPGEN("User menu")),
- btnFindNext(this, IDOK, g_plugin.getIcon(IDI_FINDNEXT), LPGEN("Find next")),
- btnFindPrev(this, IDC_FINDPREV, g_plugin.getIcon(IDI_FINDPREV), LPGEN("Find previous")),
- btnTimeTree(this, IDC_TIMETREE, g_plugin.getIcon(IDI_TIMETREE), LPGEN("Conversations")),
- btnBookmarks(this, IDC_BOOKMARKS, g_plugin.getIcon(IDI_BOOKMARK), LPGEN("Bookmarks"))
- {
- showFlags = g_plugin.getWord("showFlags", 0x7f);
- m_dwOptions = g_plugin.getDword("dwOptions");
-
- if (!GetDatabasePlugin("JSON")) {
- btnExport.Disable();
- btnExport.SetTooltip(LPGEN("You need to install the Import plugin to export events"));
- }
-
- if (m_hContact == INVALID_CONTACT_ID)
- m_dwOptions |= WND_OPT_SEARCHBAR;
- else if (m_hContact > 0) {
- m_toolbar.push_back(Button(btnUserMenu));
- m_toolbar.push_back(Button(btnUserInfo));
- m_toolbar.push_back(Button(btnSendMsg, Button::SPACED));
- }
-
- m_toolbar.push_back(Button(btnTimeTree));
- m_toolbar.push_back(Button(btnBookmarks));
- m_toolbar.push_back(Button(btnSearch));
- m_toolbar.push_back(Button(btnFilter));
- m_toolbar.push_back(Button(btnCalendar, Button::SPACED));
- m_toolbar.push_back(Button(btnCopy));
- m_toolbar.push_back(Button(btnDelete));
- m_toolbar.push_back(Button(btnExport));
- m_toolbar.push_back(Button(btnOptions, Button::RIGHT));
-
- m_timeTree.OnSelChanged = Callback(this, &CHistoryDlg::onSelChanged_TimeTree);
- m_bookmarks.OnItemChanged = Callback(this, &CHistoryDlg::onSelChanged_Bookmarks);
-
- edtSearchText.OnChange = Callback(this, &CHistoryDlg::onChange_SearchText);
-
- splitTime.OnChange = Callback(this, &CHistoryDlg::onChange_Splitter);
-
- btnCopy.OnClick = Callback(this, &CHistoryDlg::onClick_Copy);
- btnExport.OnClick = Callback(this, &CHistoryDlg::onClick_Export);
- btnDelete.OnClick = Callback(this, &CHistoryDlg::onClick_Delete);
- btnFilter.OnClick = Callback(this, &CHistoryDlg::onClick_Filter);
- btnSearch.OnClick = Callback(this, &CHistoryDlg::onClick_Search);
- btnOptions.OnClick = Callback(this, &CHistoryDlg::onClick_Options);
- btnSendMsg.OnClick = Callback(this, &CHistoryDlg::onClick_Message);
- btnCalendar.OnClick = Callback(this, &CHistoryDlg::onClick_Calendar);
- btnFindPrev.OnClick = Callback(this, &CHistoryDlg::onClick_FindPrev);
- btnUserInfo.OnClick = Callback(this, &CHistoryDlg::onClick_UserInfo);
- btnUserMenu.OnClick = Callback(this, &CHistoryDlg::onClick_UserMenu);
- btnTimeTree.OnClick = Callback(this, &CHistoryDlg::onClick_TimeTree);
- btnBookmarks.OnClick = Callback(this, &CHistoryDlg::onClick_Bookmarks);
-
- m_hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_POPUPS));
- TranslateMenu(m_hMenu);
-
- HMENU hMenu = GetSubMenu(m_hMenu, 0);
- CheckMenuItem(hMenu, ID_FILTER_INCOMING, (showFlags & HIST_SHOW_IN) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(hMenu, ID_FILTER_OUTGOING, (showFlags & HIST_SHOW_OUT) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(hMenu, ID_FILTER_MESSAGES, (showFlags & HIST_SHOW_MSGS) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(hMenu, ID_FILTER_FILES, (showFlags & HIST_SHOW_FILES) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(hMenu, ID_FILTER_URLS, (showFlags & HIST_SHOW_URLS) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(hMenu, ID_FILTER_STATUS, (showFlags & HIST_SHOW_STATUS) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(hMenu, ID_FILTER_OTHER, (showFlags & HIST_SHOW_OTHER) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(hMenu, ID_FILTER_AUTO, (showFlags & HIST_AUTO_FILTER) ? MF_CHECKED : MF_UNCHECKED);
- }
-
- bool OnInitDialog() override
- {
- // get handles
- m_hwndBtnCloseSearch = GetDlgItem(m_hwnd, IDC_SEARCHICON);
- m_hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0, m_hwnd, NULL, g_plugin.getInst(), NULL);
- SendMessage(m_hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON), 0);
-
- // bookmarks
- RECT rc;
- GetClientRect(m_bookmarks.GetHwnd(), &rc);
- m_bookmarks.AddColumn(0, TranslateT("Bookmarks"), rc.right - rc.left);
-
- // filterbar
- btnFilter.MakePush();
-
- btnTimeTree.MakePush();
- if (m_dwOptions & WND_OPT_TIMETREE)
- btnTimeTree.Push(true);
-
- btnSearch.MakePush();
- if (m_dwOptions & WND_OPT_SEARCHBAR)
- btnSearch.Push(true);
-
- btnBookmarks.MakePush();
- if (m_dwOptions & WND_OPT_BOOKMARKS)
- btnBookmarks.Push(true);
-
- // filters
- m_hwndChkDateFrom = GetDlgItem(m_hwnd, IDC_CHK_DATE_FROM);
- m_hwndChkDateTo = GetDlgItem(m_hwnd, IDC_CHK_DATE_TO);
- m_hwndDateFrom = GetDlgItem(m_hwnd, IDC_DATE_FROM);
- m_hwndDateTo = GetDlgItem(m_hwnd, IDC_DATE_TO);
-
- ibMessages.hwndIco = GetDlgItem(m_hwnd, IDC_ICO_MESSAGES);
- ibMessages.hwndTxt = GetDlgItem(m_hwnd, IDC_TXT_MESSAGES);
- ibMessages.hwndIcoIn = GetDlgItem(m_hwnd, IDC_ICO_MESSAGES_IN);
- SendMessage(ibMessages.hwndIcoIn, BUTTONSETASFLATBTN, 0, 0);
- SendMessage(ibMessages.hwndIcoIn, BUTTONSETASPUSHBTN, 0, 0);
- SendMessage(ibMessages.hwndIcoIn, BM_SETCHECK, BST_CHECKED, 0);
-
- ibMessages.hwndTxtIn = GetDlgItem(m_hwnd, IDC_TXT_MESSAGES_IN);
- ibMessages.hwndIcoOut = GetDlgItem(m_hwnd, IDC_ICO_MESSAGES_OUT);
- SendMessage(ibMessages.hwndIcoOut, BUTTONSETASFLATBTN, 0, 0);
- SendMessage(ibMessages.hwndIcoOut, BUTTONSETASPUSHBTN, 0, 0);
- SendMessage(ibMessages.hwndIcoOut, BM_SETCHECK, BST_CHECKED, 0);
- ibMessages.hwndTxtOut = GetDlgItem(m_hwnd, IDC_TXT_MESSAGES_OUT);
-
- ibFiles.hwndIco = GetDlgItem(m_hwnd, IDC_ICO_FILES);
- ibFiles.hwndTxt = GetDlgItem(m_hwnd, IDC_TXT_FILES);
- ibFiles.hwndIcoIn = GetDlgItem(m_hwnd, IDC_ICO_FILES_IN);
- SendMessage(ibFiles.hwndIcoIn, BUTTONSETASFLATBTN, 0, 0);
-
- SendMessage(ibFiles.hwndIcoIn, BUTTONSETASPUSHBTN, 0, 0);
- SendMessage(ibFiles.hwndIcoIn, BM_SETCHECK, BST_CHECKED, 0);
-
- ibFiles.hwndTxtIn = GetDlgItem(m_hwnd, IDC_TXT_FILES_IN);
- ibFiles.hwndIcoOut = GetDlgItem(m_hwnd, IDC_ICO_FILES_OUT);
- SendMessage(ibFiles.hwndIcoOut, BUTTONSETASFLATBTN, 0, 0);
- SendMessage(ibFiles.hwndIcoOut, BUTTONSETASPUSHBTN, 0, 0);
- SendMessage(ibFiles.hwndIcoOut, BM_SETCHECK, BST_CHECKED, 0);
- ibFiles.hwndTxtOut = GetDlgItem(m_hwnd, IDC_TXT_FILES_OUT);
-
- ibUrls.hwndIco = GetDlgItem(m_hwnd, IDC_ICO_URLS);
- ibUrls.hwndTxt = GetDlgItem(m_hwnd, IDC_TXT_URLS);
- ibUrls.hwndIcoIn = GetDlgItem(m_hwnd, IDC_ICO_URLS_IN);
- SendMessage(ibUrls.hwndIcoIn, BUTTONSETASFLATBTN, 0, 0);
- SendMessage(ibUrls.hwndIcoIn, BUTTONSETASPUSHBTN, 0, 0);
- SendMessage(ibUrls.hwndIcoIn, BM_SETCHECK, BST_CHECKED, 0);
-
- ibUrls.hwndTxtIn = GetDlgItem(m_hwnd, IDC_TXT_URLS_IN);
- ibUrls.hwndIcoOut = GetDlgItem(m_hwnd, IDC_ICO_URLS_OUT);
- SendMessage(ibUrls.hwndIcoOut, BUTTONSETASFLATBTN, 0, 0);
- SendMessage(ibUrls.hwndIcoOut, BUTTONSETASPUSHBTN, 0, 0);
- SendMessage(ibUrls.hwndIcoOut, BM_SETCHECK, BST_CHECKED, 0);
- ibUrls.hwndTxtOut = GetDlgItem(m_hwnd, IDC_TXT_URLS_OUT);
-
- ibTotal.hwndIco = GetDlgItem(m_hwnd, IDC_ICO_TOTAL);
- ibTotal.hwndTxt = GetDlgItem(m_hwnd, IDC_TXT_TOTAL);
- ibTotal.hwndIcoIn = GetDlgItem(m_hwnd, IDC_ICO_TOTAL_IN);
- SendMessage(ibTotal.hwndIcoIn, BUTTONSETASFLATBTN, 0, 0);
- SendMessage(ibTotal.hwndIcoIn, BUTTONSETASPUSHBTN, 0, 0);
- SendMessage(ibTotal.hwndIcoIn, BM_SETCHECK, BST_CHECKED, 0);
-
- ibTotal.hwndTxtIn = GetDlgItem(m_hwnd, IDC_TXT_TOTAL_IN);
- ibTotal.hwndIcoOut = GetDlgItem(m_hwnd, IDC_ICO_TOTAL_OUT);
- SendMessage(ibTotal.hwndIcoOut, BUTTONSETASFLATBTN, 0, 0);
- SendMessage(ibTotal.hwndIcoOut, BUTTONSETASPUSHBTN, 0, 0);
- SendMessage(ibTotal.hwndIcoOut, BM_SETCHECK, BST_CHECKED, 0);
- ibTotal.hwndTxtOut = GetDlgItem(m_hwnd, IDC_TXT_TOTAL_OUT);
-
- // Ask for layout
- PostMessage(m_hwnd, WM_SIZE, 0, 0);
-
- WindowList_Add(g_hNewstoryWindows, m_hwnd, m_hContact);
-
- ShowHideControls();
- UpdateTitle();
-
- m_histCtrl = (NewstoryListData *)GetWindowLongPtr(m_histWindow.GetHwnd(), 0);
-
- if (m_hContact == 0 || m_hContact == INVALID_CONTACT_ID) {
- btnSendMsg.Hide();
- btnUserInfo.Hide();
- btnUserMenu.Hide();
- }
-
- if (m_hContact != INVALID_CONTACT_ID) {
- Utils_RestoreWindowPosition(m_hwnd, m_hContact, MODULENAME, "wnd_");
-
- m_histCtrl->AddEvent(m_hContact, 0, -1);
-
- BuildTimeTree();
- SetFocus(m_histWindow.GetHwnd());
- }
- else Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "glb_");
-
- BuildBookmarksList();
-
- m_histCtrl->SetContact(m_hContact);
- m_histCtrl->ScrollBottom();
-
- Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_NEWSTORY));
-
- SendMessage(GetDlgItem(m_hwnd, IDC_SEARCHICON), STM_SETICON, (WPARAM)g_plugin.getIcon(IDI_SEARCH), 0);
-
- SendMessage(ibMessages.hwndIco, STM_SETICON, (LPARAM)g_plugin.getIcon(IDI_SENDMSG), 0);
- SendMessage(ibMessages.hwndIcoIn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGIN));
- SendMessage(ibMessages.hwndIcoOut, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGOUT));
-
- SendMessage(ibFiles.hwndIco, STM_SETICON, (LPARAM)Skin_LoadIcon(SKINICON_EVENT_FILE), 0);
- SendMessage(ibFiles.hwndIcoIn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGIN));
- SendMessage(ibFiles.hwndIcoOut, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGOUT));
-
- SendMessage(ibUrls.hwndIco, STM_SETICON, (LPARAM)Skin_LoadIcon(SKINICON_EVENT_URL), 0);
- SendMessage(ibUrls.hwndIcoIn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGIN));
- SendMessage(ibUrls.hwndIcoOut, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGOUT));
-
- SendMessage(ibTotal.hwndIco, STM_SETICON, (LPARAM)g_plugin.getIcon(IDI_UNKNOWN), 0);
- SendMessage(ibTotal.hwndIcoIn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGIN));
- SendMessage(ibTotal.hwndIcoOut, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGOUT));
- return true;
- }
-
- bool OnApply() override
- {
- if (m_bSearchChanged) {
- m_bSearchChanged = false;
- if (m_hContact == INVALID_CONTACT_ID)
- DoGlobalSearch();
- }
-
- int iOldCaret = m_histCtrl->caret;
- int res = m_histCtrl->FindNext(ptrW(edtSearchText.GetText()));
- if (res == -1)
- SetWindowTextW(m_hwndStatus, TranslateT("No more occuurences found"));
- else if (res < iOldCaret)
- SetWindowTextW(m_hwndStatus, TranslateT("Passed the end of history"));
- else
- SetWindowTextW(m_hwndStatus, L"");
-
- return false;
- }
-
- void OnResize() override
- {
- RECT rc;
- GetClientRect(m_hwnd, &rc);
- int y; // tmp vars
- int w = rc.right - rc.left;
- int h = rc.bottom - rc.top;
-
- HDWP hDwp = BeginDeferWindowPos(43 + (int)m_toolbar.size());
-
- // toolbar
- int hToolBar = TBTN_SIZE + WND_SPACING;
-
- int x = WND_SPACING;
- for (auto &it : m_toolbar) {
- if (it.options & Button::RIGHT)
- continue;
-
- hDwp = DeferWindowPos(hDwp, it.hwnd.GetHwnd(), 0, x, WND_SPACING, TBTN_SIZE, TBTN_SIZE, SWP_NOZORDER);
- x += TBTN_SIZE;
- if (it.options & Button::SPACED)
- x += 10;
- }
-
- x = w - WND_SPACING - TBTN_SIZE;
- for (auto &it : m_toolbar) {
- if (it.options & Button::RIGHT) {
- hDwp = DeferWindowPos(hDwp, it.hwnd.GetHwnd(), 0, x, WND_SPACING, TBTN_SIZE, TBTN_SIZE, SWP_NOZORDER);
- x -= TBTN_SIZE;
- }
- }
-
- // infobar
- // hDwp = DeferWindowPos(hDwp, hwndIcoProtocol, 0,
- // w-100+WND_SPACING, WND_SPACING,
- // 16, 16,
- // SWP_NOZORDER);
- // hDwp = DeferWindowPos(hDwp, hwndTxtNickname, 0,
- // w-100+WND_SPACING*2+16, WND_SPACING,
- // 100, 16,
- // SWP_NOZORDER);
- // hDwp = DeferWindowPos(hDwp, hwndTxtUID, 0,
- // w-100+WND_SPACING*2+16, WND_SPACING*2+16,
- // 100, 16,
- // SWP_NOZORDER);
-
- // filter bar
- int hFilterBar = 0;
- if (m_dwOptions & WND_OPT_FILTERBAR) {
- hFilterBar = WND_SPACING + (16 + WND_SPACING) * 3;
- LayoutFilterBar(hDwp, WND_SPACING + (WND_SPACING + 75) * 0, WND_SPACING * 2 + hToolBar, 75, &ibMessages);
- LayoutFilterBar(hDwp, WND_SPACING + (WND_SPACING + 75) * 1, WND_SPACING * 2 + hToolBar, 75, &ibFiles);
- LayoutFilterBar(hDwp, WND_SPACING + (WND_SPACING + 75) * 2, WND_SPACING * 2 + hToolBar, 75, &ibUrls);
- LayoutFilterBar(hDwp, WND_SPACING + (WND_SPACING + 75) * 3, WND_SPACING * 2 + hToolBar, 75, &ibTotal);
-
- GetWindowRect(m_hwndChkDateFrom, &rc);
- x = rc.right - rc.left;
- GetWindowRect(m_hwndDateFrom, &rc);
- y = hToolBar + WND_SPACING + (WND_SPACING + (16 + WND_SPACING) * 3 - (rc.bottom - rc.top) * 2 - WND_SPACING) / 2;
- hDwp = DeferWindowPos(hDwp, m_hwndChkDateFrom, 0,
- w - x - (rc.right - rc.left) - WND_SPACING * 2, y,
- x, rc.bottom - rc.top,
- SWP_NOZORDER | SWP_NOACTIVATE);
- hDwp = DeferWindowPos(hDwp, m_hwndDateFrom, 0,
- w - (rc.right - rc.left) - WND_SPACING, y,
- rc.right - rc.left, rc.bottom - rc.top,
- SWP_NOZORDER | SWP_NOACTIVATE);
-
- hDwp = DeferWindowPos(hDwp, m_hwndChkDateTo, 0,
- w - x - (rc.right - rc.left) - WND_SPACING * 2, y + (rc.bottom - rc.top) + WND_SPACING,
- x, rc.bottom - rc.top,
- SWP_NOZORDER | SWP_NOACTIVATE);
- hDwp = DeferWindowPos(hDwp, m_hwndDateTo, 0,
- w - (rc.right - rc.left) - WND_SPACING, y + (rc.bottom - rc.top) + WND_SPACING,
- rc.right - rc.left, rc.bottom - rc.top,
- SWP_NOZORDER | SWP_NOACTIVATE);
-
- hDwp = DeferWindowPos(hDwp, GetDlgItem(m_hwnd, IDC_IB_SEPARATOR), 0,
- WND_SPACING, hToolBar + WND_SPACING,
- w - WND_SPACING * 2, 2,
- SWP_NOZORDER | SWP_NOACTIVATE);
- }
-
- // general
- GetWindowRect(m_hwndStatus, &rc);
- int hStatus = rc.bottom - rc.top;
- hDwp = DeferWindowPos(hDwp, m_hwndStatus, 0,
- 0, h - hStatus,
- w, hStatus,
- SWP_NOZORDER | SWP_NOACTIVATE);
-
- // search bar
- int hSearch = 0;
- if (m_dwOptions & WND_OPT_SEARCHBAR) {
- GetWindowRect(edtSearchText.GetHwnd(), &rc);
- hSearch = rc.bottom - rc.top;
- hDwp = DeferWindowPos(hDwp, m_hwndBtnCloseSearch, 0,
- WND_SPACING, h - hSearch - hStatus - WND_SPACING,
- TBTN_SIZE, hSearch, SWP_NOZORDER | SWP_NOACTIVATE);
- hDwp = DeferWindowPos(hDwp, edtSearchText.GetHwnd(), 0,
- TBTN_SIZE + WND_SPACING * 2, h - hSearch - hStatus - WND_SPACING,
- w - WND_SPACING * 4 - TBTN_SIZE * 3, hSearch,
- SWP_NOZORDER | SWP_NOACTIVATE);
- hDwp = DeferWindowPos(hDwp, btnFindPrev.GetHwnd(), 0,
- w - WND_SPACING - TBTN_SIZE * 2, h - hSearch - hStatus - WND_SPACING,
- TBTN_SIZE, hSearch,
- SWP_NOZORDER | SWP_NOACTIVATE);
- hDwp = DeferWindowPos(hDwp, btnFindNext.GetHwnd(), 0,
- w - WND_SPACING - TBTN_SIZE * 1, h - hSearch - hStatus - WND_SPACING,
- TBTN_SIZE, hSearch,
- SWP_NOZORDER | SWP_NOACTIVATE);
- hSearch += WND_SPACING;
- }
-
- int iClientTop = WND_SPACING + hToolBar + hFilterBar;
- int iClientBottom = h - WND_SPACING - hSearch - hStatus - iClientTop;
-
- // time tree bar
- int hTimeTree = 0;
- bool bShowTime = (m_dwOptions & WND_OPT_TIMETREE) != 0;
- bool bShowBookmarks = (m_dwOptions & WND_OPT_BOOKMARKS) != 0;
-
- if (bShowTime || bShowBookmarks) {
- hTimeTree = m_iSplitter;
- hDwp = DeferWindowPos(hDwp, splitTime.GetHwnd(), 0, hTimeTree, iClientTop, WND_SPACING, iClientBottom, SWP_NOZORDER | SWP_NOACTIVATE);
-
- if (bShowTime && bShowBookmarks) {
- int hMiddle = iClientBottom / 2;
- hDwp = DeferWindowPos(hDwp, m_timeTree.GetHwnd(), 0, WND_SPACING, iClientTop, hTimeTree - WND_SPACING, hMiddle, SWP_NOZORDER | SWP_NOACTIVATE);
- hDwp = DeferWindowPos(hDwp, m_bookmarks.GetHwnd(), 0, WND_SPACING, iClientTop + hMiddle, hTimeTree - WND_SPACING, hMiddle, SWP_NOZORDER | SWP_NOACTIVATE);
- }
- else if (bShowTime)
- hDwp = DeferWindowPos(hDwp, m_timeTree.GetHwnd(), 0, WND_SPACING, iClientTop, hTimeTree - WND_SPACING, iClientBottom, SWP_NOZORDER | SWP_NOACTIVATE);
- else
- hDwp = DeferWindowPos(hDwp, m_bookmarks.GetHwnd(), 0, WND_SPACING, iClientTop, hTimeTree - WND_SPACING, iClientBottom, SWP_NOZORDER | SWP_NOACTIVATE);
- }
-
- hDwp = DeferWindowPos(hDwp, m_histWindow.GetHwnd(), 0,
- WND_SPACING + hTimeTree, iClientTop,
- w - WND_SPACING * 2 - hTimeTree, iClientBottom,
- SWP_NOZORDER | SWP_NOACTIVATE);
-
- EndDeferWindowPos(hDwp);
- }
-
- void OnDestroy() override
- {
- g_splitter = m_iSplitter;
-
- if (m_hContact != INVALID_CONTACT_ID)
- Utils_SaveWindowPosition(m_hwnd, m_hContact, MODULENAME, "wnd_");
- else
- Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "glb_");
-
- g_plugin.setWord(0, "showFlags", showFlags);
- g_plugin.setDword(0, "dwOptions", m_dwOptions);
-
- Window_FreeIcon_IcoLib(m_hwnd);
- WindowList_Remove(g_hNewstoryWindows, m_hwnd);
-
- if (m_hwndStatus != nullptr) {
- DestroyWindow(m_hwndStatus);
- m_hwndStatus = nullptr;
- }
- }
-
- void onClick_Calendar(CCtrlButton *pButton)
- {
- RECT rc;
- GetWindowRect(pButton->GetHwnd(), &rc);
-
- CalendarTool_Show(m_hwnd, rc.left, rc.bottom);
- }
-
- void onClick_Copy(CCtrlButton *)
- {
- m_histCtrl->Copy();
- }
-
- void onClick_Delete(CCtrlButton *)
- {
- if (!CallService(MS_HISTORY_EMPTY, m_hContact, 0)) {
- m_histCtrl->Clear();
-
- UpdateTitle();
- BuildTimeTree();
- }
- }
-
- void onClick_TimeTree(CCtrlButton *)
- {
- if (m_dwOptions & WND_OPT_TIMETREE)
- m_dwOptions &= ~WND_OPT_TIMETREE;
- else
- m_dwOptions |= WND_OPT_TIMETREE;
-
- OnResize();
- ShowHideControls();
- BuildTimeTree();
- }
-
- void onClick_Bookmarks(CCtrlButton *)
- {
- if (m_dwOptions & WND_OPT_BOOKMARKS)
- m_dwOptions &= ~WND_OPT_BOOKMARKS;
- else
- m_dwOptions |= WND_OPT_BOOKMARKS;
-
- OnResize();
- ShowHideControls();
- BuildBookmarksList();
- }
-
- void onClick_Export(CCtrlButton *)
- {
- auto *pDriver = GetDatabasePlugin("JSON");
- if (pDriver == nullptr) {
- CMStringW wszText(TranslateT("The required plugin 'Import' isn't loaded to perform this operation."));
- if (ServiceExists(MS_PU_SHOWLIST)) {
- wszText.AppendFormat(L" %s", TranslateT("Do you want to install it using Plugin Updater?"));
- if (IDYES == MessageBoxW(m_hwnd, wszText, TranslateT("Missing plugin"), MB_YESNO | MB_ICONQUESTION))
- CallService(MS_PU_SHOWLIST);
- }
- else MessageBoxW(m_hwnd, wszText, TranslateT("Missing plugin"), MB_OK | MB_ICONERROR);
- return;
- }
-
- wchar_t FileName[MAX_PATH];
- VARSW tszMirDir(L"%miranda_userdata%\\NewStoryExport");
-
- if (db_mc_isMeta(m_hContact)) {
- CMStringW SubContactsList, MessageText;
- bool FirstTime = true;
- int subcount = db_mc_getSubCount(m_hContact);
- for (int i = 0; i < subcount; i++) {
- MCONTACT hSubContact = db_mc_getSub(m_hContact, i);
- char *subproto = Proto_GetBaseAccountName(hSubContact);
- ptrW subid(Contact::GetInfo(CNF_UNIQUEID, hSubContact, subproto));
- if (FirstTime)
- SubContactsList.Append(subid);
- else
- SubContactsList.AppendFormat(L"\r\n%s", subid.get());
- FirstTime = false;
- }
- MessageText.AppendFormat(TranslateT("It is metacontact. For export use one of this subcontacts:\r\n%s"), SubContactsList.c_str());
- MessageBox(m_hwnd, MessageText, TranslateT("Export warning"), MB_OK | MB_ICONWARNING);
- return;
- }
-
- OPENFILENAME ofn = { 0 };
- ofn.lStructSize = sizeof(ofn);
- CMStringW tszFilter, tszTitle, tszFileName;
- tszFilter.AppendFormat(L"%s (*.json)%c*.json%c%c", TranslateT("JSON files"), 0, 0, 0);
- tszTitle.AppendFormat(TranslateT("Export history"));
- ofn.lpstrFilter = tszFilter;
- ofn.hwndOwner = nullptr;
- ofn.lpstrTitle = tszTitle;
- ofn.lpstrFile = FileName;
- ofn.nMaxFile = MAX_PATH;
- ofn.nMaxFileTitle = MAX_PATH;
- ofn.Flags = OFN_HIDEREADONLY | OFN_SHAREAWARE | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
- ofn.lpstrInitialDir = tszMirDir;
- *FileName = '\0';
- ofn.lpstrDefExt = L"";
- if (!GetSaveFileName(&ofn))
- return;
-
- // create file
- CreatePathToFileW(FileName);
- if (PathFileExistsW(FileName))
- DeleteFileW(FileName);
-
- auto *pDB = pDriver->Export(FileName);
- pDB->BeginExport();
- pDB->ExportContact(m_hContact);
-
- // export events
- int iDone = 0;
- bAppendOnly = false;
- auto &arItems = m_histCtrl->items;
- int iCount = arItems.getCount();
- for (int i = 0; i < iCount; i++) {
- auto *pItem = arItems.get(i);
- if (pItem->m_bSelected) {
- DB::EventInfo dbei(pItem->hEvent);
- if (dbei)
- pDB->ExportEvent(dbei);
- iDone++;
- }
- }
-
- // no items selected? export whole history
- if (iDone == 0)
- for (int i = 0; i < iCount; i++) {
- auto *pItem = arItems.get(i);
- DB::EventInfo dbei(pItem->hEvent);
- if (dbei)
- pDB->ExportEvent(dbei);
- }
-
- // Close the file
- pDB->EndExport();
- MessageBox(m_hwnd, TranslateT("Complete"), TranslateT("History export"), MB_OK | MB_ICONINFORMATION);
- }
-
- void onClick_Filter(CCtrlButton *)
- {
- if (m_dwOptions & WND_OPT_FILTERBAR)
- m_dwOptions &= ~WND_OPT_FILTERBAR;
- else
- m_dwOptions |= WND_OPT_FILTERBAR;
-
- ShowHideControls();
- OnResize();
- }
-
- void onClick_FindPrev(CCtrlButton *)
- {
- int iOldCaret = m_histCtrl->caret;
- int res = m_histCtrl->FindPrev(ptrW(edtSearchText.GetText()));
- if (res == -1)
- SetWindowTextW(m_hwndStatus, TranslateT("No more occuurences found"));
- else if (res > iOldCaret)
- SetWindowTextW(m_hwndStatus, TranslateT("Passed the beginning of history"));
- else
- SetWindowTextW(m_hwndStatus, L"");
- }
-
- void onClick_Message(CCtrlButton *)
- {
- CallService(MS_MSG_SENDMESSAGE, m_hContact, 0);
- }
-
- void onClick_Options(CCtrlButton *pButton)
- {
- RECT rc;
- GetWindowRect(pButton->GetHwnd(), &rc);
-
- switch (TrackPopupMenu(GetSubMenu(m_hMenu, 1), TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, NULL)) {
- case ID_LOGOPTIONS_OPTIONS:
- g_plugin.openOptions(L"History", L"NewStory", L"Advanced");
- break;
-
- case ID_LOGOPTIONS_TEMPLATES:
- g_plugin.openOptions(L"History", L"NewStory", L"Templates");
- break;
- }
- PostMessage(m_hwnd, WM_SIZE, 0, 0);
- }
-
- void onClick_Search(CCtrlButton *)
- {
- if (m_dwOptions & WND_OPT_SEARCHBAR)
- m_dwOptions &= ~WND_OPT_SEARCHBAR;
- else
- m_dwOptions |= WND_OPT_SEARCHBAR;
-
- ShowHideControls();
- OnResize();
- }
-
- void onClick_UserInfo(CCtrlButton *)
- {
- CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0);
- }
-
- void onClick_UserMenu(CCtrlButton *pButton)
- {
- RECT rc;
- GetWindowRect(pButton->GetHwnd(), &rc);
-
- HMENU hMenu = Menu_BuildContactMenu(m_hContact);
- TrackPopupMenu(hMenu, 0, rc.left, rc.bottom, 0, m_hwnd, NULL);
- DestroyMenu(hMenu);
- }
-
- void onChange_SearchText(CCtrlEdit *)
- {
- m_bSearchChanged = true;
-
- if (m_bInitialized)
- if (showFlags & HIST_AUTO_FILTER)
- PostMessage(m_hwnd, UM_REBUILDLIST, 0, 0);
- }
-
- void onChange_Splitter(CSplitter *)
- {
- m_iSplitter = splitTime.GetPos();
- }
-
- INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
- {
- if ((msg >= NSM_FIRST) && (msg < NSM_LAST)) {
- LPARAM result = m_histWindow.SendMsg(msg, wParam, lParam);
- SetWindowLongPtr(m_hwnd, DWLP_MSGRESULT, result);
- return result;
- }
-
- MSG message = { m_hwnd, msg, wParam, lParam };
- switch (Hotkey_Check(&message, MODULENAME)) {
- case HOTKEY_SEEK_FORWARD:
- btnFindNext.Click();
- break;
- case HOTKEY_SEEK_BACK:
- btnFindPrev.Click();
- break;
- case HOTKEY_SEARCH:
- btnSearch.Click();
- break;
- }
-
- switch (msg) {
- case WM_CHARTOITEM:
- if (!((GetKeyState(VK_CONTROL) & 0x80) || (GetKeyState(VK_MENU) & 0x80))) {
- wchar_t s[] = { LOWORD(wParam), 0 };
- SetWindowText(GetDlgItem(m_hwnd, IDC_SEARCHTEXT), s);
- SendMessage(GetDlgItem(m_hwnd, IDC_SEARCHTEXT), EM_SETSEL, 1, 1);
- SetFocus(GetDlgItem(m_hwnd, IDC_SEARCHTEXT));
- }
- return -1;
-
- case WM_COMMAND:
- if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, m_hContact))
- return TRUE;
- break;
-
- /*
- GetWindowRect(GetDlgItem(m_hwnd, LOWORD(wParam)), &rc);
-// uint32_t itemID = 0;
- bool doFilter = true;
- switch (TrackPopupMenu(GetSubMenu(hMenu, 1), TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, NULL))
- {
- case ID_FILTER_INCOMING:
- {
- showFlags = toggleBit(showFlags, HIST_SHOW_IN);
- CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_INCOMING,
- showFlags&HIST_SHOW_IN ? MF_CHECKED : MF_UNCHECKED);
- break;
- }
- case ID_FILTER_OUTGOING:
- {
- showFlags = toggleBit(showFlags, HIST_SHOW_OUT);
- CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_OUTGOING,
- showFlags&HIST_SHOW_OUT ? MF_CHECKED : MF_UNCHECKED);
- break;
- }
- case ID_FILTER_MESSAGES:
- {
- showFlags = toggleBit(showFlags, HIST_SHOW_MSGS);
- CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_MESSAGES,
- showFlags&HIST_SHOW_MSGS ? MF_CHECKED : MF_UNCHECKED);
- break;
- }
- case ID_FILTER_FILES:
- {
- showFlags = toggleBit(showFlags, HIST_SHOW_FILES);
- CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_FILES,
- showFlags&HIST_SHOW_FILES ? MF_CHECKED : MF_UNCHECKED);
- break;
- }
- case ID_FILTER_URLS:
- {
- showFlags = toggleBit(showFlags, HIST_SHOW_URLS);
- CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_URLS,
- showFlags&HIST_SHOW_URLS ? MF_CHECKED : MF_UNCHECKED);
- break;
- }
- case ID_FILTER_STATUS:
- {
- showFlags = toggleBit(showFlags, HIST_SHOW_STATUS);
- CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_STATUS,
- showFlags&HIST_SHOW_STATUS ? MF_CHECKED : MF_UNCHECKED);
- break;
- }
- case ID_FILTER_OTHER:
- {
- showFlags = toggleBit(showFlags, HIST_SHOW_OTHER);
- CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_OTHER,
- showFlags&HIST_SHOW_OTHER ? MF_CHECKED : MF_UNCHECKED);
- break;
- }
- case ID_FILTER_AUTO:
- {
- showFlags = toggleBit(showFlags, HIST_AUTO_FILTER);
- CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_AUTO,
- showFlags&HIST_AUTO_FILTER ? MF_CHECKED : MF_UNCHECKED);
- break;
- }
- default:
- {
- doFilter = false;
- break;
- }
- }
- if (doFilter)
- PostMessage(m_hwnd, UM_REBUILDLIST, 0, 0);
- break;*/
-
- case UM_BOOKMARKS:
- BuildBookmarksList();
- break;
-
- case WM_USER + 0x600:
- if (wParam)
- m_histWindow.SendMsg(NSM_SEEKTIME, wParam, 0);
- }
-
- return CDlgBase::DlgProc(msg, wParam, lParam);
- }
-
- void onSelChanged_Bookmarks(CCtrlListView::TEventInfo *ev)
- {
- int idx = m_bookmarks.GetItemData(ev->nmlv->iItem);
-
- if (m_hContact == INVALID_CONTACT_ID) {
- m_histCtrl->Clear();
-
- if (idx == INVALID_CONTACT_ID) {
- for (auto &cc : m_events)
- for (auto &hEvent : cc.second)
- m_histCtrl->AddEvent(cc.first, hEvent, 1);
- }
- else for (auto &hEvent : m_events[idx])
- m_histCtrl->AddEvent(idx, hEvent, 1);
- }
- else m_histCtrl->SetPos(idx);
- }
-
- void onSelChanged_TimeTree(CCtrlTreeView::TEventInfo *)
- {
- wchar_t *val1, *val2, *val3;
- int yearsel = 0, monthsel = 0, daysel = 1;
- bool monthfound = false;
- if (disableTimeTreeChange)
- disableTimeTreeChange = false;
- else {
- HTREEITEM hti1 = m_timeTree.GetSelection();
- TVITEMEX tvi = { 0 };
- tvi.hItem = hti1;
- tvi.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_PARAM;
- tvi.cchTextMax = MAX_PATH;
- tvi.lParam = 0;
- tvi.pszText = (wchar_t *)_alloca(MAX_PATH * sizeof(wchar_t));
-
- m_timeTree.GetItem(&tvi);
- val1 = tvi.pszText;
- if (tvi.lParam) {
- monthsel = tvi.lParam;
- monthfound = true;
- }
- HTREEITEM hti2 = m_timeTree.GetParent(hti1);
- if ((!monthfound) && (!hti2))
- yearsel = _wtoi(val1);
- if ((!monthfound) && (hti2))
- daysel = _wtoi(val1);
- if (hti2) {
- tvi.hItem = hti2;
- tvi.lParam = 0;
- m_timeTree.GetItem(&tvi);
- val2 = tvi.pszText;
- if (tvi.lParam) {
- monthsel = tvi.lParam;
- monthfound = true;
- }
- else
- yearsel = _wtoi(val2);
- HTREEITEM hti3 = m_timeTree.GetParent(hti2);
- if (hti3) {
- tvi.hItem = hti3;
- tvi.lParam = 0;
- m_timeTree.GetItem(&tvi);
- val3 = tvi.pszText;
- yearsel = _wtoi(val3);
- }
- }
- struct tm tm_sel;
- tm_sel.tm_hour = tm_sel.tm_min = tm_sel.tm_sec = 0;
- tm_sel.tm_isdst = 1;
- tm_sel.tm_mday = daysel;
- if (monthsel)
- tm_sel.tm_mon = monthsel - 1;
- else
- tm_sel.tm_mon = 0;
- tm_sel.tm_year = yearsel - 1900;
- PostMessage(m_hwnd, WM_USER + 0x600, mktime(&tm_sel), 0);
- }
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Services
-
-INT_PTR svcShowNewstory(WPARAM hContact, LPARAM)
-{
- HWND hwnd = (HWND)WindowList_Find(g_hNewstoryWindows, hContact);
- if (!hwnd) {
- auto *pDlg = new CHistoryDlg(hContact);
- pDlg->Show();
- hwnd = pDlg->GetHwnd();
- }
-
- SetForegroundWindow(hwnd);
- SetFocus(hwnd);
- return 0;
-}
-
-INT_PTR svcGlobalSearch(WPARAM, LPARAM)
-{
- return svcShowNewstory(INVALID_CONTACT_ID, 0);
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+static CMOption<int> g_splitter(MODULENAME, "SplitterY", 100);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Main history dialog
+
+enum
+{
+ HIST_SHOW_IN = 0x001,
+ HIST_SHOW_OUT = 0x002,
+ HIST_SHOW_MSGS = 0x004,
+ HIST_SHOW_FILES = 0x008,
+ HIST_SHOW_URLS = 0x010,
+ HIST_SHOW_STATUS = 0x020,
+ HIST_SHOW_OTHER = 0x040,
+ HIST_AUTO_FILTER = 0x080,
+};
+
+enum
+{
+ WND_OPT_TIMETREE = 0x01,
+ WND_OPT_SEARCHBAR = 0x02,
+ WND_OPT_FILTERBAR = 0x04,
+ WND_OPT_BOOKMARKS = 0x08,
+};
+
+enum
+{
+ WND_SPACING = 4,
+ TBTN_SIZE = 25,
+};
+
+struct InfoBarEvents
+{
+ HWND hwndIco, hwndIcoIn, hwndIcoOut;
+ HWND hwndTxt, hwndTxtIn, hwndTxtOut;
+};
+
+void LayoutFilterBar(HDWP hDwp, int x, int y, int w, InfoBarEvents *ib)
+{
+ hDwp = DeferWindowPos(hDwp, ib->hwndIco, 0,
+ x, y, 16, 16, SWP_NOZORDER);
+ hDwp = DeferWindowPos(hDwp, ib->hwndTxt, 0,
+ x + 16 + WND_SPACING, y, w - 16 - WND_SPACING, 16, SWP_NOZORDER);
+
+ hDwp = DeferWindowPos(hDwp, ib->hwndIcoIn, 0,
+ x + 16, y + 16 + WND_SPACING, 16, 16, SWP_NOZORDER);
+ hDwp = DeferWindowPos(hDwp, ib->hwndTxtIn, 0,
+ x + 32 + WND_SPACING, y + 16 + WND_SPACING, w - WND_SPACING - 32, 16, SWP_NOZORDER);
+
+ hDwp = DeferWindowPos(hDwp, ib->hwndIcoOut, 0,
+ x + 16, y + (16 + WND_SPACING) * 2, 16, 16, SWP_NOZORDER);
+ DeferWindowPos(hDwp, ib->hwndTxtOut, 0,
+ x + 32 + WND_SPACING, y + (16 + WND_SPACING) * 2, w - WND_SPACING - 32, 16, SWP_NOZORDER);
+}
+
+class CHistoryDlg : public CDlgBase
+{
+ HMENU m_hMenu;
+ uint16_t showFlags;
+ bool m_bSearchChanged = false;
+ MCONTACT m_hContact;
+ int lastYear = -1, lastMonth = -1, lastDay = -1;
+ HTREEITEM hLastYear = 0, hLastMonth = 0, hLastDay = 0;
+ bool disableTimeTreeChange = false;
+ bool bAppendOnly;
+
+ // window flags
+ uint32_t m_dwOptions = 0;
+
+ // toolbar buttons
+ struct Button
+ {
+ enum { RIGHT = 1, SPACED = 2 };
+
+ Button(CCtrlMButton &_1, int _2 = 0) :
+ hwnd(_1),
+ options(_2)
+ {}
+
+ CCtrlMButton &hwnd;
+ int options;
+ };
+ std::vector<Button> m_toolbar;
+
+ // main controls
+ int m_iSplitter;
+
+ CCtrlBase m_histWindow;
+ NewstoryListData *m_histCtrl;
+
+ // bookmarks
+ HIMAGELIST hBookmarksIcons = 0;
+ std::map<MCONTACT, std::vector<MEVENT>> m_events; // for filtered events
+
+ // searchbar
+ HWND m_hwndBtnCloseSearch;
+
+ // statusbar
+ HWND m_hwndStatus;
+
+ // filter bar
+ HWND m_hwndChkDateFrom, m_hwndChkDateTo;
+ HWND m_hwndDateFrom, m_hwndDateTo;
+ InfoBarEvents ibMessages, ibFiles, ibUrls, ibTotal;
+
+ OBJLIST<SearchResult> m_arResults;
+
+ static int stubSortResults(const void *p1, const void *p2)
+ {
+ return int((*(SearchResult **)p1)->ts) - int((*(SearchResult **)p2)->ts);
+ }
+
+ void DoGlobalSearch()
+ {
+ ptrW wszPattern(edtSearchText.GetText());
+ if (!mir_wstrlen(wszPattern))
+ return;
+
+ // clear messages array first
+ m_histCtrl->Clear();
+
+ CharLowerW(wszPattern);
+ DoSearchContact(0, wszPattern);
+ for (auto &hContact : Contacts())
+ DoSearchContact(hContact, wszPattern);
+
+ qsort(m_arResults.getArray(), m_arResults.getCount(), sizeof(void *), stubSortResults);
+ m_histCtrl->AddResults(m_arResults);
+ m_arResults.destroy();
+
+ BuildTimeTree();
+ }
+
+ void DoSearchContact(MCONTACT hContact, const wchar_t *pwszPattern)
+ {
+ DB::ECPTR pCursor(DB::Events(hContact));
+ while (MEVENT hDbEvent = pCursor.FetchNext()) {
+ DB::EventInfo dbei(hDbEvent);
+ if (!dbei)
+ continue;
+
+ ptrW pwszText(DbEvent_GetTextW(&dbei));
+ if (!mir_wstrlen(pwszText))
+ continue;
+
+ CharLowerW(pwszText);
+ if (wcsstr(pwszText, pwszPattern))
+ m_arResults.insert(new SearchResult(hContact, hDbEvent, dbei.timestamp));
+ }
+ }
+
+ void ShowHideControls()
+ {
+ int cmd = (m_dwOptions & WND_OPT_FILTERBAR) ? SW_SHOW : SW_HIDE;
+ ShowWindow(ibMessages.hwndIco, cmd);
+ ShowWindow(ibMessages.hwndIcoIn, cmd);
+ ShowWindow(ibMessages.hwndIcoOut, cmd);
+ ShowWindow(ibMessages.hwndTxt, cmd);
+ ShowWindow(ibMessages.hwndTxtIn, cmd);
+ ShowWindow(ibMessages.hwndTxtOut, cmd);
+ ShowWindow(ibFiles.hwndIco, cmd);
+ ShowWindow(ibFiles.hwndIcoIn, cmd);
+ ShowWindow(ibFiles.hwndIcoOut, cmd);
+ ShowWindow(ibFiles.hwndTxt, cmd);
+ ShowWindow(ibFiles.hwndTxtIn, cmd);
+ ShowWindow(ibFiles.hwndTxtOut, cmd);
+ ShowWindow(ibUrls.hwndIco, cmd);
+ ShowWindow(ibUrls.hwndIcoIn, cmd);
+ ShowWindow(ibUrls.hwndIcoOut, cmd);
+ ShowWindow(ibUrls.hwndTxt, cmd);
+ ShowWindow(ibUrls.hwndTxtIn, cmd);
+ ShowWindow(ibUrls.hwndTxtOut, cmd);
+ ShowWindow(ibTotal.hwndIco, cmd);
+ ShowWindow(ibTotal.hwndIcoIn, cmd);
+ ShowWindow(ibTotal.hwndIcoOut, cmd);
+ ShowWindow(ibTotal.hwndTxt, cmd);
+ ShowWindow(ibTotal.hwndTxtIn, cmd);
+ ShowWindow(ibTotal.hwndTxtOut, cmd);
+ ShowWindow(m_hwndDateFrom, cmd);
+ ShowWindow(m_hwndDateTo, cmd);
+ ShowWindow(m_hwndChkDateFrom, cmd);
+ ShowWindow(m_hwndChkDateTo, cmd);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_IB_SEPARATOR), cmd);
+
+ cmd = (m_dwOptions & WND_OPT_SEARCHBAR) ? SW_SHOW : SW_HIDE;
+ ShowWindow(m_hwndBtnCloseSearch, cmd);
+ ShowWindow(btnFindNext.GetHwnd(), cmd);
+ ShowWindow(btnFindPrev.GetHwnd(), cmd);
+ ShowWindow(edtSearchText.GetHwnd(), cmd);
+ if (cmd)
+ SetFocus(edtSearchText.GetHwnd());
+
+ bool bShowTime = (m_dwOptions & WND_OPT_TIMETREE) != 0;
+ bool bShowBookmarks = (m_dwOptions & WND_OPT_BOOKMARKS) != 0;
+ m_timeTree.Show(bShowTime);
+ m_bookmarks.Show(bShowBookmarks);
+ splitTime.Show(bShowTime || bShowBookmarks);
+ }
+
+ void UpdateTitle()
+ {
+ switch (m_hContact) {
+ case INVALID_CONTACT_ID:
+ SetWindowText(m_hwnd, TranslateT("Global history search"));
+ break;
+
+ case 0:
+ SetWindowText(m_hwnd, TranslateT("System history"));
+ break;
+
+ default:
+ SetWindowText(m_hwnd, TplFormatString(TPL_TITLE, m_hContact, 0));
+ }
+ }
+
+ void BuildBookmarksList()
+ {
+ if (!(m_dwOptions & WND_OPT_BOOKMARKS))
+ return;
+
+ m_events.clear();
+ m_bookmarks.DeleteAllItems();
+ if (hBookmarksIcons)
+ ImageList_Destroy(hBookmarksIcons);
+
+ hBookmarksIcons = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 1, 0);
+ m_bookmarks.SetImageList(hBookmarksIcons, LVSIL_SMALL);
+
+ if (m_hContact == INVALID_CONTACT_ID) {
+ auto *pDB = db_get_current();
+
+ int iIconId = ImageList_ReplaceIcon(hBookmarksIcons, -1, g_plugin.getIcon(IDI_USERS));
+ m_bookmarks.AddItem(TranslateT("All results"), iIconId, INVALID_CONTACT_ID);
+
+ std::map<char *, int> protoMap;
+
+ for (auto &cc : Contacts()) {
+ bool bAdded = false;
+ DB::ECPTR pCursor(pDB->EventCursor(cc, 0));
+ while (MEVENT hEvent = pCursor.FetchNext()) {
+ DB::EventInfo dbei(hEvent, false);
+ if (dbei && dbei.flags & DBEF_BOOKMARK) {
+ if (!bAdded) {
+ auto *pa = Proto_GetContactAccount(cc);
+ char *szProto = (pa == nullptr) ? "SomeShit" : pa->szModuleName;
+ auto pProto = protoMap.find(szProto);
+
+ if (pProto == protoMap.end()) {
+ iIconId = ImageList_ReplaceIcon(hBookmarksIcons, -1, Skin_LoadProtoIcon(szProto, ID_STATUS_ONLINE));
+ protoMap[szProto] = iIconId;
+ }
+ else iIconId = (*pProto).second;
+
+ m_bookmarks.AddItem(Clist_GetContactDisplayName(cc, 0), iIconId, cc);
+ bAdded = true;
+ }
+
+ m_events[cc].push_back(hEvent);
+ }
+ }
+ }
+ }
+ else {
+ ImageList_ReplaceIcon(hBookmarksIcons, -1, g_plugin.getIcon(IDI_BOOKMARK));
+
+ int numItems = m_histCtrl->totalCount;
+
+ for (int i = 0; i < numItems; i++) {
+ auto *pItem = m_histCtrl->GetItem(i);
+ if (pItem->fetch())
+ if (pItem->dbe.flags & DBEF_BOOKMARK)
+ m_bookmarks.AddItem(pItem->getWBuf(), 0, i);
+ }
+ }
+ }
+
+ void BuildTimeTree()
+ {
+ if (!(m_dwOptions & WND_OPT_TIMETREE))
+ return;
+
+ m_timeTree.DeleteAllItems();
+ auto &pArray = m_histCtrl->items;
+ int numItems = pArray.getCount();
+
+ int CurYear = 0, CurMonth = 0, CurDay = 0, PrevYear = -1, PrevMonth = -1, PrevDay = -1;
+ HTREEITEM hCurYear = 0, hCurMonth = 0, hCurDay = 0;
+ for (int i = 0; i < numItems; i++) {
+ auto *pItem = pArray.get(i, false);
+ if (!pItem->fetch())
+ continue;
+
+ if (pItem->dbe.timestamp == 0)
+ continue;
+
+ struct tm ts = { 0 };
+ time_t timestamp = pItem->dbe.timestamp;
+ errno_t err = localtime_s(&ts, &timestamp); /* statically alloced, local time correction */
+ if (err != 0)
+ return;
+
+ CurYear = ts.tm_year + 1900;
+ CurMonth = ts.tm_mon + 1;
+ CurDay = ts.tm_mday;
+ wchar_t buf[50];
+ TVINSERTSTRUCT tvi;
+ tvi.hParent = nullptr;
+ tvi.item.mask = TVIF_TEXT | TVIF_PARAM;
+ if (CurYear != PrevYear) {
+ _itow(CurYear, buf, 10);
+ tvi.item.pszText = buf;
+ tvi.item.lParam = 0;
+ hCurYear = TreeView_InsertItem(m_timeTree.GetHwnd(), &tvi);
+ PrevYear = CurYear;
+ }
+ if (CurMonth != PrevMonth) {
+ tvi.hParent = hCurYear;
+ tvi.item.pszText = TranslateW(months[CurMonth - 1]);
+ tvi.item.lParam = CurMonth;
+ hCurMonth = TreeView_InsertItem(m_timeTree.GetHwnd(), &tvi);
+ PrevMonth = CurMonth;
+ }
+ if (CurDay != PrevDay) {
+ _itow(CurDay, buf, 10);
+ tvi.hParent = hCurMonth;
+ tvi.item.pszText = buf;
+ tvi.item.lParam = 0;
+ hCurDay = TreeView_InsertItem(m_timeTree.GetHwnd(), &tvi);
+ PrevDay = CurDay;
+ }
+ }
+ disableTimeTreeChange = true;
+ HTREEITEM root = m_timeTree.GetRoot();
+ m_timeTree.SelectItem(root);
+ }
+
+ CSplitter splitTime;
+ CCtrlEdit edtSearchText;
+ CCtrlMButton btnUserInfo, btnSendMsg, btnUserMenu, btnCopy, btnOptions, btnFilter;
+ CCtrlMButton btnCalendar, btnSearch, btnExport, btnFindNext, btnFindPrev, btnDelete, btnTimeTree, btnBookmarks;
+ CCtrlListView m_bookmarks;
+ CCtrlTreeView m_timeTree;
+
+public:
+ CHistoryDlg(MCONTACT _hContact) :
+ CDlgBase(g_plugin, IDD_HISTORY),
+ m_arResults(10000),
+ m_hContact(_hContact),
+ m_iSplitter(g_splitter),
+ m_bookmarks(this, IDC_BOOKMARKSLIST),
+ m_timeTree(this, IDC_TIMETREEVIEW),
+ m_histWindow(this, IDC_HISTORYCONTROL),
+ splitTime(this, IDC_SPLITTERY),
+ edtSearchText(this, IDC_SEARCHTEXT),
+ btnCopy(this, IDC_COPY, g_plugin.getIcon(IDI_COPY), LPGEN("Copy")),
+ btnExport(this, IDC_EXPORT, g_plugin.getIcon(IDI_EXPORT), LPGEN("Export...")),
+ btnDelete(this, IDC_DELETE, SKINICON_OTHER_DELETE, LPGEN("Delete...")),
+ btnFilter(this, IDC_FILTER, g_plugin.getIcon(IDI_FILTER), LPGEN("Filter")),
+ btnSearch(this, IDC_SEARCH, g_plugin.getIcon(IDI_SEARCH), LPGEN("Search...")),
+ btnOptions(this, IDC_LOGOPTIONS, g_plugin.getIcon(IDI_OPTIONS), LPGEN("Options")),
+ btnSendMsg(this, IDC_MESSAGE, g_plugin.getIcon(IDI_SENDMSG), LPGEN("Send message")),
+ btnCalendar(this, IDC_DATEPOPUP, g_plugin.getIcon(IDI_CALENDAR), LPGEN("Jump to date")),
+ btnUserInfo(this, IDC_USERINFO, g_plugin.getIcon(IDI_USERINFO), LPGEN("User info")),
+ btnUserMenu(this, IDC_USERMENU, g_plugin.getIcon(IDI_USERMENU), LPGEN("User menu")),
+ btnFindNext(this, IDOK, g_plugin.getIcon(IDI_FINDNEXT), LPGEN("Find next")),
+ btnFindPrev(this, IDC_FINDPREV, g_plugin.getIcon(IDI_FINDPREV), LPGEN("Find previous")),
+ btnTimeTree(this, IDC_TIMETREE, g_plugin.getIcon(IDI_TIMETREE), LPGEN("Conversations")),
+ btnBookmarks(this, IDC_BOOKMARKS, g_plugin.getIcon(IDI_BOOKMARK), LPGEN("Bookmarks"))
+ {
+ showFlags = g_plugin.getWord("showFlags", 0x7f);
+ m_dwOptions = g_plugin.getDword("dwOptions");
+
+ if (!GetDatabasePlugin("JSON")) {
+ btnExport.Disable();
+ btnExport.SetTooltip(LPGEN("You need to install the Import plugin to export events"));
+ }
+
+ if (m_hContact == INVALID_CONTACT_ID)
+ m_dwOptions |= WND_OPT_SEARCHBAR;
+ else if (m_hContact > 0) {
+ m_toolbar.push_back(Button(btnUserMenu));
+ m_toolbar.push_back(Button(btnUserInfo));
+ m_toolbar.push_back(Button(btnSendMsg, Button::SPACED));
+ }
+
+ m_toolbar.push_back(Button(btnTimeTree));
+ m_toolbar.push_back(Button(btnBookmarks));
+ m_toolbar.push_back(Button(btnSearch));
+ m_toolbar.push_back(Button(btnFilter));
+ m_toolbar.push_back(Button(btnCalendar, Button::SPACED));
+ m_toolbar.push_back(Button(btnCopy));
+ m_toolbar.push_back(Button(btnDelete));
+ m_toolbar.push_back(Button(btnExport));
+ m_toolbar.push_back(Button(btnOptions, Button::RIGHT));
+
+ m_timeTree.OnSelChanged = Callback(this, &CHistoryDlg::onSelChanged_TimeTree);
+ m_bookmarks.OnItemChanged = Callback(this, &CHistoryDlg::onSelChanged_Bookmarks);
+
+ edtSearchText.OnChange = Callback(this, &CHistoryDlg::onChange_SearchText);
+
+ splitTime.OnChange = Callback(this, &CHistoryDlg::onChange_Splitter);
+
+ btnCopy.OnClick = Callback(this, &CHistoryDlg::onClick_Copy);
+ btnExport.OnClick = Callback(this, &CHistoryDlg::onClick_Export);
+ btnDelete.OnClick = Callback(this, &CHistoryDlg::onClick_Delete);
+ btnFilter.OnClick = Callback(this, &CHistoryDlg::onClick_Filter);
+ btnSearch.OnClick = Callback(this, &CHistoryDlg::onClick_Search);
+ btnOptions.OnClick = Callback(this, &CHistoryDlg::onClick_Options);
+ btnSendMsg.OnClick = Callback(this, &CHistoryDlg::onClick_Message);
+ btnCalendar.OnClick = Callback(this, &CHistoryDlg::onClick_Calendar);
+ btnFindPrev.OnClick = Callback(this, &CHistoryDlg::onClick_FindPrev);
+ btnUserInfo.OnClick = Callback(this, &CHistoryDlg::onClick_UserInfo);
+ btnUserMenu.OnClick = Callback(this, &CHistoryDlg::onClick_UserMenu);
+ btnTimeTree.OnClick = Callback(this, &CHistoryDlg::onClick_TimeTree);
+ btnBookmarks.OnClick = Callback(this, &CHistoryDlg::onClick_Bookmarks);
+
+ m_hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_POPUPS));
+ TranslateMenu(m_hMenu);
+
+ HMENU hMenu = GetSubMenu(m_hMenu, 0);
+ CheckMenuItem(hMenu, ID_FILTER_INCOMING, (showFlags & HIST_SHOW_IN) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_FILTER_OUTGOING, (showFlags & HIST_SHOW_OUT) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_FILTER_MESSAGES, (showFlags & HIST_SHOW_MSGS) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_FILTER_FILES, (showFlags & HIST_SHOW_FILES) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_FILTER_URLS, (showFlags & HIST_SHOW_URLS) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_FILTER_STATUS, (showFlags & HIST_SHOW_STATUS) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_FILTER_OTHER, (showFlags & HIST_SHOW_OTHER) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu, ID_FILTER_AUTO, (showFlags & HIST_AUTO_FILTER) ? MF_CHECKED : MF_UNCHECKED);
+ }
+
+ bool OnInitDialog() override
+ {
+ // get handles
+ m_hwndBtnCloseSearch = GetDlgItem(m_hwnd, IDC_SEARCHICON);
+ m_hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0, m_hwnd, NULL, g_plugin.getInst(), NULL);
+ SendMessage(m_hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON), 0);
+
+ // bookmarks
+ RECT rc;
+ GetClientRect(m_bookmarks.GetHwnd(), &rc);
+ m_bookmarks.AddColumn(0, TranslateT("Bookmarks"), rc.right - rc.left);
+
+ // filterbar
+ btnFilter.MakePush();
+
+ btnTimeTree.MakePush();
+ if (m_dwOptions & WND_OPT_TIMETREE)
+ btnTimeTree.Push(true);
+
+ btnSearch.MakePush();
+ if (m_dwOptions & WND_OPT_SEARCHBAR)
+ btnSearch.Push(true);
+
+ btnBookmarks.MakePush();
+ if (m_dwOptions & WND_OPT_BOOKMARKS)
+ btnBookmarks.Push(true);
+
+ // filters
+ m_hwndChkDateFrom = GetDlgItem(m_hwnd, IDC_CHK_DATE_FROM);
+ m_hwndChkDateTo = GetDlgItem(m_hwnd, IDC_CHK_DATE_TO);
+ m_hwndDateFrom = GetDlgItem(m_hwnd, IDC_DATE_FROM);
+ m_hwndDateTo = GetDlgItem(m_hwnd, IDC_DATE_TO);
+
+ ibMessages.hwndIco = GetDlgItem(m_hwnd, IDC_ICO_MESSAGES);
+ ibMessages.hwndTxt = GetDlgItem(m_hwnd, IDC_TXT_MESSAGES);
+ ibMessages.hwndIcoIn = GetDlgItem(m_hwnd, IDC_ICO_MESSAGES_IN);
+ SendMessage(ibMessages.hwndIcoIn, BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(ibMessages.hwndIcoIn, BUTTONSETASPUSHBTN, 0, 0);
+ SendMessage(ibMessages.hwndIcoIn, BM_SETCHECK, BST_CHECKED, 0);
+
+ ibMessages.hwndTxtIn = GetDlgItem(m_hwnd, IDC_TXT_MESSAGES_IN);
+ ibMessages.hwndIcoOut = GetDlgItem(m_hwnd, IDC_ICO_MESSAGES_OUT);
+ SendMessage(ibMessages.hwndIcoOut, BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(ibMessages.hwndIcoOut, BUTTONSETASPUSHBTN, 0, 0);
+ SendMessage(ibMessages.hwndIcoOut, BM_SETCHECK, BST_CHECKED, 0);
+ ibMessages.hwndTxtOut = GetDlgItem(m_hwnd, IDC_TXT_MESSAGES_OUT);
+
+ ibFiles.hwndIco = GetDlgItem(m_hwnd, IDC_ICO_FILES);
+ ibFiles.hwndTxt = GetDlgItem(m_hwnd, IDC_TXT_FILES);
+ ibFiles.hwndIcoIn = GetDlgItem(m_hwnd, IDC_ICO_FILES_IN);
+ SendMessage(ibFiles.hwndIcoIn, BUTTONSETASFLATBTN, 0, 0);
+
+ SendMessage(ibFiles.hwndIcoIn, BUTTONSETASPUSHBTN, 0, 0);
+ SendMessage(ibFiles.hwndIcoIn, BM_SETCHECK, BST_CHECKED, 0);
+
+ ibFiles.hwndTxtIn = GetDlgItem(m_hwnd, IDC_TXT_FILES_IN);
+ ibFiles.hwndIcoOut = GetDlgItem(m_hwnd, IDC_ICO_FILES_OUT);
+ SendMessage(ibFiles.hwndIcoOut, BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(ibFiles.hwndIcoOut, BUTTONSETASPUSHBTN, 0, 0);
+ SendMessage(ibFiles.hwndIcoOut, BM_SETCHECK, BST_CHECKED, 0);
+ ibFiles.hwndTxtOut = GetDlgItem(m_hwnd, IDC_TXT_FILES_OUT);
+
+ ibUrls.hwndIco = GetDlgItem(m_hwnd, IDC_ICO_URLS);
+ ibUrls.hwndTxt = GetDlgItem(m_hwnd, IDC_TXT_URLS);
+ ibUrls.hwndIcoIn = GetDlgItem(m_hwnd, IDC_ICO_URLS_IN);
+ SendMessage(ibUrls.hwndIcoIn, BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(ibUrls.hwndIcoIn, BUTTONSETASPUSHBTN, 0, 0);
+ SendMessage(ibUrls.hwndIcoIn, BM_SETCHECK, BST_CHECKED, 0);
+
+ ibUrls.hwndTxtIn = GetDlgItem(m_hwnd, IDC_TXT_URLS_IN);
+ ibUrls.hwndIcoOut = GetDlgItem(m_hwnd, IDC_ICO_URLS_OUT);
+ SendMessage(ibUrls.hwndIcoOut, BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(ibUrls.hwndIcoOut, BUTTONSETASPUSHBTN, 0, 0);
+ SendMessage(ibUrls.hwndIcoOut, BM_SETCHECK, BST_CHECKED, 0);
+ ibUrls.hwndTxtOut = GetDlgItem(m_hwnd, IDC_TXT_URLS_OUT);
+
+ ibTotal.hwndIco = GetDlgItem(m_hwnd, IDC_ICO_TOTAL);
+ ibTotal.hwndTxt = GetDlgItem(m_hwnd, IDC_TXT_TOTAL);
+ ibTotal.hwndIcoIn = GetDlgItem(m_hwnd, IDC_ICO_TOTAL_IN);
+ SendMessage(ibTotal.hwndIcoIn, BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(ibTotal.hwndIcoIn, BUTTONSETASPUSHBTN, 0, 0);
+ SendMessage(ibTotal.hwndIcoIn, BM_SETCHECK, BST_CHECKED, 0);
+
+ ibTotal.hwndTxtIn = GetDlgItem(m_hwnd, IDC_TXT_TOTAL_IN);
+ ibTotal.hwndIcoOut = GetDlgItem(m_hwnd, IDC_ICO_TOTAL_OUT);
+ SendMessage(ibTotal.hwndIcoOut, BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(ibTotal.hwndIcoOut, BUTTONSETASPUSHBTN, 0, 0);
+ SendMessage(ibTotal.hwndIcoOut, BM_SETCHECK, BST_CHECKED, 0);
+ ibTotal.hwndTxtOut = GetDlgItem(m_hwnd, IDC_TXT_TOTAL_OUT);
+
+ // Ask for layout
+ PostMessage(m_hwnd, WM_SIZE, 0, 0);
+
+ WindowList_Add(g_hNewstoryWindows, m_hwnd, m_hContact);
+
+ ShowHideControls();
+ UpdateTitle();
+
+ m_histCtrl = (NewstoryListData *)GetWindowLongPtr(m_histWindow.GetHwnd(), 0);
+
+ if (m_hContact == 0 || m_hContact == INVALID_CONTACT_ID) {
+ btnSendMsg.Hide();
+ btnUserInfo.Hide();
+ btnUserMenu.Hide();
+ }
+
+ if (m_hContact != INVALID_CONTACT_ID) {
+ Utils_RestoreWindowPosition(m_hwnd, m_hContact, MODULENAME, "wnd_");
+
+ m_histCtrl->AddEvent(m_hContact, 0, -1);
+
+ BuildTimeTree();
+ SetFocus(m_histWindow.GetHwnd());
+ }
+ else Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "glb_");
+
+ BuildBookmarksList();
+
+ m_histCtrl->SetContact(m_hContact);
+ m_histCtrl->ScrollBottom();
+
+ Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_NEWSTORY));
+
+ SendMessage(GetDlgItem(m_hwnd, IDC_SEARCHICON), STM_SETICON, (WPARAM)g_plugin.getIcon(IDI_SEARCH), 0);
+
+ SendMessage(ibMessages.hwndIco, STM_SETICON, (LPARAM)g_plugin.getIcon(IDI_SENDMSG), 0);
+ SendMessage(ibMessages.hwndIcoIn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGIN));
+ SendMessage(ibMessages.hwndIcoOut, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGOUT));
+
+ SendMessage(ibFiles.hwndIco, STM_SETICON, (LPARAM)Skin_LoadIcon(SKINICON_EVENT_FILE), 0);
+ SendMessage(ibFiles.hwndIcoIn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGIN));
+ SendMessage(ibFiles.hwndIcoOut, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGOUT));
+
+ SendMessage(ibUrls.hwndIco, STM_SETICON, (LPARAM)Skin_LoadIcon(SKINICON_EVENT_URL), 0);
+ SendMessage(ibUrls.hwndIcoIn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGIN));
+ SendMessage(ibUrls.hwndIcoOut, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGOUT));
+
+ SendMessage(ibTotal.hwndIco, STM_SETICON, (LPARAM)g_plugin.getIcon(IDI_UNKNOWN), 0);
+ SendMessage(ibTotal.hwndIcoIn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGIN));
+ SendMessage(ibTotal.hwndIcoOut, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_MSGOUT));
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ if (m_bSearchChanged) {
+ m_bSearchChanged = false;
+ if (m_hContact == INVALID_CONTACT_ID)
+ DoGlobalSearch();
+ }
+
+ int iOldCaret = m_histCtrl->caret;
+ int res = m_histCtrl->FindNext(ptrW(edtSearchText.GetText()));
+ if (res == -1)
+ SetWindowTextW(m_hwndStatus, TranslateT("No more occuurences found"));
+ else if (res < iOldCaret)
+ SetWindowTextW(m_hwndStatus, TranslateT("Passed the end of history"));
+ else
+ SetWindowTextW(m_hwndStatus, L"");
+
+ return false;
+ }
+
+ void OnResize() override
+ {
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ int y; // tmp vars
+ int w = rc.right - rc.left;
+ int h = rc.bottom - rc.top;
+
+ HDWP hDwp = BeginDeferWindowPos(43 + (int)m_toolbar.size());
+
+ // toolbar
+ int hToolBar = TBTN_SIZE + WND_SPACING;
+
+ int x = WND_SPACING;
+ for (auto &it : m_toolbar) {
+ if (it.options & Button::RIGHT)
+ continue;
+
+ hDwp = DeferWindowPos(hDwp, it.hwnd.GetHwnd(), 0, x, WND_SPACING, TBTN_SIZE, TBTN_SIZE, SWP_NOZORDER);
+ x += TBTN_SIZE;
+ if (it.options & Button::SPACED)
+ x += 10;
+ }
+
+ x = w - WND_SPACING - TBTN_SIZE;
+ for (auto &it : m_toolbar) {
+ if (it.options & Button::RIGHT) {
+ hDwp = DeferWindowPos(hDwp, it.hwnd.GetHwnd(), 0, x, WND_SPACING, TBTN_SIZE, TBTN_SIZE, SWP_NOZORDER);
+ x -= TBTN_SIZE;
+ }
+ }
+
+ // infobar
+ // hDwp = DeferWindowPos(hDwp, hwndIcoProtocol, 0,
+ // w-100+WND_SPACING, WND_SPACING,
+ // 16, 16,
+ // SWP_NOZORDER);
+ // hDwp = DeferWindowPos(hDwp, hwndTxtNickname, 0,
+ // w-100+WND_SPACING*2+16, WND_SPACING,
+ // 100, 16,
+ // SWP_NOZORDER);
+ // hDwp = DeferWindowPos(hDwp, hwndTxtUID, 0,
+ // w-100+WND_SPACING*2+16, WND_SPACING*2+16,
+ // 100, 16,
+ // SWP_NOZORDER);
+
+ // filter bar
+ int hFilterBar = 0;
+ if (m_dwOptions & WND_OPT_FILTERBAR) {
+ hFilterBar = WND_SPACING + (16 + WND_SPACING) * 3;
+ LayoutFilterBar(hDwp, WND_SPACING + (WND_SPACING + 75) * 0, WND_SPACING * 2 + hToolBar, 75, &ibMessages);
+ LayoutFilterBar(hDwp, WND_SPACING + (WND_SPACING + 75) * 1, WND_SPACING * 2 + hToolBar, 75, &ibFiles);
+ LayoutFilterBar(hDwp, WND_SPACING + (WND_SPACING + 75) * 2, WND_SPACING * 2 + hToolBar, 75, &ibUrls);
+ LayoutFilterBar(hDwp, WND_SPACING + (WND_SPACING + 75) * 3, WND_SPACING * 2 + hToolBar, 75, &ibTotal);
+
+ GetWindowRect(m_hwndChkDateFrom, &rc);
+ x = rc.right - rc.left;
+ GetWindowRect(m_hwndDateFrom, &rc);
+ y = hToolBar + WND_SPACING + (WND_SPACING + (16 + WND_SPACING) * 3 - (rc.bottom - rc.top) * 2 - WND_SPACING) / 2;
+ hDwp = DeferWindowPos(hDwp, m_hwndChkDateFrom, 0,
+ w - x - (rc.right - rc.left) - WND_SPACING * 2, y,
+ x, rc.bottom - rc.top,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ hDwp = DeferWindowPos(hDwp, m_hwndDateFrom, 0,
+ w - (rc.right - rc.left) - WND_SPACING, y,
+ rc.right - rc.left, rc.bottom - rc.top,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
+ hDwp = DeferWindowPos(hDwp, m_hwndChkDateTo, 0,
+ w - x - (rc.right - rc.left) - WND_SPACING * 2, y + (rc.bottom - rc.top) + WND_SPACING,
+ x, rc.bottom - rc.top,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ hDwp = DeferWindowPos(hDwp, m_hwndDateTo, 0,
+ w - (rc.right - rc.left) - WND_SPACING, y + (rc.bottom - rc.top) + WND_SPACING,
+ rc.right - rc.left, rc.bottom - rc.top,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
+ hDwp = DeferWindowPos(hDwp, GetDlgItem(m_hwnd, IDC_IB_SEPARATOR), 0,
+ WND_SPACING, hToolBar + WND_SPACING,
+ w - WND_SPACING * 2, 2,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+
+ // general
+ GetWindowRect(m_hwndStatus, &rc);
+ int hStatus = rc.bottom - rc.top;
+ hDwp = DeferWindowPos(hDwp, m_hwndStatus, 0,
+ 0, h - hStatus,
+ w, hStatus,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
+ // search bar
+ int hSearch = 0;
+ if (m_dwOptions & WND_OPT_SEARCHBAR) {
+ GetWindowRect(edtSearchText.GetHwnd(), &rc);
+ hSearch = rc.bottom - rc.top;
+ hDwp = DeferWindowPos(hDwp, m_hwndBtnCloseSearch, 0,
+ WND_SPACING, h - hSearch - hStatus - WND_SPACING,
+ TBTN_SIZE, hSearch, SWP_NOZORDER | SWP_NOACTIVATE);
+ hDwp = DeferWindowPos(hDwp, edtSearchText.GetHwnd(), 0,
+ TBTN_SIZE + WND_SPACING * 2, h - hSearch - hStatus - WND_SPACING,
+ w - WND_SPACING * 4 - TBTN_SIZE * 3, hSearch,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ hDwp = DeferWindowPos(hDwp, btnFindPrev.GetHwnd(), 0,
+ w - WND_SPACING - TBTN_SIZE * 2, h - hSearch - hStatus - WND_SPACING,
+ TBTN_SIZE, hSearch,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ hDwp = DeferWindowPos(hDwp, btnFindNext.GetHwnd(), 0,
+ w - WND_SPACING - TBTN_SIZE * 1, h - hSearch - hStatus - WND_SPACING,
+ TBTN_SIZE, hSearch,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ hSearch += WND_SPACING;
+ }
+
+ int iClientTop = WND_SPACING + hToolBar + hFilterBar;
+ int iClientBottom = h - WND_SPACING - hSearch - hStatus - iClientTop;
+
+ // time tree bar
+ int hTimeTree = 0;
+ bool bShowTime = (m_dwOptions & WND_OPT_TIMETREE) != 0;
+ bool bShowBookmarks = (m_dwOptions & WND_OPT_BOOKMARKS) != 0;
+
+ if (bShowTime || bShowBookmarks) {
+ hTimeTree = m_iSplitter;
+ hDwp = DeferWindowPos(hDwp, splitTime.GetHwnd(), 0, hTimeTree, iClientTop, WND_SPACING, iClientBottom, SWP_NOZORDER | SWP_NOACTIVATE);
+
+ if (bShowTime && bShowBookmarks) {
+ int hMiddle = iClientBottom / 2;
+ hDwp = DeferWindowPos(hDwp, m_timeTree.GetHwnd(), 0, WND_SPACING, iClientTop, hTimeTree - WND_SPACING, hMiddle, SWP_NOZORDER | SWP_NOACTIVATE);
+ hDwp = DeferWindowPos(hDwp, m_bookmarks.GetHwnd(), 0, WND_SPACING, iClientTop + hMiddle, hTimeTree - WND_SPACING, hMiddle, SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+ else if (bShowTime)
+ hDwp = DeferWindowPos(hDwp, m_timeTree.GetHwnd(), 0, WND_SPACING, iClientTop, hTimeTree - WND_SPACING, iClientBottom, SWP_NOZORDER | SWP_NOACTIVATE);
+ else
+ hDwp = DeferWindowPos(hDwp, m_bookmarks.GetHwnd(), 0, WND_SPACING, iClientTop, hTimeTree - WND_SPACING, iClientBottom, SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+
+ hDwp = DeferWindowPos(hDwp, m_histWindow.GetHwnd(), 0,
+ WND_SPACING + hTimeTree, iClientTop,
+ w - WND_SPACING * 2 - hTimeTree, iClientBottom,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
+ EndDeferWindowPos(hDwp);
+ }
+
+ void OnDestroy() override
+ {
+ g_splitter = m_iSplitter;
+
+ if (m_hContact != INVALID_CONTACT_ID)
+ Utils_SaveWindowPosition(m_hwnd, m_hContact, MODULENAME, "wnd_");
+ else
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "glb_");
+
+ g_plugin.setWord(0, "showFlags", showFlags);
+ g_plugin.setDword(0, "dwOptions", m_dwOptions);
+
+ Window_FreeIcon_IcoLib(m_hwnd);
+ WindowList_Remove(g_hNewstoryWindows, m_hwnd);
+
+ if (m_hwndStatus != nullptr) {
+ DestroyWindow(m_hwndStatus);
+ m_hwndStatus = nullptr;
+ }
+ }
+
+ void onClick_Calendar(CCtrlButton *pButton)
+ {
+ RECT rc;
+ GetWindowRect(pButton->GetHwnd(), &rc);
+
+ CalendarTool_Show(m_hwnd, rc.left, rc.bottom);
+ }
+
+ void onClick_Copy(CCtrlButton *)
+ {
+ m_histCtrl->Copy();
+ }
+
+ void onClick_Delete(CCtrlButton *)
+ {
+ if (!CallService(MS_HISTORY_EMPTY, m_hContact, 0)) {
+ m_histCtrl->Clear();
+
+ UpdateTitle();
+ BuildTimeTree();
+ }
+ }
+
+ void onClick_TimeTree(CCtrlButton *)
+ {
+ if (m_dwOptions & WND_OPT_TIMETREE)
+ m_dwOptions &= ~WND_OPT_TIMETREE;
+ else
+ m_dwOptions |= WND_OPT_TIMETREE;
+
+ OnResize();
+ ShowHideControls();
+ BuildTimeTree();
+ }
+
+ void onClick_Bookmarks(CCtrlButton *)
+ {
+ if (m_dwOptions & WND_OPT_BOOKMARKS)
+ m_dwOptions &= ~WND_OPT_BOOKMARKS;
+ else
+ m_dwOptions |= WND_OPT_BOOKMARKS;
+
+ OnResize();
+ ShowHideControls();
+ BuildBookmarksList();
+ }
+
+ void onClick_Export(CCtrlButton *)
+ {
+ auto *pDriver = GetDatabasePlugin("JSON");
+ if (pDriver == nullptr) {
+ CMStringW wszText(TranslateT("The required plugin 'Import' isn't loaded to perform this operation."));
+ if (ServiceExists(MS_PU_SHOWLIST)) {
+ wszText.AppendFormat(L" %s", TranslateT("Do you want to install it using Plugin Updater?"));
+ if (IDYES == MessageBoxW(m_hwnd, wszText, TranslateT("Missing plugin"), MB_YESNO | MB_ICONQUESTION))
+ CallService(MS_PU_SHOWLIST);
+ }
+ else MessageBoxW(m_hwnd, wszText, TranslateT("Missing plugin"), MB_OK | MB_ICONERROR);
+ return;
+ }
+
+ wchar_t FileName[MAX_PATH];
+ VARSW tszMirDir(L"%miranda_userdata%\\NewStoryExport");
+
+ if (db_mc_isMeta(m_hContact)) {
+ CMStringW SubContactsList, MessageText;
+ bool FirstTime = true;
+ int subcount = db_mc_getSubCount(m_hContact);
+ for (int i = 0; i < subcount; i++) {
+ MCONTACT hSubContact = db_mc_getSub(m_hContact, i);
+ char *subproto = Proto_GetBaseAccountName(hSubContact);
+ ptrW subid(Contact::GetInfo(CNF_UNIQUEID, hSubContact, subproto));
+ if (FirstTime)
+ SubContactsList.Append(subid);
+ else
+ SubContactsList.AppendFormat(L"\r\n%s", subid.get());
+ FirstTime = false;
+ }
+ MessageText.AppendFormat(TranslateT("It is metacontact. For export use one of this subcontacts:\r\n%s"), SubContactsList.c_str());
+ MessageBox(m_hwnd, MessageText, TranslateT("Export warning"), MB_OK | MB_ICONWARNING);
+ return;
+ }
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = sizeof(ofn);
+ CMStringW tszFilter, tszTitle, tszFileName;
+ tszFilter.AppendFormat(L"%s (*.json)%c*.json%c%c", TranslateT("JSON files"), 0, 0, 0);
+ tszTitle.AppendFormat(TranslateT("Export history"));
+ ofn.lpstrFilter = tszFilter;
+ ofn.hwndOwner = nullptr;
+ ofn.lpstrTitle = tszTitle;
+ ofn.lpstrFile = FileName;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.Flags = OFN_HIDEREADONLY | OFN_SHAREAWARE | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
+ ofn.lpstrInitialDir = tszMirDir;
+ *FileName = '\0';
+ ofn.lpstrDefExt = L"";
+ if (!GetSaveFileName(&ofn))
+ return;
+
+ // create file
+ CreatePathToFileW(FileName);
+ if (PathFileExistsW(FileName))
+ DeleteFileW(FileName);
+
+ auto *pDB = pDriver->Export(FileName);
+ pDB->BeginExport();
+ pDB->ExportContact(m_hContact);
+
+ // export events
+ int iDone = 0;
+ bAppendOnly = false;
+ auto &arItems = m_histCtrl->items;
+ int iCount = arItems.getCount();
+ for (int i = 0; i < iCount; i++) {
+ auto *pItem = arItems.get(i);
+ if (pItem->m_bSelected) {
+ DB::EventInfo dbei(pItem->hEvent);
+ if (dbei)
+ pDB->ExportEvent(dbei);
+ iDone++;
+ }
+ }
+
+ // no items selected? export whole history
+ if (iDone == 0)
+ for (int i = 0; i < iCount; i++) {
+ auto *pItem = arItems.get(i);
+ DB::EventInfo dbei(pItem->hEvent);
+ if (dbei)
+ pDB->ExportEvent(dbei);
+ }
+
+ // Close the file
+ pDB->EndExport();
+ MessageBox(m_hwnd, TranslateT("Complete"), TranslateT("History export"), MB_OK | MB_ICONINFORMATION);
+ }
+
+ void onClick_Filter(CCtrlButton *)
+ {
+ if (m_dwOptions & WND_OPT_FILTERBAR)
+ m_dwOptions &= ~WND_OPT_FILTERBAR;
+ else
+ m_dwOptions |= WND_OPT_FILTERBAR;
+
+ ShowHideControls();
+ OnResize();
+ }
+
+ void onClick_FindPrev(CCtrlButton *)
+ {
+ int iOldCaret = m_histCtrl->caret;
+ int res = m_histCtrl->FindPrev(ptrW(edtSearchText.GetText()));
+ if (res == -1)
+ SetWindowTextW(m_hwndStatus, TranslateT("No more occuurences found"));
+ else if (res > iOldCaret)
+ SetWindowTextW(m_hwndStatus, TranslateT("Passed the beginning of history"));
+ else
+ SetWindowTextW(m_hwndStatus, L"");
+ }
+
+ void onClick_Message(CCtrlButton *)
+ {
+ CallService(MS_MSG_SENDMESSAGE, m_hContact, 0);
+ }
+
+ void onClick_Options(CCtrlButton *pButton)
+ {
+ RECT rc;
+ GetWindowRect(pButton->GetHwnd(), &rc);
+
+ switch (TrackPopupMenu(GetSubMenu(m_hMenu, 1), TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, NULL)) {
+ case ID_LOGOPTIONS_OPTIONS:
+ g_plugin.openOptions(L"History", L"NewStory", L"Advanced");
+ break;
+
+ case ID_LOGOPTIONS_TEMPLATES:
+ g_plugin.openOptions(L"History", L"NewStory", L"Templates");
+ break;
+ }
+ PostMessage(m_hwnd, WM_SIZE, 0, 0);
+ }
+
+ void onClick_Search(CCtrlButton *)
+ {
+ if (m_dwOptions & WND_OPT_SEARCHBAR)
+ m_dwOptions &= ~WND_OPT_SEARCHBAR;
+ else
+ m_dwOptions |= WND_OPT_SEARCHBAR;
+
+ ShowHideControls();
+ OnResize();
+ }
+
+ void onClick_UserInfo(CCtrlButton *)
+ {
+ CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0);
+ }
+
+ void onClick_UserMenu(CCtrlButton *pButton)
+ {
+ RECT rc;
+ GetWindowRect(pButton->GetHwnd(), &rc);
+
+ HMENU hMenu = Menu_BuildContactMenu(m_hContact);
+ TrackPopupMenu(hMenu, 0, rc.left, rc.bottom, 0, m_hwnd, NULL);
+ DestroyMenu(hMenu);
+ }
+
+ void onChange_SearchText(CCtrlEdit *)
+ {
+ m_bSearchChanged = true;
+
+ if (m_bInitialized)
+ if (showFlags & HIST_AUTO_FILTER)
+ PostMessage(m_hwnd, UM_REBUILDLIST, 0, 0);
+ }
+
+ void onChange_Splitter(CSplitter *)
+ {
+ m_iSplitter = splitTime.GetPos();
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
+ {
+ if ((msg >= NSM_FIRST) && (msg < NSM_LAST)) {
+ LPARAM result = m_histWindow.SendMsg(msg, wParam, lParam);
+ SetWindowLongPtr(m_hwnd, DWLP_MSGRESULT, result);
+ return result;
+ }
+
+ MSG message = { m_hwnd, msg, wParam, lParam };
+ switch (Hotkey_Check(&message, MODULENAME)) {
+ case HOTKEY_SEEK_FORWARD:
+ btnFindNext.Click();
+ break;
+ case HOTKEY_SEEK_BACK:
+ btnFindPrev.Click();
+ break;
+ case HOTKEY_SEARCH:
+ btnSearch.Click();
+ break;
+ }
+
+ switch (msg) {
+ case WM_CHARTOITEM:
+ if (!((GetKeyState(VK_CONTROL) & 0x80) || (GetKeyState(VK_MENU) & 0x80))) {
+ wchar_t s[] = { LOWORD(wParam), 0 };
+ SetWindowText(GetDlgItem(m_hwnd, IDC_SEARCHTEXT), s);
+ SendMessage(GetDlgItem(m_hwnd, IDC_SEARCHTEXT), EM_SETSEL, 1, 1);
+ SetFocus(GetDlgItem(m_hwnd, IDC_SEARCHTEXT));
+ }
+ return -1;
+
+ case WM_COMMAND:
+ if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, m_hContact))
+ return TRUE;
+ break;
+
+ /*
+ GetWindowRect(GetDlgItem(m_hwnd, LOWORD(wParam)), &rc);
+// uint32_t itemID = 0;
+ bool doFilter = true;
+ switch (TrackPopupMenu(GetSubMenu(hMenu, 1), TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, NULL))
+ {
+ case ID_FILTER_INCOMING:
+ {
+ showFlags = toggleBit(showFlags, HIST_SHOW_IN);
+ CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_INCOMING,
+ showFlags&HIST_SHOW_IN ? MF_CHECKED : MF_UNCHECKED);
+ break;
+ }
+ case ID_FILTER_OUTGOING:
+ {
+ showFlags = toggleBit(showFlags, HIST_SHOW_OUT);
+ CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_OUTGOING,
+ showFlags&HIST_SHOW_OUT ? MF_CHECKED : MF_UNCHECKED);
+ break;
+ }
+ case ID_FILTER_MESSAGES:
+ {
+ showFlags = toggleBit(showFlags, HIST_SHOW_MSGS);
+ CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_MESSAGES,
+ showFlags&HIST_SHOW_MSGS ? MF_CHECKED : MF_UNCHECKED);
+ break;
+ }
+ case ID_FILTER_FILES:
+ {
+ showFlags = toggleBit(showFlags, HIST_SHOW_FILES);
+ CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_FILES,
+ showFlags&HIST_SHOW_FILES ? MF_CHECKED : MF_UNCHECKED);
+ break;
+ }
+ case ID_FILTER_URLS:
+ {
+ showFlags = toggleBit(showFlags, HIST_SHOW_URLS);
+ CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_URLS,
+ showFlags&HIST_SHOW_URLS ? MF_CHECKED : MF_UNCHECKED);
+ break;
+ }
+ case ID_FILTER_STATUS:
+ {
+ showFlags = toggleBit(showFlags, HIST_SHOW_STATUS);
+ CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_STATUS,
+ showFlags&HIST_SHOW_STATUS ? MF_CHECKED : MF_UNCHECKED);
+ break;
+ }
+ case ID_FILTER_OTHER:
+ {
+ showFlags = toggleBit(showFlags, HIST_SHOW_OTHER);
+ CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_OTHER,
+ showFlags&HIST_SHOW_OTHER ? MF_CHECKED : MF_UNCHECKED);
+ break;
+ }
+ case ID_FILTER_AUTO:
+ {
+ showFlags = toggleBit(showFlags, HIST_AUTO_FILTER);
+ CheckMenuItem(GetSubMenu(hMenu, 1), ID_FILTER_AUTO,
+ showFlags&HIST_AUTO_FILTER ? MF_CHECKED : MF_UNCHECKED);
+ break;
+ }
+ default:
+ {
+ doFilter = false;
+ break;
+ }
+ }
+ if (doFilter)
+ PostMessage(m_hwnd, UM_REBUILDLIST, 0, 0);
+ break;*/
+
+ case UM_BOOKMARKS:
+ BuildBookmarksList();
+ break;
+
+ case WM_USER + 0x600:
+ if (wParam)
+ m_histWindow.SendMsg(NSM_SEEKTIME, wParam, 0);
+ }
+
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+ }
+
+ void onSelChanged_Bookmarks(CCtrlListView::TEventInfo *ev)
+ {
+ int idx = m_bookmarks.GetItemData(ev->nmlv->iItem);
+
+ if (m_hContact == INVALID_CONTACT_ID) {
+ m_histCtrl->Clear();
+
+ if (idx == INVALID_CONTACT_ID) {
+ for (auto &cc : m_events)
+ for (auto &hEvent : cc.second)
+ m_histCtrl->AddEvent(cc.first, hEvent, 1);
+ }
+ else for (auto &hEvent : m_events[idx])
+ m_histCtrl->AddEvent(idx, hEvent, 1);
+ }
+ else m_histCtrl->SetPos(idx);
+ }
+
+ void onSelChanged_TimeTree(CCtrlTreeView::TEventInfo *)
+ {
+ wchar_t *val1, *val2, *val3;
+ int yearsel = 0, monthsel = 0, daysel = 1;
+ bool monthfound = false;
+ if (disableTimeTreeChange)
+ disableTimeTreeChange = false;
+ else {
+ HTREEITEM hti1 = m_timeTree.GetSelection();
+ TVITEMEX tvi = { 0 };
+ tvi.hItem = hti1;
+ tvi.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_PARAM;
+ tvi.cchTextMax = MAX_PATH;
+ tvi.lParam = 0;
+ tvi.pszText = (wchar_t *)_alloca(MAX_PATH * sizeof(wchar_t));
+
+ m_timeTree.GetItem(&tvi);
+ val1 = tvi.pszText;
+ if (tvi.lParam) {
+ monthsel = tvi.lParam;
+ monthfound = true;
+ }
+ HTREEITEM hti2 = m_timeTree.GetParent(hti1);
+ if ((!monthfound) && (!hti2))
+ yearsel = _wtoi(val1);
+ if ((!monthfound) && (hti2))
+ daysel = _wtoi(val1);
+ if (hti2) {
+ tvi.hItem = hti2;
+ tvi.lParam = 0;
+ m_timeTree.GetItem(&tvi);
+ val2 = tvi.pszText;
+ if (tvi.lParam) {
+ monthsel = tvi.lParam;
+ monthfound = true;
+ }
+ else
+ yearsel = _wtoi(val2);
+ HTREEITEM hti3 = m_timeTree.GetParent(hti2);
+ if (hti3) {
+ tvi.hItem = hti3;
+ tvi.lParam = 0;
+ m_timeTree.GetItem(&tvi);
+ val3 = tvi.pszText;
+ yearsel = _wtoi(val3);
+ }
+ }
+ struct tm tm_sel;
+ tm_sel.tm_hour = tm_sel.tm_min = tm_sel.tm_sec = 0;
+ tm_sel.tm_isdst = 1;
+ tm_sel.tm_mday = daysel;
+ if (monthsel)
+ tm_sel.tm_mon = monthsel - 1;
+ else
+ tm_sel.tm_mon = 0;
+ tm_sel.tm_year = yearsel - 1900;
+ PostMessage(m_hwnd, WM_USER + 0x600, mktime(&tm_sel), 0);
+ }
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Services
+
+INT_PTR svcShowNewstory(WPARAM hContact, LPARAM)
+{
+ HWND hwnd = (HWND)WindowList_Find(g_hNewstoryWindows, hContact);
+ if (!hwnd) {
+ auto *pDlg = new CHistoryDlg(hContact);
+ pDlg->Show();
+ hwnd = pDlg->GetHwnd();
+ }
+
+ SetForegroundWindow(hwnd);
+ SetFocus(hwnd);
+ return 0;
+}
+
+INT_PTR svcGlobalSearch(WPARAM, LPARAM)
+{
+ return svcShowNewstory(INVALID_CONTACT_ID, 0);
+}
diff --git a/plugins/NewStory/src/history_log.cpp b/plugins/NewStory/src/history_log.cpp
index fd820a4fba..d6a3c53f9e 100644
--- a/plugins/NewStory/src/history_log.cpp
+++ b/plugins/NewStory/src/history_log.cpp
@@ -1,116 +1,116 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation version 2
-of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "stdafx.h"
-
-class CNewStoryLogWindow : public CSimpleLogWindow
-{
- HWND m_hwnd = nullptr;
- NewstoryListData *m_histCtrl;
-
-public:
- CNewStoryLogWindow(CMsgDialog &pDlg) :
- CSimpleLogWindow(pDlg)
- {}
-
- void Attach() override
- {
- RECT rc;
- GetClientRect(GetDlgItem(m_pDlg.GetHwnd(), IDC_SRMM_LOG), &rc);
-
- m_hwnd = ::CreateWindowW(_T(NEWSTORYLIST_CLASS), L"NewStory", WS_VISIBLE | WS_CHILD | WS_TABSTOP,
- 0, 0, rc.left - rc.right, rc.bottom - rc.top, m_pDlg.GetHwnd(), 0, m_pDlg.GetInst(), 0);
-
- m_histCtrl = (NewstoryListData *)GetWindowLongPtr(m_hwnd, 0);
- m_histCtrl->SetDialog(&m_pDlg);
- }
-
- void Detach() override
- {
- ::DestroyWindow(m_hwnd);
- }
-
- //////////////////////////////////////////////////////////////////////////////////////
-
- bool AtBottom() override
- {
- return m_histCtrl->AtBottom();
- }
-
- void Clear() override
- {
- m_histCtrl->Clear();
- }
-
- HWND GetHwnd() override
- {
- return m_hwnd;
- }
-
- wchar_t *GetSelection() override
- {
- return nullptr;
- }
-
- int GetType() override
- {
- return 1;
- }
-
- void LogEvents(MEVENT hDbEvent, int count, bool bAppend) override
- {
- if (!hDbEvent)
- return;
-
- if (!bAppend)
- Clear();
-
- m_histCtrl->AddEvent(m_pDlg.m_hContact, hDbEvent, count);
- }
-
- void LogChatEvent(const LOGINFO &lin) override
- {
- m_histCtrl->AddChatEvent(m_pDlg.getChat(), &lin);
- }
-
- void Resize() override
- {
- bool bottomScroll = m_pDlg.isChat() ? AtBottom() : true;
-
- RECT rc;
- GetWindowRect(GetDlgItem(m_pDlg.GetHwnd(), IDC_SRMM_LOG), &rc);
-
- POINT pt = { rc.left, rc.top };
- ScreenToClient(GetParent(m_hwnd), &pt);
-
- ::SetWindowPos(m_hwnd, 0, pt.x, pt.y, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
-
- if (bottomScroll)
- ScrollToBottom();
- }
-
- void ScrollToBottom() override
- {
- m_histCtrl->ScrollBottom();
- }
-};
-
-CSrmmLogWindow *__cdecl NewStory_Stub(CMsgDialog &pDlg)
-{
- return new CNewStoryLogWindow(pDlg);
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+class CNewStoryLogWindow : public CSimpleLogWindow
+{
+ HWND m_hwnd = nullptr;
+ NewstoryListData *m_histCtrl;
+
+public:
+ CNewStoryLogWindow(CMsgDialog &pDlg) :
+ CSimpleLogWindow(pDlg)
+ {}
+
+ void Attach() override
+ {
+ RECT rc;
+ GetClientRect(GetDlgItem(m_pDlg.GetHwnd(), IDC_SRMM_LOG), &rc);
+
+ m_hwnd = ::CreateWindowW(_T(NEWSTORYLIST_CLASS), L"NewStory", WS_VISIBLE | WS_CHILD | WS_TABSTOP,
+ 0, 0, rc.left - rc.right, rc.bottom - rc.top, m_pDlg.GetHwnd(), 0, m_pDlg.GetInst(), 0);
+
+ m_histCtrl = (NewstoryListData *)GetWindowLongPtr(m_hwnd, 0);
+ m_histCtrl->SetDialog(&m_pDlg);
+ }
+
+ void Detach() override
+ {
+ ::DestroyWindow(m_hwnd);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////
+
+ bool AtBottom() override
+ {
+ return m_histCtrl->AtBottom();
+ }
+
+ void Clear() override
+ {
+ m_histCtrl->Clear();
+ }
+
+ HWND GetHwnd() override
+ {
+ return m_hwnd;
+ }
+
+ wchar_t *GetSelection() override
+ {
+ return nullptr;
+ }
+
+ int GetType() override
+ {
+ return 1;
+ }
+
+ void LogEvents(MEVENT hDbEvent, int count, bool bAppend) override
+ {
+ if (!hDbEvent)
+ return;
+
+ if (!bAppend)
+ Clear();
+
+ m_histCtrl->AddEvent(m_pDlg.m_hContact, hDbEvent, count);
+ }
+
+ void LogChatEvent(const LOGINFO &lin) override
+ {
+ m_histCtrl->AddChatEvent(m_pDlg.getChat(), &lin);
+ }
+
+ void Resize() override
+ {
+ bool bottomScroll = m_pDlg.isChat() ? AtBottom() : true;
+
+ RECT rc;
+ GetWindowRect(GetDlgItem(m_pDlg.GetHwnd(), IDC_SRMM_LOG), &rc);
+
+ POINT pt = { rc.left, rc.top };
+ ScreenToClient(GetParent(m_hwnd), &pt);
+
+ ::SetWindowPos(m_hwnd, 0, pt.x, pt.y, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
+
+ if (bottomScroll)
+ ScrollToBottom();
+ }
+
+ void ScrollToBottom() override
+ {
+ m_histCtrl->ScrollBottom();
+ }
+};
+
+CSrmmLogWindow *__cdecl NewStory_Stub(CMsgDialog &pDlg)
+{
+ return new CNewStoryLogWindow(pDlg);
+}
diff --git a/plugins/NewStory/src/history_menus.cpp b/plugins/NewStory/src/history_menus.cpp
index 747f8e4f8e..d5249817e9 100644
--- a/plugins/NewStory/src/history_menus.cpp
+++ b/plugins/NewStory/src/history_menus.cpp
@@ -1,301 +1,301 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
-
-enum
-{
- MENU_COPY, MENU_COPYTEXT, MENU_COPYURL, MENU_COPYPATH, MENU_OPENFOLDER, MENU_QUOTE,
- MENU_SAVEAS, MENU_DOWNLOAD,
- MENU_EDIT, MENU_DELETE, MENU_REPLY,
- MENU_SELECTALL, MENU_BOOKMARK,
-};
-
-static int hMenuObject;
-static HANDLE hEventPreBuildMenu;
-static HGENMENU hmiHistory, hmiOpenFolder, hmiCopyUrl, hmiCopyPath, hmiSaveAs, hmiDownload, hmiQuote;
-static HGENMENU hmiCopy, hmiCopyText, hmiEdit, hmiBookmark, hmiDelete, hmiReply;
-
-HMENU NSMenu_Build(NewstoryListData *data, ItemData *item)
-{
- bool bNotProtected = true;
- if (item != nullptr)
- if (auto *szProto = Proto_GetBaseAccountName(item->hContact))
- bNotProtected = db_get_b(item->hContact, szProto, "Protected") == 0;
-
- Menu_ShowItem(hmiCopy, bNotProtected);
- Menu_ShowItem(hmiCopyText, bNotProtected);
-
- Menu_ShowItem(hmiQuote, bNotProtected && data->pMsgDlg != nullptr);
- Menu_ShowItem(hmiReply, false);
- Menu_ShowItem(hmiSaveAs, false);
- Menu_ShowItem(hmiCopyUrl, false);
- Menu_ShowItem(hmiCopyPath, false);
- Menu_ShowItem(hmiDownload, false);
- Menu_ShowItem(hmiOpenFolder, false);
-
- bool bShowEventActions, bEditable;
- if (item != nullptr) {
- if (item->m_bOfflineFile) {
- Menu_ShowItem(hmiSaveAs, bNotProtected);
- Menu_ShowItem(hmiCopyUrl, true);
- Menu_ShowItem(hmiCopyPath, item->m_bOfflineDownloaded);
- Menu_ShowItem(hmiDownload, !item->m_bOfflineDownloaded && bNotProtected);
- Menu_ShowItem(hmiOpenFolder, item->m_bOfflineDownloaded);
- }
-
- bEditable = (item->dbe.flags & DBEF_SENT) != 0;
- bShowEventActions = item->hEvent != 0;
-
- DB::EventInfo dbei(item->hEvent);
- NotifyEventHooks(hEventPreBuildMenu, item->hContact, (LPARAM)&dbei);
-
- if (data->pMsgDlg) {
- INT_PTR caps = CallProtoService(Proto_GetBaseAccountName(item->hContact), PS_GETCAPS, PFLAGNUM_4, 0);
- Menu_ShowItem(hmiReply, (caps & PF4_REPLY) != 0 && mir_strlen(dbei.szId) > 0 && !Contact::IsReadonly(item->hContact));
- }
- }
- else {
- bShowEventActions = bEditable = false;
-
- DB::EventInfo dbei;
- NotifyEventHooks(hEventPreBuildMenu, 0, (LPARAM)&dbei);
- }
-
- Menu_ShowItem(hmiEdit, bShowEventActions && bEditable);
- Menu_ShowItem(hmiDelete, bShowEventActions);
- Menu_ShowItem(hmiBookmark, bShowEventActions);
-
- HMENU hMenu = CreatePopupMenu();
- Menu_Build(hMenu, hMenuObject);
- return hMenu;
-}
-
-bool NSMenu_Process(int iCommand, NewstoryListData *data)
-{
- if (Menu_ProcessCommandById(iCommand, LPARAM(data)))
- return true;
-
- if (auto *pDlg = data->pMsgDlg) {
- PostMessage(pDlg->GetHwnd(), WM_MOUSEACTIVATE, 0, 0);
- if (Chat_DoEventHook(pDlg->getChat(), GC_USER_LOGMENU, nullptr, nullptr, iCommand))
- return true;
- }
-
- return false;
-}
-
-static INT_PTR NSMenuHelper(WPARAM wParam, LPARAM lParam)
-{
- auto *pData = (NewstoryListData *)lParam;
-
- switch (wParam) {
- case MENU_COPY:
- pData->Copy(false);
- break;
-
- case MENU_COPYTEXT:
- pData->Copy(true);
- break;
-
- case MENU_COPYURL:
- pData->CopyUrl();
- break;
-
- case MENU_COPYPATH:
- pData->CopyPath();
- break;
-
- case MENU_OPENFOLDER:
- pData->OpenFolder();
- break;
-
- case MENU_QUOTE:
- pData->Quote();
- break;
-
- case MENU_REPLY:
- pData->Reply();
- break;
-
- case MENU_EDIT:
- pData->BeginEditItem();
- break;
-
- case MENU_DELETE:
- pData->DeleteItems();
- break;
-
- case MENU_SELECTALL:
- SendMessage(pData->m_hwnd, NSM_SELECTITEMS, 0, pData->totalCount - 1);
- break;
-
- case MENU_SAVEAS:
- pData->Download(OFD_SAVEAS | OFD_RUN);
- break;
-
- case MENU_DOWNLOAD:
- pData->Download(OFD_DOWNLOAD);
- break;
-
- case MENU_BOOKMARK:
- pData->ToggleBookmark();
- PostMessage(GetParent(pData->m_hwnd), UM_BOOKMARKS, 0, 0);
- break;
- }
-
- return 0;
-}
-
-static INT_PTR NSMenuAddService(WPARAM wParam, LPARAM lParam)
-{
- auto *pmi = (TMO_MenuItem *)wParam;
- if (pmi == nullptr)
- return 0;
-
- auto *mmep = (NSMenuExecParam *)mir_calloc(sizeof(NSMenuExecParam));
- if (mmep == nullptr)
- return 0;
-
- // we need just one parametr.
- mmep->szServiceName = mir_strdup(pmi->pszService);
- mmep->iParam = lParam;
-
- HGENMENU hNewItem = Menu_AddItem(hMenuObject, pmi, mmep);
-
- char buf[1024];
- mir_snprintf(buf, "%s/%s", pmi->pszService, pmi->name.a);
- Menu_ConfigureItem(hNewItem, MCI_OPT_UNIQUENAME, buf);
- return (INT_PTR)hNewItem;
-}
-
-static INT_PTR NSMenuExecService(WPARAM wParam, LPARAM lParam)
-{
- if (auto *pParam = (NSMenuExecParam *)wParam)
- CallService(pParam->szServiceName, pParam->iParam, lParam);
-
- return 0;
-}
-
-static INT_PTR NSMenuFreeOwnerData(WPARAM, LPARAM lParam)
-{
- if (auto *param = (NSMenuExecParam *)lParam) {
- mir_free(param->szServiceName);
- mir_free(param);
- }
- return 0;
-}
-
-static int OnPrebuildContactMenu(WPARAM hContact, LPARAM)
-{
- Menu_ShowItem(hmiHistory, db_event_first(hContact) != 0);
- return 0;
-}
-
-void InitMenus()
-{
- { // Contact menu items
- CMenuItem mi(&g_plugin);
- SET_UID(mi, 0xc20d7a69, 0x7607, 0x4aad, 0xa7, 0x42, 0x10, 0x86, 0xfb, 0x32, 0x49, 0x21);
- mi.name.a = LPGEN("History");
- mi.position = 1000090000;
- mi.pszService = MS_HISTORY_SHOWCONTACTHISTORY;
- mi.hIcolibItem = g_plugin.getIconHandle(IDI_NEWSTORY);
- hmiHistory = Menu_AddContactMenuItem(&mi);
- CreateServiceFunction(mi.pszService, svcShowNewstory);
-
- HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPrebuildContactMenu);
-
- // Main menu items
- SET_UID(mi, 0x1848519e, 0x639d, 0x497a, 0xa5, 0x37, 0x6b, 0x76, 0x17, 0x2a, 0x6, 0xd9);
- mi.name.a = LPGEN("Global search");
- mi.position = 500060001;
- mi.pszService = "NewStory/GlobalSearch";
- Menu_AddMainMenuItem(&mi);
- CreateServiceFunction(mi.pszService, svcGlobalSearch);
- }
-
- // Init history item's menu
- CreateServiceFunction("NSMenu/Helper", NSMenuHelper);
- CreateServiceFunction("NSMenu/AddService", NSMenuAddService);
- CreateServiceFunction("NSMenu/ExecService", NSMenuExecService);
- CreateServiceFunction("NSMenu/FreeOwnerData", NSMenuFreeOwnerData);
-
- hEventPreBuildMenu = CreateHookableEvent(ME_NS_PREBUILDMENU);
-
- hMenuObject = Menu_AddObject("NSMenu", "NewStory item menu", nullptr, "NSMenu/ExecService");
- Menu_ConfigureObject(hMenuObject, MCO_OPT_USERDEFINEDITEMS, INT_PTR(FALSE));
- Menu_ConfigureObject(hMenuObject, MCO_OPT_FREE_SERVICE, INT_PTR("NSMenu/FreeOwnerData"));
- Menu_ConfigureObject(hMenuObject, MCO_OPT_ONADD_SERVICE, INT_PTR("NSMenu/OnAddService"));
-
- CMenuItem mi(&g_plugin);
- mi.pszService = "NSMenu/Helper";
-
- mi.position = 100000;
- mi.name.a = LPGEN("Copy");
- hmiCopy = Menu_AddNewStoryMenuItem(&mi, MENU_COPY);
-
- mi.position++;
- mi.name.a = LPGEN("Copy text");
- hmiCopyText = Menu_AddNewStoryMenuItem(&mi, MENU_COPYTEXT);
-
- mi.position++;
- mi.name.a = LPGEN("Copy URL");
- hmiCopyUrl = Menu_AddNewStoryMenuItem(&mi, MENU_COPYURL);
-
- mi.position++;
- mi.name.a = LPGEN("Copy file path");
- hmiCopyPath = Menu_AddNewStoryMenuItem(&mi, MENU_COPYPATH);
-
- mi.position++;
- mi.name.a = LPGEN("Show in folder");
- hmiOpenFolder = Menu_AddNewStoryMenuItem(&mi, MENU_OPENFOLDER);
-
- mi.position++;
- mi.name.a = LPGEN("Quote");
- hmiQuote = Menu_AddNewStoryMenuItem(&mi, MENU_QUOTE);
-
- mi.position++;
- mi.name.a = LPGEN("Save as");
- hmiSaveAs = Menu_AddNewStoryMenuItem(&mi, MENU_SAVEAS);
-
- mi.position++;
- mi.name.a = LPGEN("Download");
- hmiDownload = Menu_AddNewStoryMenuItem(&mi, MENU_DOWNLOAD);
-
- mi.position = 200000;
- mi.name.a = LPGEN("Edit");
- hmiEdit = Menu_AddNewStoryMenuItem(&mi, MENU_EDIT);
-
- mi.position++;
- mi.name.a = LPGEN("Delete");
- hmiDelete = Menu_AddNewStoryMenuItem(&mi, MENU_DELETE);
-
- mi.position++;
- mi.name.a = LPGEN("Toggle bookmark");
- hmiBookmark = Menu_AddNewStoryMenuItem(&mi, MENU_BOOKMARK);
-
- mi.position = 300000;
- mi.name.a = LPGEN("Select all");
- Menu_AddNewStoryMenuItem(&mi, MENU_SELECTALL);
-
- mi.position = NS_PROTO_MENU_POS - 1;
- mi.hIcolibItem = g_plugin.getIconHandle(IDI_REPLY);
- mi.name.a = LPGEN("Reply");
- hmiReply = Menu_AddNewStoryMenuItem(&mi, MENU_REPLY);
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
+
+enum
+{
+ MENU_COPY, MENU_COPYTEXT, MENU_COPYURL, MENU_COPYPATH, MENU_OPENFOLDER, MENU_QUOTE,
+ MENU_SAVEAS, MENU_DOWNLOAD,
+ MENU_EDIT, MENU_DELETE, MENU_REPLY,
+ MENU_SELECTALL, MENU_BOOKMARK,
+};
+
+static int hMenuObject;
+static HANDLE hEventPreBuildMenu;
+static HGENMENU hmiHistory, hmiOpenFolder, hmiCopyUrl, hmiCopyPath, hmiSaveAs, hmiDownload, hmiQuote;
+static HGENMENU hmiCopy, hmiCopyText, hmiEdit, hmiBookmark, hmiDelete, hmiReply;
+
+HMENU NSMenu_Build(NewstoryListData *data, ItemData *item)
+{
+ bool bNotProtected = true;
+ if (item != nullptr)
+ if (auto *szProto = Proto_GetBaseAccountName(item->hContact))
+ bNotProtected = db_get_b(item->hContact, szProto, "Protected") == 0;
+
+ Menu_ShowItem(hmiCopy, bNotProtected);
+ Menu_ShowItem(hmiCopyText, bNotProtected);
+
+ Menu_ShowItem(hmiQuote, bNotProtected && data->pMsgDlg != nullptr);
+ Menu_ShowItem(hmiReply, false);
+ Menu_ShowItem(hmiSaveAs, false);
+ Menu_ShowItem(hmiCopyUrl, false);
+ Menu_ShowItem(hmiCopyPath, false);
+ Menu_ShowItem(hmiDownload, false);
+ Menu_ShowItem(hmiOpenFolder, false);
+
+ bool bShowEventActions, bEditable;
+ if (item != nullptr) {
+ if (item->m_bOfflineFile) {
+ Menu_ShowItem(hmiSaveAs, bNotProtected);
+ Menu_ShowItem(hmiCopyUrl, true);
+ Menu_ShowItem(hmiCopyPath, item->m_bOfflineDownloaded);
+ Menu_ShowItem(hmiDownload, !item->m_bOfflineDownloaded && bNotProtected);
+ Menu_ShowItem(hmiOpenFolder, item->m_bOfflineDownloaded);
+ }
+
+ bEditable = (item->dbe.flags & DBEF_SENT) != 0;
+ bShowEventActions = item->hEvent != 0;
+
+ DB::EventInfo dbei(item->hEvent);
+ NotifyEventHooks(hEventPreBuildMenu, item->hContact, (LPARAM)&dbei);
+
+ if (data->pMsgDlg) {
+ INT_PTR caps = CallProtoService(Proto_GetBaseAccountName(item->hContact), PS_GETCAPS, PFLAGNUM_4, 0);
+ Menu_ShowItem(hmiReply, (caps & PF4_REPLY) != 0 && mir_strlen(dbei.szId) > 0 && !Contact::IsReadonly(item->hContact));
+ }
+ }
+ else {
+ bShowEventActions = bEditable = false;
+
+ DB::EventInfo dbei;
+ NotifyEventHooks(hEventPreBuildMenu, 0, (LPARAM)&dbei);
+ }
+
+ Menu_ShowItem(hmiEdit, bShowEventActions && bEditable);
+ Menu_ShowItem(hmiDelete, bShowEventActions);
+ Menu_ShowItem(hmiBookmark, bShowEventActions);
+
+ HMENU hMenu = CreatePopupMenu();
+ Menu_Build(hMenu, hMenuObject);
+ return hMenu;
+}
+
+bool NSMenu_Process(int iCommand, NewstoryListData *data)
+{
+ if (Menu_ProcessCommandById(iCommand, LPARAM(data)))
+ return true;
+
+ if (auto *pDlg = data->pMsgDlg) {
+ PostMessage(pDlg->GetHwnd(), WM_MOUSEACTIVATE, 0, 0);
+ if (Chat_DoEventHook(pDlg->getChat(), GC_USER_LOGMENU, nullptr, nullptr, iCommand))
+ return true;
+ }
+
+ return false;
+}
+
+static INT_PTR NSMenuHelper(WPARAM wParam, LPARAM lParam)
+{
+ auto *pData = (NewstoryListData *)lParam;
+
+ switch (wParam) {
+ case MENU_COPY:
+ pData->Copy(false);
+ break;
+
+ case MENU_COPYTEXT:
+ pData->Copy(true);
+ break;
+
+ case MENU_COPYURL:
+ pData->CopyUrl();
+ break;
+
+ case MENU_COPYPATH:
+ pData->CopyPath();
+ break;
+
+ case MENU_OPENFOLDER:
+ pData->OpenFolder();
+ break;
+
+ case MENU_QUOTE:
+ pData->Quote();
+ break;
+
+ case MENU_REPLY:
+ pData->Reply();
+ break;
+
+ case MENU_EDIT:
+ pData->BeginEditItem();
+ break;
+
+ case MENU_DELETE:
+ pData->DeleteItems();
+ break;
+
+ case MENU_SELECTALL:
+ SendMessage(pData->m_hwnd, NSM_SELECTITEMS, 0, pData->totalCount - 1);
+ break;
+
+ case MENU_SAVEAS:
+ pData->Download(OFD_SAVEAS | OFD_RUN);
+ break;
+
+ case MENU_DOWNLOAD:
+ pData->Download(OFD_DOWNLOAD);
+ break;
+
+ case MENU_BOOKMARK:
+ pData->ToggleBookmark();
+ PostMessage(GetParent(pData->m_hwnd), UM_BOOKMARKS, 0, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static INT_PTR NSMenuAddService(WPARAM wParam, LPARAM lParam)
+{
+ auto *pmi = (TMO_MenuItem *)wParam;
+ if (pmi == nullptr)
+ return 0;
+
+ auto *mmep = (NSMenuExecParam *)mir_calloc(sizeof(NSMenuExecParam));
+ if (mmep == nullptr)
+ return 0;
+
+ // we need just one parametr.
+ mmep->szServiceName = mir_strdup(pmi->pszService);
+ mmep->iParam = lParam;
+
+ HGENMENU hNewItem = Menu_AddItem(hMenuObject, pmi, mmep);
+
+ char buf[1024];
+ mir_snprintf(buf, "%s/%s", pmi->pszService, pmi->name.a);
+ Menu_ConfigureItem(hNewItem, MCI_OPT_UNIQUENAME, buf);
+ return (INT_PTR)hNewItem;
+}
+
+static INT_PTR NSMenuExecService(WPARAM wParam, LPARAM lParam)
+{
+ if (auto *pParam = (NSMenuExecParam *)wParam)
+ CallService(pParam->szServiceName, pParam->iParam, lParam);
+
+ return 0;
+}
+
+static INT_PTR NSMenuFreeOwnerData(WPARAM, LPARAM lParam)
+{
+ if (auto *param = (NSMenuExecParam *)lParam) {
+ mir_free(param->szServiceName);
+ mir_free(param);
+ }
+ return 0;
+}
+
+static int OnPrebuildContactMenu(WPARAM hContact, LPARAM)
+{
+ Menu_ShowItem(hmiHistory, db_event_first(hContact) != 0);
+ return 0;
+}
+
+void InitMenus()
+{
+ { // Contact menu items
+ CMenuItem mi(&g_plugin);
+ SET_UID(mi, 0xc20d7a69, 0x7607, 0x4aad, 0xa7, 0x42, 0x10, 0x86, 0xfb, 0x32, 0x49, 0x21);
+ mi.name.a = LPGEN("History");
+ mi.position = 1000090000;
+ mi.pszService = MS_HISTORY_SHOWCONTACTHISTORY;
+ mi.hIcolibItem = g_plugin.getIconHandle(IDI_NEWSTORY);
+ hmiHistory = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, svcShowNewstory);
+
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPrebuildContactMenu);
+
+ // Main menu items
+ SET_UID(mi, 0x1848519e, 0x639d, 0x497a, 0xa5, 0x37, 0x6b, 0x76, 0x17, 0x2a, 0x6, 0xd9);
+ mi.name.a = LPGEN("Global search");
+ mi.position = 500060001;
+ mi.pszService = "NewStory/GlobalSearch";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, svcGlobalSearch);
+ }
+
+ // Init history item's menu
+ CreateServiceFunction("NSMenu/Helper", NSMenuHelper);
+ CreateServiceFunction("NSMenu/AddService", NSMenuAddService);
+ CreateServiceFunction("NSMenu/ExecService", NSMenuExecService);
+ CreateServiceFunction("NSMenu/FreeOwnerData", NSMenuFreeOwnerData);
+
+ hEventPreBuildMenu = CreateHookableEvent(ME_NS_PREBUILDMENU);
+
+ hMenuObject = Menu_AddObject("NSMenu", "NewStory item menu", nullptr, "NSMenu/ExecService");
+ Menu_ConfigureObject(hMenuObject, MCO_OPT_USERDEFINEDITEMS, INT_PTR(FALSE));
+ Menu_ConfigureObject(hMenuObject, MCO_OPT_FREE_SERVICE, INT_PTR("NSMenu/FreeOwnerData"));
+ Menu_ConfigureObject(hMenuObject, MCO_OPT_ONADD_SERVICE, INT_PTR("NSMenu/OnAddService"));
+
+ CMenuItem mi(&g_plugin);
+ mi.pszService = "NSMenu/Helper";
+
+ mi.position = 100000;
+ mi.name.a = LPGEN("Copy");
+ hmiCopy = Menu_AddNewStoryMenuItem(&mi, MENU_COPY);
+
+ mi.position++;
+ mi.name.a = LPGEN("Copy text");
+ hmiCopyText = Menu_AddNewStoryMenuItem(&mi, MENU_COPYTEXT);
+
+ mi.position++;
+ mi.name.a = LPGEN("Copy URL");
+ hmiCopyUrl = Menu_AddNewStoryMenuItem(&mi, MENU_COPYURL);
+
+ mi.position++;
+ mi.name.a = LPGEN("Copy file path");
+ hmiCopyPath = Menu_AddNewStoryMenuItem(&mi, MENU_COPYPATH);
+
+ mi.position++;
+ mi.name.a = LPGEN("Show in folder");
+ hmiOpenFolder = Menu_AddNewStoryMenuItem(&mi, MENU_OPENFOLDER);
+
+ mi.position++;
+ mi.name.a = LPGEN("Quote");
+ hmiQuote = Menu_AddNewStoryMenuItem(&mi, MENU_QUOTE);
+
+ mi.position++;
+ mi.name.a = LPGEN("Save as");
+ hmiSaveAs = Menu_AddNewStoryMenuItem(&mi, MENU_SAVEAS);
+
+ mi.position++;
+ mi.name.a = LPGEN("Download");
+ hmiDownload = Menu_AddNewStoryMenuItem(&mi, MENU_DOWNLOAD);
+
+ mi.position = 200000;
+ mi.name.a = LPGEN("Edit");
+ hmiEdit = Menu_AddNewStoryMenuItem(&mi, MENU_EDIT);
+
+ mi.position++;
+ mi.name.a = LPGEN("Delete");
+ hmiDelete = Menu_AddNewStoryMenuItem(&mi, MENU_DELETE);
+
+ mi.position++;
+ mi.name.a = LPGEN("Toggle bookmark");
+ hmiBookmark = Menu_AddNewStoryMenuItem(&mi, MENU_BOOKMARK);
+
+ mi.position = 300000;
+ mi.name.a = LPGEN("Select all");
+ Menu_AddNewStoryMenuItem(&mi, MENU_SELECTALL);
+
+ mi.position = NS_PROTO_MENU_POS - 1;
+ mi.hIcolibItem = g_plugin.getIconHandle(IDI_REPLY);
+ mi.name.a = LPGEN("Reply");
+ hmiReply = Menu_AddNewStoryMenuItem(&mi, MENU_REPLY);
+}
diff --git a/plugins/NewStory/src/history_svc.cpp b/plugins/NewStory/src/history_svc.cpp
index 48e9936d7b..db05f3da04 100644
--- a/plugins/NewStory/src/history_svc.cpp
+++ b/plugins/NewStory/src/history_svc.cpp
@@ -1,62 +1,62 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation version 2
-of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "stdafx.h"
-
-static INT_PTR SvcGetCurrent(WPARAM wParam, LPARAM)
-{
- if (auto *pData = (NewstoryListData *)wParam)
- if (auto *p = pData->GetItem(pData->caret))
- return p->hEvent;
-
- return 0;
-}
-
-static INT_PTR SvcGetSrmm(WPARAM wParam, LPARAM)
-{
- auto *pData = (NewstoryListData *)wParam;
- return (pData) ? INT_PTR(pData->pMsgDlg) : 0;
-}
-
-static INT_PTR SvcGetSelection(WPARAM wParam, LPARAM lParam)
-{
- auto *pData = (NewstoryListData *)wParam;
- auto *pRet = (std::vector<MEVENT>*)lParam;
- if (pData && pRet) {
- for (int i = pData->totalCount; i >= 0; i--)
- if (auto *p = pData->items.get(i))
- if (p->m_bSelected)
- pRet->push_back(p->hEvent);
-
- if (pRet->empty() && pData->caret != -1)
- if (auto *p = pData->GetItem(pData->caret))
- pRet->push_back(p->hEvent);
- }
-
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Module entry point
-
-void InitServices()
-{
- CreateServiceFunction("NewStory/GetSrmm", &SvcGetSrmm);
- CreateServiceFunction("NewStory/GetCurrent", &SvcGetCurrent);
- CreateServiceFunction("NewStory/GetSelection", &SvcGetSelection);
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+static INT_PTR SvcGetCurrent(WPARAM wParam, LPARAM)
+{
+ if (auto *pData = (NewstoryListData *)wParam)
+ if (auto *p = pData->GetItem(pData->caret))
+ return p->hEvent;
+
+ return 0;
+}
+
+static INT_PTR SvcGetSrmm(WPARAM wParam, LPARAM)
+{
+ auto *pData = (NewstoryListData *)wParam;
+ return (pData) ? INT_PTR(pData->pMsgDlg) : 0;
+}
+
+static INT_PTR SvcGetSelection(WPARAM wParam, LPARAM lParam)
+{
+ auto *pData = (NewstoryListData *)wParam;
+ auto *pRet = (std::vector<MEVENT>*)lParam;
+ if (pData && pRet) {
+ for (int i = pData->totalCount; i >= 0; i--)
+ if (auto *p = pData->items.get(i))
+ if (p->m_bSelected)
+ pRet->push_back(p->hEvent);
+
+ if (pRet->empty() && pData->caret != -1)
+ if (auto *p = pData->GetItem(pData->caret))
+ pRet->push_back(p->hEvent);
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Module entry point
+
+void InitServices()
+{
+ CreateServiceFunction("NewStory/GetSrmm", &SvcGetSrmm);
+ CreateServiceFunction("NewStory/GetCurrent", &SvcGetCurrent);
+ CreateServiceFunction("NewStory/GetSelection", &SvcGetSelection);
+}
diff --git a/plugins/NewStory/src/main.cpp b/plugins/NewStory/src/main.cpp
index 0cd647fd3e..c45312d809 100644
--- a/plugins/NewStory/src/main.cpp
+++ b/plugins/NewStory/src/main.cpp
@@ -1,203 +1,203 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation version 2
-of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "stdafx.h"
-
-CMPlugin g_plugin;
-
-CMOption<bool> g_bOptGrouping(MODULENAME, "MessageGrouping", false);
-CMOption<bool> g_bOptDrawEdge(MODULENAME, "DrawEdge", true);
-CMOption<bool> g_bOptHppCompat(MODULENAME, "HppCompat", false);
-CMOption<bool> g_bShowType(MODULENAME, "ShowType", true);
-CMOption<bool> g_bShowDirection(MODULENAME, "ShowDirection", true);
-
-CMOption<uint32_t> g_clCustom0(MODULENAME, "CustomColor0", -1);
-CMOption<uint32_t> g_clCustom1(MODULENAME, "CustomColor1", -1);
-CMOption<uint32_t> g_clCustom2(MODULENAME, "CustomColor2", -1);
-CMOption<uint32_t> g_clCustom3(MODULENAME, "CustomColor3", -1);
-CMOption<uint32_t> g_clCustom4(MODULENAME, "CustomColor4", -1);
-
-MWindowList g_hNewstoryWindows = 0, g_hNewstoryLogs = 0;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-PLUGININFOEX pluginInfoEx =
-{
- sizeof(PLUGININFOEX),
- __PLUGIN_NAME,
- PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
- __DESCRIPTION,
- __AUTHOR,
- __COPYRIGHT,
- __AUTHORWEB,
- UNICODE_AWARE,
- // {1AD11C86-CAAB-4372-A0A4-8B1168D51B9E}
- { 0x1ad11c86, 0xcaab, 0x4372, { 0xa0, 0xa4, 0x8b, 0x11, 0x68, 0xd5, 0x1b, 0x9e } }
-};
-
-CMPlugin::CMPlugin() :
- PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx),
- bOptVScroll(MODULENAME, "VScroll", true),
- bSortAscending(MODULENAME, "SortAscending", true)
-{}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_UIHISTORY, MIID_LAST };
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static IconItem icons[] =
-{
- { LPGEN("Main icon"), "main", IDI_NEWSTORY },
- { LPGEN("User info"), "userinfo", IDI_USERINFO },
- { LPGEN("User menu"), "usermenu", IDI_USERMENU },
- { LPGEN("Search"), "search", IDI_SEARCH },
- { LPGEN("Options"), "options", IDI_OPTIONS },
- { LPGEN("Filter"), "filter", IDI_FILTER },
- { LPGEN("Export"), "export", IDI_EXPORT },
- { LPGEN("Copy"), "copy", IDI_COPY },
- { LPGEN("Reply"), "reply", IDI_REPLY },
- { LPGEN("Bookmark"), "bookmark", IDI_BOOKMARK },
- { LPGEN("Send message"), "message", IDI_SENDMSG },
-
- { LPGEN("Incoming message"), "msgin", IDI_MSGIN },
- { LPGEN("Outgoing message"), "msgout", IDI_MSGOUT },
- { LPGEN("User signed in"), "signin", IDI_SIGNIN },
- { LPGEN("Unknown event"), "unknown", IDI_UNKNOWN },
-
- { LPGEN("Find previous"), "findprev", IDI_FINDPREV },
- { LPGEN("Find next"), "findnext", IDI_FINDNEXT },
- { LPGEN("Jump to date"), "calendar", IDI_CALENDAR },
- { LPGEN("Conversations"), "timetree", IDI_TIMETREE },
- { LPGEN("All results"), "users", IDI_USERS },
-
- { LPGEN("Template group"), "tplgroup", IDI_TPLGROUP },
- { LPGEN("Cancel edit"), "reset", IDI_RESET },
- { LPGEN("Downloaded"), "downloaded", IDI_OK },
- { LPGEN("Help"), "varhelp", IDI_VARHELP }
-};
-
-static int SmartSendEvent(int iEvent, MCONTACT hContact, LPARAM lParam)
-{
- if (HWND hwnd = WindowList_Find(g_hNewstoryLogs, hContact))
- SendMessage(hwnd, iEvent, hContact, lParam);
-
- if (db_mc_isMeta(hContact)) {
- // Send a message to a real contact too
- MCONTACT cc = db_event_getContact(lParam);
- if (cc != hContact)
- if (HWND hwnd = WindowList_Find(g_hNewstoryLogs, cc))
- SendMessage(hwnd, iEvent, cc, lParam);
- }
-
- return 0;
-}
-
-static int evtEventAdded(WPARAM hContact, LPARAM lParam)
-{
- return SmartSendEvent(UM_ADDEVENT, hContact, lParam);
-}
-
-static int evtEventDeleted(WPARAM hContact, LPARAM lParam)
-{
- return (g_plugin.bDisableDelete) ? 0 : SmartSendEvent(UM_REMOVEEVENT, hContact, lParam);
-}
-
-static int evtEventEdited(WPARAM hContact, LPARAM lParam)
-{
- return SmartSendEvent(UM_EDITEVENT, hContact, lParam);
-}
-
-static int evtTopToolbar(WPARAM, LPARAM)
-{
- // TopToolbar button
- TTBButton ttb = {};
- ttb.dwFlags = TTBBF_VISIBLE;
- ttb.pszService = "NewStory/GlobalSearch";
- ttb.name = LPGEN("Global search");
- ttb.hIconHandleUp = g_plugin.getIconHandle(IDI_NEWSTORY);
- g_plugin.addTTB(&ttb);
- return 0;
-}
-
-static int evtModulesLoaded(WPARAM, LPARAM)
-{
- HookEvent(ME_TTB_MODULELOADED, evtTopToolbar);
-
- InitFonts();
- InitNewstoryControl();
- LoadTemplates();
- return 0;
-}
-
-static int evtPreShutdown(WPARAM, LPARAM)
-{
- WindowList_Broadcast(g_hNewstoryWindows, WM_CLOSE, 0, 0);
- return 0;
-}
-
-void CMPlugin::LoadOptions()
-{
- bShowType = g_bShowType;
- bDrawEdge = g_bOptDrawEdge;
- bHppCompat = g_bOptHppCompat;
- bMsgGrouping = g_bOptGrouping;
- bShowDirecction = g_bShowDirection;
-
- clCustom[0] = g_clCustom0;
- clCustom[1] = g_clCustom1;
- clCustom[2] = g_clCustom2;
- clCustom[3] = g_clCustom3;
- clCustom[4] = g_clCustom4;
-}
-
-int CMPlugin::Load()
-{
- registerIcon(MODULETITLE, icons);
-
- LoadOptions();
-
- m_log = RegisterSrmmLog(this, MODULETITLE, _T(MODULENAME), NewStory_Stub);
-
- g_hNewstoryLogs = WindowList_Create();
- g_hNewstoryWindows = WindowList_Create();
-
- HookEvent(ME_DB_EVENT_ADDED, evtEventAdded);
- HookEvent(ME_DB_EVENT_DELETED, evtEventDeleted);
- HookEvent(ME_DB_EVENT_EDITED, evtEventEdited);
- HookEvent(ME_OPT_INITIALISE, OptionsInitialize);
- HookEvent(ME_SYSTEM_MODULESLOADED, evtModulesLoaded);
- HookEvent(ME_SYSTEM_PRESHUTDOWN, evtPreShutdown);
-
- InitMenus();
- InitHotkeys();
- InitServices();
- return 0;
-}
-
-int CMPlugin::Unload()
-{
- WindowList_Destroy(g_hNewstoryLogs);
- WindowList_Destroy(g_hNewstoryWindows);
-
- UnregisterSrmmLog(m_log);
- UnregisterClass(_T(NEWSTORYLIST_CLASS), g_plugin.getInst());
- DestroyFonts();
- return 0;
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+CMPlugin g_plugin;
+
+CMOption<bool> g_bOptGrouping(MODULENAME, "MessageGrouping", false);
+CMOption<bool> g_bOptDrawEdge(MODULENAME, "DrawEdge", true);
+CMOption<bool> g_bOptHppCompat(MODULENAME, "HppCompat", false);
+CMOption<bool> g_bShowType(MODULENAME, "ShowType", true);
+CMOption<bool> g_bShowDirection(MODULENAME, "ShowDirection", true);
+
+CMOption<uint32_t> g_clCustom0(MODULENAME, "CustomColor0", -1);
+CMOption<uint32_t> g_clCustom1(MODULENAME, "CustomColor1", -1);
+CMOption<uint32_t> g_clCustom2(MODULENAME, "CustomColor2", -1);
+CMOption<uint32_t> g_clCustom3(MODULENAME, "CustomColor3", -1);
+CMOption<uint32_t> g_clCustom4(MODULENAME, "CustomColor4", -1);
+
+MWindowList g_hNewstoryWindows = 0, g_hNewstoryLogs = 0;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx =
+{
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {1AD11C86-CAAB-4372-A0A4-8B1168D51B9E}
+ { 0x1ad11c86, 0xcaab, 0x4372, { 0xa0, 0xa4, 0x8b, 0x11, 0x68, 0xd5, 0x1b, 0x9e } }
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx),
+ bOptVScroll(MODULENAME, "VScroll", true),
+ bSortAscending(MODULENAME, "SortAscending", true)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_UIHISTORY, MIID_LAST };
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static IconItem icons[] =
+{
+ { LPGEN("Main icon"), "main", IDI_NEWSTORY },
+ { LPGEN("User info"), "userinfo", IDI_USERINFO },
+ { LPGEN("User menu"), "usermenu", IDI_USERMENU },
+ { LPGEN("Search"), "search", IDI_SEARCH },
+ { LPGEN("Options"), "options", IDI_OPTIONS },
+ { LPGEN("Filter"), "filter", IDI_FILTER },
+ { LPGEN("Export"), "export", IDI_EXPORT },
+ { LPGEN("Copy"), "copy", IDI_COPY },
+ { LPGEN("Reply"), "reply", IDI_REPLY },
+ { LPGEN("Bookmark"), "bookmark", IDI_BOOKMARK },
+ { LPGEN("Send message"), "message", IDI_SENDMSG },
+
+ { LPGEN("Incoming message"), "msgin", IDI_MSGIN },
+ { LPGEN("Outgoing message"), "msgout", IDI_MSGOUT },
+ { LPGEN("User signed in"), "signin", IDI_SIGNIN },
+ { LPGEN("Unknown event"), "unknown", IDI_UNKNOWN },
+
+ { LPGEN("Find previous"), "findprev", IDI_FINDPREV },
+ { LPGEN("Find next"), "findnext", IDI_FINDNEXT },
+ { LPGEN("Jump to date"), "calendar", IDI_CALENDAR },
+ { LPGEN("Conversations"), "timetree", IDI_TIMETREE },
+ { LPGEN("All results"), "users", IDI_USERS },
+
+ { LPGEN("Template group"), "tplgroup", IDI_TPLGROUP },
+ { LPGEN("Cancel edit"), "reset", IDI_RESET },
+ { LPGEN("Downloaded"), "downloaded", IDI_OK },
+ { LPGEN("Help"), "varhelp", IDI_VARHELP }
+};
+
+static int SmartSendEvent(int iEvent, MCONTACT hContact, LPARAM lParam)
+{
+ if (HWND hwnd = WindowList_Find(g_hNewstoryLogs, hContact))
+ SendMessage(hwnd, iEvent, hContact, lParam);
+
+ if (db_mc_isMeta(hContact)) {
+ // Send a message to a real contact too
+ MCONTACT cc = db_event_getContact(lParam);
+ if (cc != hContact)
+ if (HWND hwnd = WindowList_Find(g_hNewstoryLogs, cc))
+ SendMessage(hwnd, iEvent, cc, lParam);
+ }
+
+ return 0;
+}
+
+static int evtEventAdded(WPARAM hContact, LPARAM lParam)
+{
+ return SmartSendEvent(UM_ADDEVENT, hContact, lParam);
+}
+
+static int evtEventDeleted(WPARAM hContact, LPARAM lParam)
+{
+ return (g_plugin.bDisableDelete) ? 0 : SmartSendEvent(UM_REMOVEEVENT, hContact, lParam);
+}
+
+static int evtEventEdited(WPARAM hContact, LPARAM lParam)
+{
+ return SmartSendEvent(UM_EDITEVENT, hContact, lParam);
+}
+
+static int evtTopToolbar(WPARAM, LPARAM)
+{
+ // TopToolbar button
+ TTBButton ttb = {};
+ ttb.dwFlags = TTBBF_VISIBLE;
+ ttb.pszService = "NewStory/GlobalSearch";
+ ttb.name = LPGEN("Global search");
+ ttb.hIconHandleUp = g_plugin.getIconHandle(IDI_NEWSTORY);
+ g_plugin.addTTB(&ttb);
+ return 0;
+}
+
+static int evtModulesLoaded(WPARAM, LPARAM)
+{
+ HookEvent(ME_TTB_MODULELOADED, evtTopToolbar);
+
+ InitFonts();
+ InitNewstoryControl();
+ LoadTemplates();
+ return 0;
+}
+
+static int evtPreShutdown(WPARAM, LPARAM)
+{
+ WindowList_Broadcast(g_hNewstoryWindows, WM_CLOSE, 0, 0);
+ return 0;
+}
+
+void CMPlugin::LoadOptions()
+{
+ bShowType = g_bShowType;
+ bDrawEdge = g_bOptDrawEdge;
+ bHppCompat = g_bOptHppCompat;
+ bMsgGrouping = g_bOptGrouping;
+ bShowDirecction = g_bShowDirection;
+
+ clCustom[0] = g_clCustom0;
+ clCustom[1] = g_clCustom1;
+ clCustom[2] = g_clCustom2;
+ clCustom[3] = g_clCustom3;
+ clCustom[4] = g_clCustom4;
+}
+
+int CMPlugin::Load()
+{
+ registerIcon(MODULETITLE, icons);
+
+ LoadOptions();
+
+ m_log = RegisterSrmmLog(this, MODULETITLE, _T(MODULENAME), NewStory_Stub);
+
+ g_hNewstoryLogs = WindowList_Create();
+ g_hNewstoryWindows = WindowList_Create();
+
+ HookEvent(ME_DB_EVENT_ADDED, evtEventAdded);
+ HookEvent(ME_DB_EVENT_DELETED, evtEventDeleted);
+ HookEvent(ME_DB_EVENT_EDITED, evtEventEdited);
+ HookEvent(ME_OPT_INITIALISE, OptionsInitialize);
+ HookEvent(ME_SYSTEM_MODULESLOADED, evtModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, evtPreShutdown);
+
+ InitMenus();
+ InitHotkeys();
+ InitServices();
+ return 0;
+}
+
+int CMPlugin::Unload()
+{
+ WindowList_Destroy(g_hNewstoryLogs);
+ WindowList_Destroy(g_hNewstoryWindows);
+
+ UnregisterSrmmLog(m_log);
+ UnregisterClass(_T(NEWSTORYLIST_CLASS), g_plugin.getInst());
+ DestroyFonts();
+ return 0;
+}
diff --git a/plugins/NewStory/src/options.cpp b/plugins/NewStory/src/options.cpp
index 1fa938c0c7..69ac03921d 100644
--- a/plugins/NewStory/src/options.cpp
+++ b/plugins/NewStory/src/options.cpp
@@ -1,333 +1,336 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Basic options page class
-
-class CBaseOptsDlg : public CDlgBase
-{
-protected:
- CBaseOptsDlg(int iDlgId) :
- CDlgBase(g_plugin, iDlgId)
- {
- m_OnFinishWizard = Callback(this, &CBaseOptsDlg::OnFinish);
- }
-
- void OnFinish(void *)
- {
- WindowList_BroadcastAsync(g_hNewstoryLogs, NSM_SET_OPTIONS, 0, 0);
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// General options dialog
-
-class CGeneralOptsDlg : public CBaseOptsDlg
-{
- CCtrlCheck chkGrouping, chkVScroll, chkDrawEdge, chkSortOrder, chkHppCompat;
- CCtrlCheck chkShowType, chkShowDirection;
-
-public:
- CGeneralOptsDlg() :
- CBaseOptsDlg(IDD_OPT_ADVANCED),
- chkVScroll(this, IDC_VSCROLL),
- chkShowType(this, IDC_SHOW_TYPE),
- chkDrawEdge(this, IDC_DRAWEDGE),
- chkGrouping(this, IDC_GROUPING),
- chkHppCompat(this, IDC_HPP_COMPAT),
- chkSortOrder(this, IDC_SORT_ASCENDING),
- chkShowDirection(this, IDC_SHOW_DIRECTION)
- {
- CreateLink(chkVScroll, g_plugin.bOptVScroll);
- CreateLink(chkShowType, g_bShowType);
- CreateLink(chkGrouping, g_bOptGrouping);
- CreateLink(chkDrawEdge, g_bOptDrawEdge);
- CreateLink(chkHppCompat, g_bOptHppCompat);
- CreateLink(chkShowDirection, g_bShowDirection);
-
- CreateLink(chkSortOrder, g_plugin.bSortAscending);
- }
-
- bool OnApply() override
- {
- g_plugin.LoadOptions();
- return true;
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Template options dialog
-
-class CTemplateOptsDlg : public CBaseOptsDlg
-{
- TemplateInfo *m_curr = 0;
- ItemData m_tempItem;
-
- CCtrlBase preview, gpreview;
- CCtrlEdit m_edit;
- CCtrlColor clr0, clr1, clr2, clr3, clr4;
- CCtrlMButton btnDiscard, bthVarHelp, btnReset;
- CCtrlTreeView m_tree;
-
- UI_MESSAGE_MAP(CTemplateOptsDlg, CBaseOptsDlg);
- UI_MESSAGE(UM_REDRAWLISTH, OnColorChanged);
- UI_MESSAGE_MAP_END();
-
- LRESULT OnColorChanged(UINT, WPARAM, LPARAM)
- {
- gpreview.SendMsg(MTM_SETBKCOLOR, g_colorTable[COLOR_BACK].cl, 0);
- return 0;
- }
-
-public:
- CTemplateOptsDlg() :
- CBaseOptsDlg(IDD_OPT_TEMPLATES),
- clr0(this, IDC_COLOR1),
- clr1(this, IDC_COLOR2),
- clr2(this, IDC_COLOR3),
- clr3(this, IDC_COLOR4),
- clr4(this, IDC_COLOR5),
- m_edit(this, IDC_EDITTEMPLATE),
- m_tree(this, IDC_TEMPLATES),
- preview(this, IDC_PREVIEW),
- gpreview(this, IDC_GPREVIEW),
- btnReset(this, IDC_RESET, Skin_LoadIcon(SKINICON_OTHER_UNDO), LPGEN("Reset to default")),
- btnDiscard(this, IDC_DISCARD, g_plugin.getIcon(IDI_RESET), LPGEN("Cancel edit")),
- bthVarHelp(this, IDC_VARHELP, g_plugin.getIcon(IDI_VARHELP), LPGEN("Variables help"))
- {
- CreateLink(clr0, g_clCustom0);
- CreateLink(clr1, g_clCustom1);
- CreateLink(clr2, g_clCustom2);
- CreateLink(clr3, g_clCustom3);
- CreateLink(clr4, g_clCustom4);
-
- btnReset.OnClick = Callback(this, &CTemplateOptsDlg::onClick_Reset);
- btnDiscard.OnClick = Callback(this, &CTemplateOptsDlg::onClick_Discard);
- bthVarHelp.OnClick = Callback(this, &CTemplateOptsDlg::onClick_Help);
-
- m_edit.OnChange = Callback(this, &CTemplateOptsDlg::onChange_Edit);
-
- m_tree.OnSelChanged = Callback(this, &CTemplateOptsDlg::onSelChanged);
- }
-
- bool OnInitDialog() override
- {
- HIMAGELIST himgTree = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 1, 1);
- m_tree.SetImageList(himgTree, TVSIL_NORMAL);
-
- ImageList_AddIcon(himgTree, g_plugin.getIcon(IDI_TPLGROUP));
-
- m_tempItem.wszNick = TranslateT("Test contact");
- m_tempItem.wtext = mir_wstrdup(TranslateT("The quick brown fox jumps over the lazy dog."));
- m_tempItem.dbe.flags = DBEF_TEMPORARY | DBEF_BOOKMARK;
- m_tempItem.dbe.szModule = MODULENAME;
- m_tempItem.dbe.eventType = EVENTTYPE_MESSAGE;
- m_tempItem.dbe.timestamp = time(0);
-
- HTREEITEM hGroup = 0, hFirst = 0;
- const wchar_t *pwszPrevGroup = nullptr;
- for (auto &it : templates) {
- if (mir_wstrcmp(it.group, pwszPrevGroup)) {
- if (hGroup)
- m_tree.Expand(hGroup, TVE_EXPAND);
-
- TVINSERTSTRUCT tvis = {};
- tvis.hInsertAfter = TVI_LAST;
- tvis.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
- tvis.item.state = tvis.item.stateMask = TVIS_BOLD;
- tvis.item.pszText = TranslateW(it.group);
- tvis.item.lParam = 0;
- hGroup = m_tree.InsertItem(&tvis);
-
- if (!hFirst)
- hFirst = hGroup;
- }
-
- TVINSERTSTRUCT tvis;
- tvis.hParent = hGroup;
- tvis.hInsertAfter = TVI_LAST;
- tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
- tvis.item.pszText = TranslateW(it.title);
- tvis.item.iSelectedImage = tvis.item.iImage = ImageList_AddIcon(himgTree, it.getIcon());
- tvis.item.lParam = (LPARAM)&it;
- m_tree.InsertItem(&tvis);
-
- pwszPrevGroup = it.group;
- }
-
- if (hGroup)
- m_tree.Expand(hGroup, TVE_EXPAND);
-
- m_tree.SelectItem(hFirst);
- m_tree.EnsureVisible(hFirst);
-
- WindowList_Add(g_hNewstoryWindows, m_hwnd);
- OnColorChanged(0, 0, 0);
- return true;
- }
-
- bool OnApply() override
- {
- if (m_curr != nullptr)
- replaceStrW(m_curr->tmpValue, m_edit.GetText());
-
- for (auto &it : templates) {
- if (it.tmpValue) {
- replaceStrW(it.value, it.tmpValue);
- it.tmpValue = nullptr;
- }
- }
-
- g_plugin.LoadOptions();
- onChange_Edit();
- SaveTemplates();
- return true;
- }
-
- void OnDestroy() override
- {
- WindowList_Remove(g_hNewstoryWindows, m_hwnd);
-
- for (auto &it : templates)
- replaceStrW(it.tmpValue, nullptr);
- }
-
- void onClick_Reset(CCtrlButton *)
- {
- if (m_curr) {
- replaceStrW(m_curr->tmpValue, nullptr);
- replaceStrW(m_curr->value, nullptr);
- m_edit.SetText(TranslateW(m_curr->defvalue));
- }
-
- onChange_Edit();
- NotifyChange();
- }
-
- void onClick_Discard(CCtrlButton *)
- {
- replaceStrW(m_curr->tmpValue, 0);
-
- if (m_curr->value)
- m_edit.SetText(m_curr->value);
- else
- m_edit.SetText(TranslateW(m_curr->defvalue));
-
- onChange_Edit();
- }
-
- void onClick_Help(CCtrlButton *)
- {
- CMStringW wszVarHelp;
- wszVarHelp.Format(L"%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s",
- L"%%", TranslateT("simply % character"),
- L"%n", TranslateT("a \"hard\" line break (cr/lf - will break indent)"),
- L"%S", TranslateT("my nickname"),
- L"%N", TranslateT("buddy\'s nickname"),
- L"%c", TranslateT("event count"),
- L"%D", TranslateT("direction symbol"),
- L"%t", TranslateT("timestamp"),
- L"%h", TranslateT("hour (24 hour format, 0-23)"),
- L"%a", TranslateT("hour (12 hour format)"),
- L"%m", TranslateT("minute"),
- L"%s", TranslateT("second"),
- L"%o", TranslateT("month"),
- L"%d", TranslateT("day of month"),
- L"%y", TranslateT("year (4 digits)"),
- L"%w", TranslateT("day of week (Sunday, Monday... translatable)"),
- L"%p", TranslateT("AM/PM symbol"),
- L"%O", TranslateT("name of month, translatable"),
- L"%M", TranslateT("the message string itself"));
- MessageBox(m_hwnd, wszVarHelp, TranslateT("Variables help"), MB_OK);
- }
-
- void onChange_Edit(CCtrlEdit* = 0)
- {
- if (m_curr == nullptr)
- return;
-
- replaceStrW(m_curr->tmpValue, m_edit.GetText());
-
- m_tempItem.fill(int(m_curr - templates)); // copy data from template to event
-
- CMStringW wszText(m_tempItem.formatStringEx(m_curr->tmpValue));
- preview.SetText(wszText);
- gpreview.SendMsg(MTM_UPDATEEX, MTEXT_FLG_RTF, LPARAM(m_tempItem.formatRtf(wszText).c_str()));
- }
-
- void onSelChanged(CCtrlTreeView::TEventInfo *)
- {
- TVITEMEX tvi;
- tvi.hItem = m_tree.GetSelection();
- tvi.mask = TVIF_PARAM;
- m_tree.GetItem(&tvi);
-
- if (tvi.lParam == 0) {
- m_edit.Disable();
- preview.Disable();
- gpreview.Disable();
- btnDiscard.Disable();
- bthVarHelp.Disable();
-
- HTREEITEM hItem = m_tree.GetChild(tvi.hItem);
- if (hItem)
- m_tree.Expand(tvi.hItem, TVE_EXPAND);
- return;
- }
-
- m_edit.Enable();
- preview.Enable();
- gpreview.Enable();
- btnDiscard.Enable();
- bthVarHelp.Enable();
-
- if (m_curr != nullptr)
- replaceStrW(m_curr->tmpValue, m_edit.GetText());
-
- m_curr = (TemplateInfo *)tvi.lParam;
-
- if (m_curr->tmpValue)
- m_edit.SetText(m_curr->tmpValue);
- else if (m_curr->value)
- m_edit.SetText(m_curr->value);
- else
- m_edit.SetText(TranslateW(m_curr->defvalue));
-
- onChange_Edit();
- }
-};
-
-int OptionsInitialize(WPARAM wParam, LPARAM)
-{
- OPTIONSDIALOGPAGE odp = {};
- odp.position = 100000000;
- odp.szGroup.a = LPGEN("History");
- odp.szTitle.a = LPGEN("NewStory");
- odp.flags = ODPF_BOLDGROUPS;
-
- odp.szTab.a = LPGEN("Templates");
- odp.pDialog = new CTemplateOptsDlg();
- g_plugin.addOptions(wParam, &odp);
-
- odp.szTab.a = LPGEN("Advanced");
- odp.pDialog = new CGeneralOptsDlg();
- g_plugin.addOptions(wParam, &odp);
- return 0;
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Basic options page class
+
+class CBaseOptsDlg : public CDlgBase
+{
+protected:
+ CBaseOptsDlg(int iDlgId) :
+ CDlgBase(g_plugin, iDlgId)
+ {
+ m_OnFinishWizard = Callback(this, &CBaseOptsDlg::OnFinish);
+ }
+
+ void OnFinish(void *)
+ {
+ WindowList_BroadcastAsync(g_hNewstoryLogs, NSM_SET_OPTIONS, 0, 0);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// General options dialog
+
+class CGeneralOptsDlg : public CBaseOptsDlg
+{
+ CCtrlCheck chkGrouping, chkVScroll, chkDrawEdge, chkSortOrder, chkHppCompat;
+ CCtrlCheck chkShowType, chkShowDirection;
+
+public:
+ CGeneralOptsDlg() :
+ CBaseOptsDlg(IDD_OPT_ADVANCED),
+ chkVScroll(this, IDC_VSCROLL),
+ chkShowType(this, IDC_SHOW_TYPE),
+ chkDrawEdge(this, IDC_DRAWEDGE),
+ chkGrouping(this, IDC_GROUPING),
+ chkHppCompat(this, IDC_HPP_COMPAT),
+ chkSortOrder(this, IDC_SORT_ASCENDING),
+ chkShowDirection(this, IDC_SHOW_DIRECTION)
+ {
+ CreateLink(chkVScroll, g_plugin.bOptVScroll);
+ CreateLink(chkShowType, g_bShowType);
+ CreateLink(chkGrouping, g_bOptGrouping);
+ CreateLink(chkDrawEdge, g_bOptDrawEdge);
+ CreateLink(chkHppCompat, g_bOptHppCompat);
+ CreateLink(chkShowDirection, g_bShowDirection);
+
+ CreateLink(chkSortOrder, g_plugin.bSortAscending);
+ }
+
+ bool OnApply() override
+ {
+ g_plugin.LoadOptions();
+ return true;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Template options dialog
+
+class CTemplateOptsDlg : public CBaseOptsDlg
+{
+ TemplateInfo *m_curr = 0;
+ ItemData m_tempItem;
+
+ CCtrlBase preview, gpreview;
+ CCtrlEdit m_edit;
+ CCtrlColor clr0, clr1, clr2, clr3, clr4;
+ CCtrlMButton btnDiscard, bthVarHelp, btnReset;
+ CCtrlTreeView m_tree;
+
+ UI_MESSAGE_MAP(CTemplateOptsDlg, CBaseOptsDlg);
+ UI_MESSAGE(UM_REDRAWLISTH, OnColorChanged);
+ UI_MESSAGE_MAP_END();
+
+ LRESULT OnColorChanged(UINT, WPARAM, LPARAM)
+ {
+ gpreview.SendMsg(MTM_SETBKCOLOR, g_colorTable[COLOR_BACK].cl, 0);
+ return 0;
+ }
+
+public:
+ CTemplateOptsDlg() :
+ CBaseOptsDlg(IDD_OPT_TEMPLATES),
+ clr0(this, IDC_COLOR1),
+ clr1(this, IDC_COLOR2),
+ clr2(this, IDC_COLOR3),
+ clr3(this, IDC_COLOR4),
+ clr4(this, IDC_COLOR5),
+ m_edit(this, IDC_EDITTEMPLATE),
+ m_tree(this, IDC_TEMPLATES),
+ preview(this, IDC_PREVIEW),
+ gpreview(this, IDC_GPREVIEW),
+ btnReset(this, IDC_RESET, Skin_LoadIcon(SKINICON_OTHER_UNDO), LPGEN("Reset to default")),
+ btnDiscard(this, IDC_DISCARD, g_plugin.getIcon(IDI_RESET), LPGEN("Cancel edit")),
+ bthVarHelp(this, IDC_VARHELP, g_plugin.getIcon(IDI_VARHELP), LPGEN("Variables help"))
+ {
+ CreateLink(clr0, g_clCustom0);
+ CreateLink(clr1, g_clCustom1);
+ CreateLink(clr2, g_clCustom2);
+ CreateLink(clr3, g_clCustom3);
+ CreateLink(clr4, g_clCustom4);
+
+ btnReset.OnClick = Callback(this, &CTemplateOptsDlg::onClick_Reset);
+ btnDiscard.OnClick = Callback(this, &CTemplateOptsDlg::onClick_Discard);
+ bthVarHelp.OnClick = Callback(this, &CTemplateOptsDlg::onClick_Help);
+
+ m_edit.OnChange = Callback(this, &CTemplateOptsDlg::onChange_Edit);
+
+ m_tree.OnSelChanged = Callback(this, &CTemplateOptsDlg::onSelChanged);
+ }
+
+ bool OnInitDialog() override
+ {
+ HIMAGELIST himgTree = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 1, 1);
+ m_tree.SetImageList(himgTree, TVSIL_NORMAL);
+
+ ImageList_AddIcon(himgTree, g_plugin.getIcon(IDI_TPLGROUP));
+
+ m_tempItem.wszNick = TranslateT("Test contact");
+ m_tempItem.wtext = mir_wstrdup(TranslateT("The quick brown fox jumps over the lazy dog."));
+ m_tempItem.dbe.flags = DBEF_TEMPORARY | DBEF_BOOKMARK;
+ m_tempItem.dbe.szModule = MODULENAME;
+ m_tempItem.dbe.eventType = EVENTTYPE_MESSAGE;
+ m_tempItem.dbe.timestamp = time(0);
+
+ HTREEITEM hGroup = 0, hFirst = 0;
+ const wchar_t *pwszPrevGroup = nullptr;
+ for (auto &it : templates) {
+ if (mir_wstrcmp(it.group, pwszPrevGroup)) {
+ if (hGroup)
+ m_tree.Expand(hGroup, TVE_EXPAND);
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.item.state = tvis.item.stateMask = TVIS_BOLD;
+ tvis.item.pszText = TranslateW(it.group);
+ tvis.item.lParam = 0;
+ hGroup = m_tree.InsertItem(&tvis);
+
+ if (!hFirst)
+ hFirst = hGroup;
+ }
+
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = hGroup;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvis.item.pszText = TranslateW(it.title);
+ tvis.item.iSelectedImage = tvis.item.iImage = ImageList_AddIcon(himgTree, it.getIcon());
+ tvis.item.lParam = (LPARAM)&it;
+ m_tree.InsertItem(&tvis);
+
+ pwszPrevGroup = it.group;
+ }
+
+ if (hGroup)
+ m_tree.Expand(hGroup, TVE_EXPAND);
+
+ m_tree.SelectItem(hFirst);
+ m_tree.EnsureVisible(hFirst);
+
+ WindowList_Add(g_hNewstoryWindows, m_hwnd);
+ OnColorChanged(0, 0, 0);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ if (m_curr != nullptr)
+ replaceStrW(m_curr->tmpValue, m_edit.GetText());
+
+ for (auto &it : templates) {
+ if (it.tmpValue) {
+ replaceStrW(it.value, it.tmpValue);
+ it.tmpValue = nullptr;
+ }
+ }
+
+ g_plugin.LoadOptions();
+ onChange_Edit();
+ SaveTemplates();
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ WindowList_Remove(g_hNewstoryWindows, m_hwnd);
+
+ for (auto &it : templates)
+ replaceStrW(it.tmpValue, nullptr);
+ }
+
+ void onClick_Reset(CCtrlButton *)
+ {
+ if (m_curr) {
+ replaceStrW(m_curr->tmpValue, nullptr);
+ replaceStrW(m_curr->value, nullptr);
+ m_edit.SetText(TranslateW(m_curr->defvalue));
+ }
+
+ onChange_Edit();
+ NotifyChange();
+ }
+
+ void onClick_Discard(CCtrlButton *)
+ {
+ replaceStrW(m_curr->tmpValue, 0);
+
+ if (m_curr->value)
+ m_edit.SetText(m_curr->value);
+ else
+ m_edit.SetText(TranslateW(m_curr->defvalue));
+
+ onChange_Edit();
+ }
+
+ void onClick_Help(CCtrlButton *)
+ {
+ CMStringW wszVarHelp;
+ wszVarHelp.Format(L"%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s",
+ L"%%", TranslateT("simply % character"),
+ L"%n", TranslateT("a \"hard\" line break (cr/lf - will break indent)"),
+ L"%S", TranslateT("my nickname"),
+ L"%N", TranslateT("buddy\'s nickname"),
+ L"%c", TranslateT("event count"),
+ L"%D", TranslateT("direction symbol"),
+ L"%t", TranslateT("timestamp"),
+ L"%h", TranslateT("hour (24 hour format, 0-23)"),
+ L"%a", TranslateT("hour (12 hour format)"),
+ L"%m", TranslateT("minute"),
+ L"%s", TranslateT("second"),
+ L"%o", TranslateT("month"),
+ L"%d", TranslateT("day of month"),
+ L"%y", TranslateT("year (4 digits)"),
+ L"%w", TranslateT("day of week (Sunday, Monday... translatable)"),
+ L"%p", TranslateT("AM/PM symbol"),
+ L"%O", TranslateT("name of month, translatable"),
+ L"%M", TranslateT("the message string itself"),
+ L"[c0]", TranslateT("default text color"),
+ L"[c1]", TranslateT("nickname's color"),
+ L"[c2]-[c6]", TranslateT("one of the user defined custom color from the options page for the following text output (X is a number from 2 to 6, referring to the color index)"));
+ MessageBox(m_hwnd, wszVarHelp, TranslateT("Variables help"), MB_OK);
+ }
+
+ void onChange_Edit(CCtrlEdit* = 0)
+ {
+ if (m_curr == nullptr)
+ return;
+
+ replaceStrW(m_curr->tmpValue, m_edit.GetText());
+
+ m_tempItem.fill(int(m_curr - templates)); // copy data from template to event
+
+ CMStringW wszText(m_tempItem.formatStringEx(m_curr->tmpValue));
+ preview.SetText(wszText);
+ gpreview.SendMsg(MTM_UPDATEEX, MTEXT_FLG_RTF, LPARAM(m_tempItem.formatRtf(wszText).c_str()));
+ }
+
+ void onSelChanged(CCtrlTreeView::TEventInfo *)
+ {
+ TVITEMEX tvi;
+ tvi.hItem = m_tree.GetSelection();
+ tvi.mask = TVIF_PARAM;
+ m_tree.GetItem(&tvi);
+
+ if (tvi.lParam == 0) {
+ m_edit.Disable();
+ preview.Disable();
+ gpreview.Disable();
+ btnDiscard.Disable();
+ bthVarHelp.Disable();
+
+ HTREEITEM hItem = m_tree.GetChild(tvi.hItem);
+ if (hItem)
+ m_tree.Expand(tvi.hItem, TVE_EXPAND);
+ return;
+ }
+
+ m_edit.Enable();
+ preview.Enable();
+ gpreview.Enable();
+ btnDiscard.Enable();
+ bthVarHelp.Enable();
+
+ if (m_curr != nullptr)
+ replaceStrW(m_curr->tmpValue, m_edit.GetText());
+
+ m_curr = (TemplateInfo *)tvi.lParam;
+
+ if (m_curr->tmpValue)
+ m_edit.SetText(m_curr->tmpValue);
+ else if (m_curr->value)
+ m_edit.SetText(m_curr->value);
+ else
+ m_edit.SetText(TranslateW(m_curr->defvalue));
+
+ onChange_Edit();
+ }
+};
+
+int OptionsInitialize(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 100000000;
+ odp.szGroup.a = LPGEN("History");
+ odp.szTitle.a = LPGEN("NewStory");
+ odp.flags = ODPF_BOLDGROUPS;
+
+ odp.szTab.a = LPGEN("Templates");
+ odp.pDialog = new CTemplateOptsDlg();
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.a = LPGEN("Advanced");
+ odp.pDialog = new CGeneralOptsDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/plugins/NewStory/src/resource.h b/plugins/NewStory/src/resource.h
index f166ba6d58..c8de56f5fa 100644
--- a/plugins/NewStory/src/resource.h
+++ b/plugins/NewStory/src/resource.h
@@ -1,131 +1,126 @@
-//{{NO_DEPENDENCIES}}
-// Microsoft Visual C++ generated include file.
-// Used by W:\miranda-ng\plugins\NewStory\res\resource.rc
-//
-#define IDD_HISTORY 101
-#define IDD_OPT_TEMPLATES 102
-#define IDD_OPT_ADVANCED 103
-#define IDD_CALENDARTOOL 104
-#define IDR_POPUPS 105
-#define IDI_BOOKMARK 106
-#define IDI_NEWSTORY 107
-#define IDI_USERINFO 108
-#define IDI_USERMENU 109
-#define IDI_SEARCH 110
-#define IDI_OPTIONS 111
-#define IDI_FILTER 112
-#define IDI_EXPORT 113
-#define IDI_COPY 114
-#define IDI_SENDMSG 115
-#define IDI_MSGIN 116
-#define IDI_MSGOUT 117
-#define IDI_SIGNIN 118
-#define IDI_OK 119
-#define IDI_UNKNOWN 121
-#define IDI_FINDPREV 122
-#define IDI_FINDNEXT 123
-#define IDI_CALENDAR 124
-#define IDI_TPLGROUP 125
-#define IDI_RESET 126
-#define IDI_USERS 127
-#define IDI_VARHELP 128
-#define IDI_TIMETREE 129
-#define IDI_REPLY 130
-#define IDD_EMPTYHISTORY 131
-#define IDC_USERINFO 1000
-#define IDC_USERMENU 1001
-#define IDC_MESSAGE 1002
-#define IDC_SEARCH 1003
-#define IDC_COPY 1004
-#define IDC_EXPORT 1005
-#define IDC_DELETE 1006
-#define IDC_TIMETREE 1007
-#define IDC_FILTER 1008
-#define IDC_FINDPREV 1009
-#define IDC_SPLITTERY 1010
-#define IDC_SEARCHTEXT 1011
-#define IDC_SEARCHICON 1012
-#define IDC_LOGOPTIONS 1013
-#define IDC_TIMETREEVIEW 1014
-#define IDC_DATEPOPUP 1015
-#define IDC_HISTORYCONTROL 1016
-#define IDC_IB_SEPARATOR 1017
-#define IDC_ICO_MESSAGES 1018
-#define IDC_ICO_FILES 1019
-#define IDC_ICO_URLS 1020
-#define IDC_ICO_TOTAL 1021
-#define IDC_TXT_MESSAGES 1022
-#define IDC_TXT_FILES 1023
-#define IDC_TXT_URLS 1024
-#define IDC_TXT_TOTAL 1025
-#define IDC_TXT_MESSAGES_IN 1026
-#define IDC_TXT_FILES_IN 1027
-#define IDC_TXT_URLS_IN 1028
-#define IDC_TXT_TOTAL_IN 1029
-#define IDC_TXT_MESSAGES_OUT 1030
-#define IDC_TXT_FILES_OUT 1031
-#define IDC_TXT_URLS_OUT 1032
-#define IDC_TXT_TOTAL_OUT 1033
-#define IDC_ICO_MESSAGES_IN 1034
-#define IDC_ICO_MESSAGES_OUT 1035
-#define IDC_ICO_FILES_IN 1036
-#define IDC_ICO_FILES_OUT 1037
-#define IDC_ICO_URLS_IN 1038
-#define IDC_ICO_URLS_OUT 1039
-#define IDC_ICO_TOTAL_IN 1040
-#define IDC_ICO_TOTAL_OUT 1041
-#define IDC_DATE_FROM 1042
-#define IDC_DATE_TO 1043
-#define IDC_CHK_DATE_FROM 1044
-#define IDC_CHK_DATE_TO 1045
-#define IDC_MONTHCALENDAR 1046
-#define IDC_GROUPING 1047
-#define IDC_TEMPLATES 1048
-#define IDC_EDITTEMPLATE 1049
-#define IDC_PREVIEW 1050
-#define IDC_GPREVIEW 1051
-#define IDC_RESET 1052
-#define IDC_DISCARD 1053
-#define IDC_BOOKMARKS 1054
-#define IDC_VARHELP 1055
-#define IDC_VSCROLL 1056
-#define IDC_DRAWEDGE 1057
-#define IDC_SORT_ASCENDING 1058
-#define IDC_HPP_COMPAT 1059
-#define IDC_SORT_ASCENDING2 1059
-#define IDC_BOOKMARKSLIST 1060
-#define IDC_SHOW_TYPE 1060
-#define IDC_SHOW_DIRECTION 1061
-#define IDC_TOPLINE 1062
-#define IDC_DELSERVERHISTORY 1063
-#define IDC_BOTH 1064
-#define IDC_COLOR1 1065
-#define IDC_COLOR2 1066
-#define IDC_COLOR3 1067
-#define IDC_COLOR4 1068
-#define IDC_COLOR5 1069
-#define ID_FILTER_SHOWALLEVENTS 40001
-#define ID_FILTER_SHOWINCOMINGEVENTSONLY 40002
-#define ID_FILTER_SHOWOUTGOINGEVENTSONLY 40003
-#define ID_FILTER_USECUSTOMFILTER 40004
-#define ID_FILTER_INCOMING 40005
-#define ID_FILTER_OUTGOING 40006
-#define ID_FILTER_MESSAGES 40007
-#define ID_FILTER_FILES 40008
-#define ID_FILTER_URLS 40009
-#define ID_FILTER_STATUS 40010
-#define ID_FILTER_OTHER 40011
-#define ID_FILTER_AUTO 40012
-#define ID_LOGOPTIONS_OPTIONS 40013
-#define ID_LOGOPTIONS_TEMPLATES 40014
-
-// Next default values for new objects
-//
-#ifdef APSTUDIO_INVOKED
-#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 131
-#define _APS_NEXT_COMMAND_VALUE 40019
-#define _APS_NEXT_CONTROL_VALUE 1061
-#define _APS_NEXT_SYMED_VALUE 101
-#endif
-#endif
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by W:\miranda-ng\plugins\NewStory\res\resource.rc
+//
+#define IDD_HISTORY 101
+#define IDD_OPT_TEMPLATES 102
+#define IDD_OPT_ADVANCED 103
+#define IDD_CALENDARTOOL 104
+#define IDR_POPUPS 105
+#define IDI_BOOKMARK 106
+#define IDI_NEWSTORY 107
+#define IDI_USERINFO 108
+#define IDI_USERMENU 109
+#define IDI_SEARCH 110
+#define IDI_OPTIONS 111
+#define IDI_FILTER 112
+#define IDI_EXPORT 113
+#define IDI_COPY 114
+#define IDI_SENDMSG 115
+#define IDI_MSGIN 116
+#define IDI_MSGOUT 117
+#define IDI_SIGNIN 118
+#define IDI_OK 119
+#define IDI_UNKNOWN 121
+#define IDI_FINDPREV 122
+#define IDI_FINDNEXT 123
+#define IDI_CALENDAR 124
+#define IDI_TPLGROUP 125
+#define IDI_RESET 126
+#define IDI_USERS 127
+#define IDI_VARHELP 128
+#define IDI_TIMETREE 129
+#define IDI_REPLY 130
+#define IDD_EMPTYHISTORY 131
+#define IDC_USERINFO 1000
+#define IDC_USERMENU 1001
+#define IDC_MESSAGE 1002
+#define IDC_SEARCH 1003
+#define IDC_COPY 1004
+#define IDC_EXPORT 1005
+#define IDC_DELETE 1006
+#define IDC_TIMETREE 1007
+#define IDC_FILTER 1008
+#define IDC_FINDPREV 1009
+#define IDC_SPLITTERY 1010
+#define IDC_SEARCHTEXT 1011
+#define IDC_SEARCHICON 1012
+#define IDC_LOGOPTIONS 1013
+#define IDC_TIMETREEVIEW 1014
+#define IDC_DATEPOPUP 1015
+#define IDC_HISTORYCONTROL 1016
+#define IDC_IB_SEPARATOR 1017
+#define IDC_ICO_MESSAGES 1018
+#define IDC_ICO_FILES 1019
+#define IDC_ICO_URLS 1020
+#define IDC_ICO_TOTAL 1021
+#define IDC_TXT_MESSAGES 1022
+#define IDC_TXT_FILES 1023
+#define IDC_TXT_URLS 1024
+#define IDC_TXT_TOTAL 1025
+#define IDC_TXT_MESSAGES_IN 1026
+#define IDC_TXT_FILES_IN 1027
+#define IDC_TXT_URLS_IN 1028
+#define IDC_TXT_TOTAL_IN 1029
+#define IDC_TXT_MESSAGES_OUT 1030
+#define IDC_TXT_FILES_OUT 1031
+#define IDC_TXT_URLS_OUT 1032
+#define IDC_TXT_TOTAL_OUT 1033
+#define IDC_ICO_MESSAGES_IN 1034
+#define IDC_ICO_MESSAGES_OUT 1035
+#define IDC_ICO_FILES_IN 1036
+#define IDC_ICO_FILES_OUT 1037
+#define IDC_ICO_URLS_IN 1038
+#define IDC_ICO_URLS_OUT 1039
+#define IDC_ICO_TOTAL_IN 1040
+#define IDC_ICO_TOTAL_OUT 1041
+#define IDC_DATE_FROM 1042
+#define IDC_DATE_TO 1043
+#define IDC_CHK_DATE_FROM 1044
+#define IDC_CHK_DATE_TO 1045
+#define IDC_MONTHCALENDAR 1046
+#define IDC_GROUPING 1047
+#define IDC_TEMPLATES 1048
+#define IDC_EDITTEMPLATE 1049
+#define IDC_PREVIEW 1050
+#define IDC_GPREVIEW 1051
+#define IDC_RESET 1052
+#define IDC_DISCARD 1053
+#define IDC_BOOKMARKS 1054
+#define IDC_VARHELP 1055
+#define IDC_VSCROLL 1056
+#define IDC_DRAWEDGE 1057
+#define IDC_SORT_ASCENDING 1058
+#define IDC_HPP_COMPAT 1059
+#define IDC_SORT_ASCENDING2 1059
+#define IDC_BOOKMARKSLIST 1060
+#define IDC_SHOW_TYPE 1060
+#define IDC_SHOW_DIRECTION 1061
+#define IDC_TOPLINE 1062
+#define IDC_DELSERVERHISTORY 1063
+#define IDC_BOTH 1064
+#define ID_FILTER_SHOWALLEVENTS 40001
+#define ID_FILTER_SHOWINCOMINGEVENTSONLY 40002
+#define ID_FILTER_SHOWOUTGOINGEVENTSONLY 40003
+#define ID_FILTER_USECUSTOMFILTER 40004
+#define ID_FILTER_INCOMING 40005
+#define ID_FILTER_OUTGOING 40006
+#define ID_FILTER_MESSAGES 40007
+#define ID_FILTER_FILES 40008
+#define ID_FILTER_URLS 40009
+#define ID_FILTER_STATUS 40010
+#define ID_FILTER_OTHER 40011
+#define ID_FILTER_AUTO 40012
+#define ID_LOGOPTIONS_OPTIONS 40013
+#define ID_LOGOPTIONS_TEMPLATES 40014
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 131
+#define _APS_NEXT_COMMAND_VALUE 40019
+#define _APS_NEXT_CONTROL_VALUE 1061
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/plugins/NewStory/src/stdafx.h b/plugins/NewStory/src/stdafx.h
index 5cafb41211..11fa8753b4 100644
--- a/plugins/NewStory/src/stdafx.h
+++ b/plugins/NewStory/src/stdafx.h
@@ -1,121 +1,115 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation version 2
-of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-//Windows headers
-#include <windows.h>
-#include <windowsx.h>
-#include <process.h>
-#include <tchar.h>
-#include <commctrl.h>
-#include <time.h>
-#include <Shlwapi.h>
-#include <malloc.h>
-
-#include <map>
-
-//Miranda headers
-#include <newpluginapi.h>
-#include <m_button.h>
-#include <m_chat_int.h>
-#include <m_clc.h>
-#include <m_clistint.h>
-#include <m_contacts.h>
-#include <m_db_int.h>
-#include <m_file.h>
-#include <m_fontservice.h>
-#include <m_history.h>
-#include <m_hotkeys.h>
-#include <m_icolib.h>
-#include <m_json.h>
-#include <m_langpack.h>
-#include <m_message.h>
-#include <m_metacontacts.h>
-#include <m_netlib.h>
-#include <m_options.h>
-#include <m_protocols.h>
-#include <m_protosvc.h>
-#include <m_skin.h>
-#include <m_srmm_int.h>
-#include <m_text.h>
-#include <m_timezones.h>
-#include <m_toptoolbar.h>
-#include <m_userinfo.h>
-#include <m_utils.h>
-
-#include "m_NewStory.h"
-#include "m_PluginUpdater.h"
-#include "m_smileyadd.h"
-#ifndef MTEXT_NOHELPERS
-#define MTEXT_NOHELPERS
-#endif // MTEXT_NOHELPERS
-#include "m_text.h"
-
-#include "resource.h"
-#include "version.h"
-
-#define MODULENAME "NewStory"
-#define MODULETITLE "NewStory"
-
-#include "utils.h"
-#include "fonts.h"
-#include "calendartool.h"
-#include "history.h"
-#include "history_array.h"
-#include "history_control.h"
-#include "templates.h"
-
-void InitServices();
-
-int OptionsInitialize(WPARAM, LPARAM);
-
-enum
-{
- HOTKEY_BOOKMARK = 1,
- HOTKEY_SEARCH = 2,
- HOTKEY_SEEK_FORWARD = 3,
- HOTKEY_SEEK_BACK = 4,
-};
-
-struct CMPlugin : public PLUGIN<CMPlugin>
-{
- HANDLE m_log;
- HBRUSH hBackBrush;
-
- CMOption<bool> bOptVScroll, bSortAscending;
-
- // thesw options are a copy of static CMOption to keep performance high
- bool bMsgGrouping, bDrawEdge, bHppCompat, bDisableDelete = false;
- bool bShowType, bShowDirecction;
-
- COLORREF clCustom[5];
-
- CMPlugin();
-
- void LoadOptions();
-
- int Load() override;
- int Unload() override;
-};
-
-extern CMOption<bool> g_bOptGrouping, g_bOptDrawEdge, g_bOptHppCompat, g_bShowType, g_bShowDirection;
-extern CMOption<uint32_t> g_clCustom0, g_clCustom1, g_clCustom2, g_clCustom3, g_clCustom4;
-
-extern wchar_t* months[12];
-extern int g_iPixelY;
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+//Windows headers
+#include <windows.h>
+#include <windowsx.h>
+#include <process.h>
+#include <tchar.h>
+#include <commctrl.h>
+#include <time.h>
+#include <Shlwapi.h>
+#include <malloc.h>
+
+#include <map>
+
+//Miranda headers
+#include <newpluginapi.h>
+#include <m_button.h>
+#include <m_chat_int.h>
+#include <m_clc.h>
+#include <m_clistint.h>
+#include <m_contacts.h>
+#include <m_db_int.h>
+#include <m_file.h>
+#include <m_fontservice.h>
+#include <m_history.h>
+#include <m_hotkeys.h>
+#include <m_icolib.h>
+#include <m_json.h>
+#include <m_langpack.h>
+#include <m_message.h>
+#include <m_metacontacts.h>
+#include <m_netlib.h>
+#include <m_options.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_skin.h>
+#include <m_srmm_int.h>
+#include <m_text.h>
+#include <m_timezones.h>
+#include <m_toptoolbar.h>
+#include <m_userinfo.h>
+#include <m_utils.h>
+
+#include "m_NewStory.h"
+#include "m_PluginUpdater.h"
+#include "m_smileyadd.h"
+#ifndef MTEXT_NOHELPERS
+#define MTEXT_NOHELPERS
+#endif // MTEXT_NOHELPERS
+#include "m_text.h"
+
+#include "resource.h"
+#include "version.h"
+
+#define MODULENAME "NewStory"
+#define MODULETITLE "NewStory"
+
+#include "utils.h"
+#include "fonts.h"
+#include "calendartool.h"
+#include "history.h"
+#include "history_array.h"
+#include "history_control.h"
+#include "templates.h"
+
+void InitServices();
+
+int OptionsInitialize(WPARAM, LPARAM);
+
+enum
+{
+ HOTKEY_BOOKMARK = 1,
+ HOTKEY_SEARCH = 2,
+ HOTKEY_SEEK_FORWARD = 3,
+ HOTKEY_SEEK_BACK = 4,
+};
+
+struct CMPlugin : public PLUGIN<CMPlugin>
+{
+ HANDLE m_log;
+ HBRUSH hBackBrush;
+
+ CMOption<bool> bOptVScroll, bSortAscending;
+
+ // thesw options are a copy of static CMOption to keep performance high
+ bool bMsgGrouping, bDrawEdge, bHppCompat, bDisableDelete = false;
+ bool bShowType, bShowDirecction;
+
+ CMPlugin();
+
+ int Load() override;
+ int Unload() override;
+};
+
+extern CMOption<bool> g_bOptGrouping, g_bOptDrawEdge, g_bOptHppCompat, g_bShowType, g_bShowDirection;
+extern wchar_t* months[12];
+extern int g_iPixelY;
diff --git a/plugins/NewStory/src/templates.cpp b/plugins/NewStory/src/templates.cpp
index 16341329ea..d598d9ce99 100644
--- a/plugins/NewStory/src/templates.cpp
+++ b/plugins/NewStory/src/templates.cpp
@@ -1,473 +1,473 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
-
-int g_iPixelY;
-
-wchar_t *weekDays[7] = { LPGENW("Sunday"), LPGENW("Monday"), LPGENW("Tuesday"), LPGENW("Wednesday"), LPGENW("Thursday"), LPGENW("Friday"), LPGENW("Saturday") };
-
-wchar_t *months[12] =
-{
- LPGENW("January"), LPGENW("February"), LPGENW("March"), LPGENW("April"), LPGENW("May"), LPGENW("June"),
- LPGENW("July"), LPGENW("August"), LPGENW("September"), LPGENW("October"), LPGENW("November"), LPGENW("December")
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// RTF generator
-
-static void AppendUnicodeToBuffer(CMStringA &buf, const wchar_t *p)
-{
- for (; *p; p++) {
- if (*p == '\r' && p[1] == '\n') {
- buf.Append("\\par ");
- p++;
- }
- else if (*p == '\n') {
- buf.Append("\\par ");
- }
- else if (*p == '\t') {
- buf.Append("\\tab ");
- }
- else if (*p == '\\' || *p == '{' || *p == '}') {
- buf.AppendChar('\\');
- buf.AppendChar((char)*p);
- }
- else if (*p == '[') {
- p++;
- if (*p == 'c' && p[2] == ']') {
- buf.AppendFormat("\\cf%c ", p[1]);
- p += 2;
- continue;
- }
-
- char *pEnd = "";
- if (*p == '/') {
- pEnd = "0";
- p++;
- }
- if (*p == 'b' && p[1] == ']') {
- buf.AppendFormat("\\b%s ", pEnd);
- p++;
- }
- else if (*p == 'i' && p[1] == ']') {
- buf.AppendFormat("\\i%s ", pEnd);
- p++;
- }
- else if (*p == 'u' && p[1] == ']') {
- buf.AppendFormat("\\ul%s ", pEnd);
- p++;
- }
- else if (*p == 's' && p[1] == ']') {
- buf.AppendFormat("\\strike%s ", pEnd);
- p++;
- }
- else {
- buf.AppendChar('[');
- if (*pEnd == '0')
- p--;
- p--;
- }
- }
- else if (*p < 128) {
- buf.AppendChar((char)*p);
- }
- else {
- buf.AppendFormat("\\u%d ?", *p);
- }
- }
-}
-
-CMStringA ItemData::formatRtf(const wchar_t *pwszStr)
-{
- CMStringA buf;
- buf.Append("{\\rtf1\\ansi\\deff0");
-
- int fontID, colorID;
- getFontColor(fontID, colorID);
- auto &F = g_fontTable[fontID];
- buf.AppendFormat("{\\fonttbl{\\f0\\fnil\\fcharset0 %s;}}", F.lf.lfFaceName);
-
- COLORREF cr = F.cl;
- buf.AppendFormat("{\\colortbl \\red%u\\green%u\\blue%u;", GetRValue(cr), GetGValue(cr), GetBValue(cr));
- cr = g_colorTable[(dbe.flags & DBEF_SENT) ? COLOR_OUTNICK : COLOR_INNICK].cl;
- buf.AppendFormat("\\red%u\\green%u\\blue%u;", GetRValue(cr), GetGValue(cr), GetBValue(cr));
-
- for (auto cl : g_plugin.clCustom) {
- cr = (cl == -1) ? 0 : cl;
- buf.AppendFormat("\\red%u\\green%u\\blue%u;", GetRValue(cr), GetGValue(cr), GetBValue(cr));
- }
-
- buf.AppendFormat("}\\uc1\\pard \\cf0\\f0\\b0\\i0\\fs%d ", GetFontHeight(F.lf));
- AppendUnicodeToBuffer(buf, (pwszStr) ? pwszStr : formatString());
-
- buf.Append("}");
- // Netlib_Logf(0, buf);
- return buf;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Template formatting for the control
-
-CMStringW TplFormatString(int tpl, MCONTACT hContact, ItemData *item)
-{
- if (tpl < 0 || tpl >= TPL_COUNT)
- return CMStringW();
-
- auto &T = templates[tpl];
- if (T.value == nullptr)
- T.value = mir_wstrdup(T.defvalue);
-
- TemplateVars vars;
-
- for (auto &it : T.vf)
- if (it)
- it(&vars, hContact, item);
-
- CMStringW buf;
-
- for (wchar_t *p = T.value; *p; p++) {
- if (*p == '%') {
- wchar_t *var = vars.GetVar((p[1] & 0xff));
- if (var)
- buf.Append(var);
-
- p++;
- }
- else buf.AppendChar(*p);
- }
-
- return buf;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Template formatting for options dialog
-
-CMStringW ItemData::formatStringEx(wchar_t *sztpl)
-{
- CMStringW buf;
- int tpl = getTemplate();
- if (tpl < 0 || tpl >= TPL_COUNT || !sztpl)
- return buf;
-
- TemplateVars vars;
-
- auto &T = templates[tpl];
- for (auto &it : T.vf)
- if (it)
- it(&vars, hContact, this);
-
- for (wchar_t *p = sztpl; *p; p++) {
- if (*p == '%') {
- wchar_t *var = vars.GetVar((p[1] & 0xff));
- if (var)
- buf.Append(var);
- p++;
- }
- else buf.AppendChar(*p);
- }
-
- return buf;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TemplateVars members
-
-TemplateVars::TemplateVars()
-{
- memset(&vars, 0, sizeof(vars));
-}
-
-TemplateVars::~TemplateVars()
-{
- for (auto &V : vars)
- if (V.val && V.del)
- mir_free(V.val);
-}
-
-// Loading variables
-void vfGlobal(TemplateVars *vars, MCONTACT hContact, ItemData *)
-{
- // %%: simply % character
- vars->SetVar('%', L"%", false);
-
- // %n: line break
- vars->SetVar('n', L"\x0d\x0a", false);
-
- // %S: my nick (not for messages)
- char* proto = Proto_GetBaseAccountName(hContact);
- ptrW nick(Contact::GetInfo(CNF_DISPLAY, 0, proto));
- vars->SetVar('S', nick, true);
-}
-
-void vfContact(TemplateVars *vars, MCONTACT hContact, ItemData *)
-{
- // %N: buddy's nick (not for messages)
- wchar_t *nick = (hContact == 0) ? TranslateT("System history") : Clist_GetContactDisplayName(hContact, 0);
- vars->SetVar('N', nick, false);
-
- wchar_t buf[20];
- // %c: event count
- mir_snwprintf(buf, L"%d", db_event_count(hContact));
- vars->SetVar('c', buf, true);
-}
-
-void vfSystem(TemplateVars *vars, MCONTACT hContact, ItemData *)
-{
- // %N: buddy's nick (not for messages)
- vars->SetVar('N', TranslateT("System event"), false);
-
- // %c: event count
- wchar_t buf[20];
- mir_snwprintf(buf, L"%d", db_event_count(hContact));
- vars->SetVar('c', buf, true);
-}
-
-void vfEvent(TemplateVars *vars, MCONTACT, ItemData *item)
-{
- wchar_t buf[100];
-
- // %N: Nickname
- if (!item->m_bIsResult && (item->dbe.flags & DBEF_SENT)) {
- if (!item->wszNick) {
- char *proto = Proto_GetBaseAccountName(item->hContact);
- ptrW nick(Contact::GetInfo(CNF_DISPLAY, 0, proto));
- vars->SetNick(nick);
- }
- else vars->SetNick(item->wszNick);
- }
- else {
- wchar_t *nick = (item->wszNick) ? item->wszNick : Clist_GetContactDisplayName(item->hContact, 0);
- vars->SetNick(nick);
- }
-
- // %D: direction symbol
- if (item->dbe.flags & DBEF_SENT)
- vars->SetVar('D', L"<<", false);
- else
- vars->SetVar('D', L">>", false);
-
- // %t: timestamp
- SYSTEMTIME st;
- if (!TimeZone_GetSystemTime(nullptr, item->dbe.timestamp, &st, 0)) {
- int iLocale = Langpack_GetDefaultLocale();
-
- GetDateFormatW(iLocale, 0, &st, L"dd.MM.yyyy, ", buf, _countof(buf));
- GetTimeFormatW(iLocale, 0, &st, L"HH:mm", buf + 12, _countof(buf));
- vars->SetVar('t', buf, true);
-
- // %h: hour (24 hour format, 0-23)
- GetTimeFormatW(iLocale, 0, &st, L"HH", buf, _countof(buf));
- vars->SetVar('h', buf, true);
-
- // %a: hour (12 hour format)
- GetTimeFormatW(iLocale, 0, &st, L"hh", buf, _countof(buf));
- vars->SetVar('a', buf, true);
-
- // %m: minute
- GetTimeFormatW(iLocale, 0, &st, L"mm", buf, _countof(buf));
- vars->SetVar('m', buf, true);
-
- // %s: second
- GetTimeFormatW(iLocale, 0, &st, L"ss", buf, _countof(buf));
- vars->SetVar('s', buf, true);
-
- // %o: month
- GetDateFormatW(iLocale, 0, &st, L"MM", buf, _countof(buf));
- vars->SetVar('o', buf, true);
-
- // %d: day of month
- GetDateFormatW(iLocale, 0, &st, L"dd", buf, _countof(buf));
- vars->SetVar('d', buf, true);
-
- // %y: year
- GetDateFormatW(iLocale, 0, &st, L"yyyy", buf, _countof(buf));
- vars->SetVar('y', buf, true);
-
- // %w: day of week (Sunday, Monday... translatable)
- vars->SetVar('w', TranslateW(weekDays[st.wDayOfWeek]), false);
-
- // %p: AM/PM symbol
- vars->SetVar('p', (st.wHour > 11) ? L"PM" : L"AM", false);
-
- // %O: Name of month, translatable
- vars->SetVar('O', TranslateW(months[st.wMonth-1]), false);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// %M: the message string itself
-
-void vfMessage(TemplateVars *vars, MCONTACT, ItemData *item)
-{
- vars->SetVar('M', item->getWBuf(), false);
-}
-
-void vfFile(TemplateVars *vars, MCONTACT, ItemData *item)
-{
- vars->SetVar('M', item->getWBuf(), false);
-}
-
-void vfSign(TemplateVars *vars, MCONTACT, ItemData *item)
-{
- vars->SetVar('M', item->getWBuf(), false);
-}
-
-void vfAuth(TemplateVars *vars, MCONTACT, ItemData *item)
-{
- vars->SetVar('M', item->getWBuf(), false);
-}
-
-void vfAdded(TemplateVars *vars, MCONTACT, ItemData *item)
-{
- vars->SetVar('M', item->getWBuf(), false);
-}
-
-void vfPresence(TemplateVars* vars, MCONTACT, ItemData* item)
-{
- vars->SetVar('M', item->getWBuf(), false);
-}
-
-void vfDeleted(TemplateVars *vars, MCONTACT, ItemData *item)
-{
- vars->SetVar('M', item->getWBuf(), false);
-}
-
-void vfOther(TemplateVars *vars, MCONTACT, ItemData *item)
-{
- auto *pText = item->getWBuf();
- vars->SetVar('M', mir_wstrlen(pText) == 0 ? TranslateT("Unknown event") : pText, false);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void TemplateVars::SetNick(wchar_t *v)
-{
- CMStringW wszNick(FORMAT, L"[c1]%s[c0]", v);
-
- auto &V = vars['N'];
- if (V.del)
- mir_free(V.val);
- V.val = wszNick.Detach();
- V.del = true;
-}
-
-void TemplateVars::SetVar(uint8_t id, wchar_t *v, bool d)
-{
- auto &V = vars[id];
- if (V.del)
- mir_free(V.val);
-
- V.val = (d) ? mir_wstrdup(v) : v;
- V.del = d;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-HICON TemplateInfo::getIcon() const
-{
- return (iIcon < 0) ? Skin_LoadIcon(-iIcon) : g_plugin.getIcon(iIcon);
-}
-
-TemplateInfo templates[TPL_COUNT] =
-{
- { "tpl/interface/title", LPGENW("Interface"), IDI_NEWSTORY, LPGENW("Window title"),
- LPGENW("%N - history [%c messages total]"), 0, 0,
- { vfGlobal, vfContact, 0, 0, 0 } },
-
- { "tpl/msglog/msg", LPGENW("Message log"), IDI_SENDMSG, LPGENW("Messages"),
- L"[b]%N, %t:[/b]\r\n%M", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfMessage, 0 } },
- { "tpl/msglog/msg_head", LPGENW("Message log"), IDI_SENDMSG, LPGENW("Group head"),
- L"[b]%N, %t:[/b] %M", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfMessage, 0 } },
- { "tpl/msglog/msg_grp", LPGENW("Message log"), IDI_SENDMSG, LPGENW("Grouped messages"),
- L"[b]%h:%m:%s:[/b] %M", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfMessage, 0 } },
- { "tpl/msglog/file", LPGENW("Message log"), -SKINICON_EVENT_FILE, LPGENW("Files"),
- L"[b]%N, %t:[/b]%n%M", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfFile, 0 } },
- { "tpl/msglog/status", LPGENW("Message log"), IDI_SIGNIN, LPGENW("Status changes"),
- L"[b]%N, %t:[/b]%n%M", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfSign, 0 } },
- { "tpl/msglog/presense", LPGENW("Message log"), IDI_UNKNOWN, LPGENW("Presence requests"),
- L"%B[b]%N, %t:[/b]%n%M", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfPresence, 0 } },
- { "tpl/msglog/other", LPGENW("Message log"), IDI_UNKNOWN, LPGENW("Other events"),
- L"%B[b]%N, %t:[/b]%n%M", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfOther, 0 } },
-
- { "tpl/msglog/authrq", LPGENW("Message log"), IDI_UNKNOWN, LPGENW("Authorization requests"),
- L"[b]%N, %t:[/b]%n%M", 0, 0,
- { vfGlobal, vfEvent, vfSystem, vfAuth, 0 } },
- { "tpl/msglog/added", LPGENW("Message log"), IDI_UNKNOWN, LPGENW("'You were added' events"),
- L"[b]%N, %t:[/b]%n%M", 0, 0,
- { vfGlobal, vfEvent, vfSystem, vfAdded, 0 } },
- { "tpl/msglog/deleted", LPGENW("Message log"), IDI_UNKNOWN, LPGENW("'You were deleted' events"),
- L"[b]%N, %t:[/b]%n%M", 0, 0,
- { vfGlobal, vfEvent, vfSystem, vfDeleted, 0 } },
-
- { "tpl/copy/msg", LPGENW("Clipboard"), IDI_SENDMSG, LPGENW("Messages"),
- L"%N, %t:\x0d\x0a%M%n", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfMessage, 0 } },
- { "tpl/copy/file", LPGENW("Clipboard"), -SKINICON_EVENT_FILE, LPGENW("Files"),
- L"%N, %t:\x0d\x0a%M%n", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfFile, 0 } },
- { "tpl/copy/status", LPGENW("Clipboard"), IDI_SIGNIN, LPGENW("Status changes"),
- L"%N, %t:\x0d\x0a%M%n", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfSign, 0 } },
- { "tpl/copy/presence", LPGENW("Clipboard"), IDI_UNKNOWN, LPGENW("Presence requests"),
- L"%N, %t:\x0d\x0a%M%n", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfPresence, 0 } },
- { "tpl/copy/other", LPGENW("Clipboard"), IDI_UNKNOWN, LPGENW("Other events"),
- L"%N, %t:\x0d\x0a%M%n", 0, 0,
- { vfGlobal, vfContact, vfEvent, vfOther, 0 } },
-
- { "tpl/copy/authrq", LPGENW("Clipboard"), IDI_UNKNOWN, LPGENW("Authorization requests"),
- L"%N, %t:\x0d\x0a%M%n", 0, 0,
- { vfGlobal, vfEvent, vfSystem, vfAuth, 0 } },
- { "tpl/copy/added", LPGENW("Clipboard"), IDI_UNKNOWN, LPGENW("'You were added' events"),
- L"%N, %t:\x0d\x0a%M%n", 0, 0,
- { vfGlobal, vfEvent, vfSystem, vfAdded, 0 } },
- { "tpl/copy/deleted", LPGENW("Clipboard"), IDI_UNKNOWN, LPGENW("'You were deleted' events"),
- L"%N, %t:\x0d\x0a%M%n", 0, 0,
- { vfGlobal, vfEvent, vfSystem, vfDeleted, 0 } }
-};
-
-void LoadTemplates()
-{
- HDC hdc = GetDC(nullptr);
- g_iPixelY = GetDeviceCaps(hdc, LOGPIXELSY);
- ReleaseDC(nullptr, hdc);
-
- for (auto &it : templates)
- replaceStrW(it.value, g_plugin.getWStringA(it.setting));
-}
-
-void SaveTemplates()
-{
- for (auto &it : templates) {
- if (it.value) {
- if (mir_wstrcmp(it.value, it.defvalue))
- g_plugin.setWString(it.setting, it.value);
- else
- g_plugin.delSetting(it.setting);
- }
- else g_plugin.delSetting(it.setting);
- }
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
+
+int g_iPixelY;
+
+wchar_t *weekDays[7] = { LPGENW("Sunday"), LPGENW("Monday"), LPGENW("Tuesday"), LPGENW("Wednesday"), LPGENW("Thursday"), LPGENW("Friday"), LPGENW("Saturday") };
+
+wchar_t *months[12] =
+{
+ LPGENW("January"), LPGENW("February"), LPGENW("March"), LPGENW("April"), LPGENW("May"), LPGENW("June"),
+ LPGENW("July"), LPGENW("August"), LPGENW("September"), LPGENW("October"), LPGENW("November"), LPGENW("December")
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// RTF generator
+
+static void AppendUnicodeToBuffer(CMStringA &buf, const wchar_t *p)
+{
+ for (; *p; p++) {
+ if (*p == '\r' && p[1] == '\n') {
+ buf.Append("\\par ");
+ p++;
+ }
+ else if (*p == '\n') {
+ buf.Append("\\par ");
+ }
+ else if (*p == '\t') {
+ buf.Append("\\tab ");
+ }
+ else if (*p == '\\' || *p == '{' || *p == '}') {
+ buf.AppendChar('\\');
+ buf.AppendChar((char)*p);
+ }
+ else if (*p == '[') {
+ p++;
+ if (*p == 'c' && p[2] == ']') {
+ buf.AppendFormat("\\cf%c ", p[1]);
+ p += 2;
+ continue;
+ }
+
+ char *pEnd = "";
+ if (*p == '/') {
+ pEnd = "0";
+ p++;
+ }
+ if (*p == 'b' && p[1] == ']') {
+ buf.AppendFormat("\\b%s ", pEnd);
+ p++;
+ }
+ else if (*p == 'i' && p[1] == ']') {
+ buf.AppendFormat("\\i%s ", pEnd);
+ p++;
+ }
+ else if (*p == 'u' && p[1] == ']') {
+ buf.AppendFormat("\\ul%s ", pEnd);
+ p++;
+ }
+ else if (*p == 's' && p[1] == ']') {
+ buf.AppendFormat("\\strike%s ", pEnd);
+ p++;
+ }
+ else {
+ buf.AppendChar('[');
+ if (*pEnd == '0')
+ p--;
+ p--;
+ }
+ }
+ else if (*p < 128) {
+ buf.AppendChar((char)*p);
+ }
+ else {
+ buf.AppendFormat("\\u%d ?", *p);
+ }
+ }
+}
+
+CMStringA ItemData::formatRtf(const wchar_t *pwszStr)
+{
+ CMStringA buf;
+ buf.Append("{\\rtf1\\ansi\\deff0");
+
+ int fontID, colorID;
+ getFontColor(fontID, colorID);
+ auto &F = g_fontTable[fontID];
+ buf.AppendFormat("{\\fonttbl{\\f0\\fnil\\fcharset0 %s;}}", F.lf.lfFaceName);
+
+ COLORREF cr = F.cl;
+ buf.AppendFormat("{\\colortbl \\red%u\\green%u\\blue%u;", GetRValue(cr), GetGValue(cr), GetBValue(cr));
+ cr = g_colorTable[(dbe.flags & DBEF_SENT) ? COLOR_OUTNICK : COLOR_INNICK].cl;
+ buf.AppendFormat("\\red%u\\green%u\\blue%u;", GetRValue(cr), GetGValue(cr), GetBValue(cr));
+
+ for (auto cl : g_plugin.clCustom) {
+ cr = (cl == -1) ? 0 : cl;
+ buf.AppendFormat("\\red%u\\green%u\\blue%u;", GetRValue(cr), GetGValue(cr), GetBValue(cr));
+ }
+
+ buf.AppendFormat("}\\uc1\\pard \\cf0\\f0\\b0\\i0\\fs%d ", GetFontHeight(F.lf));
+ AppendUnicodeToBuffer(buf, (pwszStr) ? pwszStr : formatString());
+
+ buf.Append("}");
+ // Netlib_Logf(0, buf);
+ return buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Template formatting for the control
+
+CMStringW TplFormatString(int tpl, MCONTACT hContact, ItemData *item)
+{
+ if (tpl < 0 || tpl >= TPL_COUNT)
+ return CMStringW();
+
+ auto &T = templates[tpl];
+ if (T.value == nullptr)
+ T.value = mir_wstrdup(T.defvalue);
+
+ TemplateVars vars;
+
+ for (auto &it : T.vf)
+ if (it)
+ it(&vars, hContact, item);
+
+ CMStringW buf;
+
+ for (wchar_t *p = T.value; *p; p++) {
+ if (*p == '%') {
+ wchar_t *var = vars.GetVar((p[1] & 0xff));
+ if (var)
+ buf.Append(var);
+
+ p++;
+ }
+ else buf.AppendChar(*p);
+ }
+
+ return buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Template formatting for options dialog
+
+CMStringW ItemData::formatStringEx(wchar_t *sztpl)
+{
+ CMStringW buf;
+ int tpl = getTemplate();
+ if (tpl < 0 || tpl >= TPL_COUNT || !sztpl)
+ return buf;
+
+ TemplateVars vars;
+
+ auto &T = templates[tpl];
+ for (auto &it : T.vf)
+ if (it)
+ it(&vars, hContact, this);
+
+ for (wchar_t *p = sztpl; *p; p++) {
+ if (*p == '%') {
+ wchar_t *var = vars.GetVar((p[1] & 0xff));
+ if (var)
+ buf.Append(var);
+ p++;
+ }
+ else buf.AppendChar(*p);
+ }
+
+ return buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TemplateVars members
+
+TemplateVars::TemplateVars()
+{
+ memset(&vars, 0, sizeof(vars));
+}
+
+TemplateVars::~TemplateVars()
+{
+ for (auto &V : vars)
+ if (V.val && V.del)
+ mir_free(V.val);
+}
+
+// Loading variables
+void vfGlobal(TemplateVars *vars, MCONTACT hContact, ItemData *)
+{
+ // %%: simply % character
+ vars->SetVar('%', L"%", false);
+
+ // %n: line break
+ vars->SetVar('n', L"\x0d\x0a", false);
+
+ // %S: my nick (not for messages)
+ char* proto = Proto_GetBaseAccountName(hContact);
+ ptrW nick(Contact::GetInfo(CNF_DISPLAY, 0, proto));
+ vars->SetVar('S', nick, true);
+}
+
+void vfContact(TemplateVars *vars, MCONTACT hContact, ItemData *)
+{
+ // %N: buddy's nick (not for messages)
+ wchar_t *nick = (hContact == 0) ? TranslateT("System history") : Clist_GetContactDisplayName(hContact, 0);
+ vars->SetVar('N', nick, false);
+
+ wchar_t buf[20];
+ // %c: event count
+ mir_snwprintf(buf, L"%d", db_event_count(hContact));
+ vars->SetVar('c', buf, true);
+}
+
+void vfSystem(TemplateVars *vars, MCONTACT hContact, ItemData *)
+{
+ // %N: buddy's nick (not for messages)
+ vars->SetVar('N', TranslateT("System event"), false);
+
+ // %c: event count
+ wchar_t buf[20];
+ mir_snwprintf(buf, L"%d", db_event_count(hContact));
+ vars->SetVar('c', buf, true);
+}
+
+void vfEvent(TemplateVars *vars, MCONTACT, ItemData *item)
+{
+ wchar_t buf[100];
+
+ // %N: Nickname
+ if (!item->m_bIsResult && (item->dbe.flags & DBEF_SENT)) {
+ if (!item->wszNick) {
+ char *proto = Proto_GetBaseAccountName(item->hContact);
+ ptrW nick(Contact::GetInfo(CNF_DISPLAY, 0, proto));
+ vars->SetNick(nick);
+ }
+ else vars->SetNick(item->wszNick);
+ }
+ else {
+ wchar_t *nick = (item->wszNick) ? item->wszNick : Clist_GetContactDisplayName(item->hContact, 0);
+ vars->SetNick(nick);
+ }
+
+ // %D: direction symbol
+ if (item->dbe.flags & DBEF_SENT)
+ vars->SetVar('D', L"<<", false);
+ else
+ vars->SetVar('D', L">>", false);
+
+ // %t: timestamp
+ SYSTEMTIME st;
+ if (!TimeZone_GetSystemTime(nullptr, item->dbe.timestamp, &st, 0)) {
+ int iLocale = Langpack_GetDefaultLocale();
+
+ GetDateFormatW(iLocale, 0, &st, L"dd.MM.yyyy, ", buf, _countof(buf));
+ GetTimeFormatW(iLocale, 0, &st, L"HH:mm", buf + 12, _countof(buf));
+ vars->SetVar('t', buf, true);
+
+ // %h: hour (24 hour format, 0-23)
+ GetTimeFormatW(iLocale, 0, &st, L"HH", buf, _countof(buf));
+ vars->SetVar('h', buf, true);
+
+ // %a: hour (12 hour format)
+ GetTimeFormatW(iLocale, 0, &st, L"hh", buf, _countof(buf));
+ vars->SetVar('a', buf, true);
+
+ // %m: minute
+ GetTimeFormatW(iLocale, 0, &st, L"mm", buf, _countof(buf));
+ vars->SetVar('m', buf, true);
+
+ // %s: second
+ GetTimeFormatW(iLocale, 0, &st, L"ss", buf, _countof(buf));
+ vars->SetVar('s', buf, true);
+
+ // %o: month
+ GetDateFormatW(iLocale, 0, &st, L"MM", buf, _countof(buf));
+ vars->SetVar('o', buf, true);
+
+ // %d: day of month
+ GetDateFormatW(iLocale, 0, &st, L"dd", buf, _countof(buf));
+ vars->SetVar('d', buf, true);
+
+ // %y: year
+ GetDateFormatW(iLocale, 0, &st, L"yyyy", buf, _countof(buf));
+ vars->SetVar('y', buf, true);
+
+ // %w: day of week (Sunday, Monday... translatable)
+ vars->SetVar('w', TranslateW(weekDays[st.wDayOfWeek]), false);
+
+ // %p: AM/PM symbol
+ vars->SetVar('p', (st.wHour > 11) ? L"PM" : L"AM", false);
+
+ // %O: Name of month, translatable
+ vars->SetVar('O', TranslateW(months[st.wMonth-1]), false);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// %M: the message string itself
+
+void vfMessage(TemplateVars *vars, MCONTACT, ItemData *item)
+{
+ vars->SetVar('M', item->getWBuf(), false);
+}
+
+void vfFile(TemplateVars *vars, MCONTACT, ItemData *item)
+{
+ vars->SetVar('M', item->getWBuf(), false);
+}
+
+void vfSign(TemplateVars *vars, MCONTACT, ItemData *item)
+{
+ vars->SetVar('M', item->getWBuf(), false);
+}
+
+void vfAuth(TemplateVars *vars, MCONTACT, ItemData *item)
+{
+ vars->SetVar('M', item->getWBuf(), false);
+}
+
+void vfAdded(TemplateVars *vars, MCONTACT, ItemData *item)
+{
+ vars->SetVar('M', item->getWBuf(), false);
+}
+
+void vfPresence(TemplateVars* vars, MCONTACT, ItemData* item)
+{
+ vars->SetVar('M', item->getWBuf(), false);
+}
+
+void vfDeleted(TemplateVars *vars, MCONTACT, ItemData *item)
+{
+ vars->SetVar('M', item->getWBuf(), false);
+}
+
+void vfOther(TemplateVars *vars, MCONTACT, ItemData *item)
+{
+ auto *pText = item->getWBuf();
+ vars->SetVar('M', mir_wstrlen(pText) == 0 ? TranslateT("Unknown event") : pText, false);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TemplateVars::SetNick(wchar_t *v)
+{
+ CMStringW wszNick(FORMAT, L"[c1]%s[c0]", v);
+
+ auto &V = vars['N'];
+ if (V.del)
+ mir_free(V.val);
+ V.val = wszNick.Detach();
+ V.del = true;
+}
+
+void TemplateVars::SetVar(uint8_t id, wchar_t *v, bool d)
+{
+ auto &V = vars[id];
+ if (V.del)
+ mir_free(V.val);
+
+ V.val = (d) ? mir_wstrdup(v) : v;
+ V.del = d;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HICON TemplateInfo::getIcon() const
+{
+ return (iIcon < 0) ? Skin_LoadIcon(-iIcon) : g_plugin.getIcon(iIcon);
+}
+
+TemplateInfo templates[TPL_COUNT] =
+{
+ { "tpl/interface/title", LPGENW("Interface"), IDI_NEWSTORY, LPGENW("Window title"),
+ LPGENW("%N - history [%c messages total]"), 0, 0,
+ { vfGlobal, vfContact, 0, 0, 0 } },
+
+ { "tpl/msglog/msg", LPGENW("Message log"), IDI_SENDMSG, LPGENW("Messages"),
+ L"[b]%N, %t:[/b]\r\n%M", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfMessage, 0 } },
+ { "tpl/msglog/msg_head", LPGENW("Message log"), IDI_SENDMSG, LPGENW("Group head"),
+ L"[b]%N, %t:[/b] %M", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfMessage, 0 } },
+ { "tpl/msglog/msg_grp", LPGENW("Message log"), IDI_SENDMSG, LPGENW("Grouped messages"),
+ L"[b]%h:%m:%s:[/b] %M", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfMessage, 0 } },
+ { "tpl/msglog/file", LPGENW("Message log"), -SKINICON_EVENT_FILE, LPGENW("Files"),
+ L"[b]%N, %t:[/b]%n%M", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfFile, 0 } },
+ { "tpl/msglog/status", LPGENW("Message log"), IDI_SIGNIN, LPGENW("Status changes"),
+ L"[b]%N, %t:[/b]%n%M", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfSign, 0 } },
+ { "tpl/msglog/presense", LPGENW("Message log"), IDI_UNKNOWN, LPGENW("Presence requests"),
+ L"%B[b]%N, %t:[/b]%n%M", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfPresence, 0 } },
+ { "tpl/msglog/other", LPGENW("Message log"), IDI_UNKNOWN, LPGENW("Other events"),
+ L"%B[b]%N, %t:[/b]%n%M", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfOther, 0 } },
+
+ { "tpl/msglog/authrq", LPGENW("Message log"), IDI_UNKNOWN, LPGENW("Authorization requests"),
+ L"[b]%N, %t:[/b]%n%M", 0, 0,
+ { vfGlobal, vfEvent, vfSystem, vfAuth, 0 } },
+ { "tpl/msglog/added", LPGENW("Message log"), IDI_UNKNOWN, LPGENW("'You were added' events"),
+ L"[b]%N, %t:[/b]%n%M", 0, 0,
+ { vfGlobal, vfEvent, vfSystem, vfAdded, 0 } },
+ { "tpl/msglog/deleted", LPGENW("Message log"), IDI_UNKNOWN, LPGENW("'You were deleted' events"),
+ L"[b]%N, %t:[/b]%n%M", 0, 0,
+ { vfGlobal, vfEvent, vfSystem, vfDeleted, 0 } },
+
+ { "tpl/copy/msg", LPGENW("Clipboard"), IDI_SENDMSG, LPGENW("Messages"),
+ L"%N, %t:\x0d\x0a%M%n", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfMessage, 0 } },
+ { "tpl/copy/file", LPGENW("Clipboard"), -SKINICON_EVENT_FILE, LPGENW("Files"),
+ L"%N, %t:\x0d\x0a%M%n", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfFile, 0 } },
+ { "tpl/copy/status", LPGENW("Clipboard"), IDI_SIGNIN, LPGENW("Status changes"),
+ L"%N, %t:\x0d\x0a%M%n", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfSign, 0 } },
+ { "tpl/copy/presence", LPGENW("Clipboard"), IDI_UNKNOWN, LPGENW("Presence requests"),
+ L"%N, %t:\x0d\x0a%M%n", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfPresence, 0 } },
+ { "tpl/copy/other", LPGENW("Clipboard"), IDI_UNKNOWN, LPGENW("Other events"),
+ L"%N, %t:\x0d\x0a%M%n", 0, 0,
+ { vfGlobal, vfContact, vfEvent, vfOther, 0 } },
+
+ { "tpl/copy/authrq", LPGENW("Clipboard"), IDI_UNKNOWN, LPGENW("Authorization requests"),
+ L"%N, %t:\x0d\x0a%M%n", 0, 0,
+ { vfGlobal, vfEvent, vfSystem, vfAuth, 0 } },
+ { "tpl/copy/added", LPGENW("Clipboard"), IDI_UNKNOWN, LPGENW("'You were added' events"),
+ L"%N, %t:\x0d\x0a%M%n", 0, 0,
+ { vfGlobal, vfEvent, vfSystem, vfAdded, 0 } },
+ { "tpl/copy/deleted", LPGENW("Clipboard"), IDI_UNKNOWN, LPGENW("'You were deleted' events"),
+ L"%N, %t:\x0d\x0a%M%n", 0, 0,
+ { vfGlobal, vfEvent, vfSystem, vfDeleted, 0 } }
+};
+
+void LoadTemplates()
+{
+ HDC hdc = GetDC(nullptr);
+ g_iPixelY = GetDeviceCaps(hdc, LOGPIXELSY);
+ ReleaseDC(nullptr, hdc);
+
+ for (auto &it : templates)
+ replaceStrW(it.value, g_plugin.getWStringA(it.setting));
+}
+
+void SaveTemplates()
+{
+ for (auto &it : templates) {
+ if (it.value) {
+ if (mir_wstrcmp(it.value, it.defvalue))
+ g_plugin.setWString(it.setting, it.value);
+ else
+ g_plugin.delSetting(it.setting);
+ }
+ else g_plugin.delSetting(it.setting);
+ }
+}
diff --git a/plugins/NewStory/src/templates.h b/plugins/NewStory/src/templates.h
index ffe904c16e..ad16b646ef 100644
--- a/plugins/NewStory/src/templates.h
+++ b/plugins/NewStory/src/templates.h
@@ -1,73 +1,73 @@
-#ifndef __templates_h__
-#define __templates_h__
-
-struct TemplateVars
-{
- TemplateVars();
- ~TemplateVars();
-
- struct {
- wchar_t *val;
- bool del;
- }
- vars[256];
-
- __forceinline wchar_t* GetVar(uint8_t id) {
- return vars[id].val;
- }
-
- void SetNick(wchar_t *v);
- void SetVar(uint8_t id, wchar_t *v, bool d);
-};
-
-typedef void (*VarFunc)(TemplateVars *vars, MCONTACT hContact, ItemData *item);
-
-struct TemplateInfo
-{
- char* setting;
- wchar_t* group;
- int iIcon;
- wchar_t* title;
- wchar_t* defvalue;
- wchar_t* value;
- wchar_t* tmpValue;
- VarFunc vf[6];
-
- HICON getIcon() const;
-};
-
-enum
-{
- TPL_TITLE,
-
- TPL_MESSAGE,
- TPL_MSG_HEAD,
- TPL_MSG_GRP,
- TPL_FILE,
- TPL_SIGN,
- TPL_PRESENCE,
- TPL_OTHER,
-
- TPL_AUTH,
- TPL_ADDED,
- TPL_DELETED,
-
- TPL_COPY_MESSAGE,
- TPL_COPY_FILE,
- TPL_COPY_SIGN,
- TPL_COPY_OTHER,
-
- TPL_COPY_AUTH,
- TPL_COPY_ADDED,
- TPL_COPY_DELETED,
- TPL_COPY_PRESENCE,
-
- TPL_COUNT
-};
-
-extern TemplateInfo templates[TPL_COUNT];
-
-void LoadTemplates();
-void SaveTemplates();
-
+#ifndef __templates_h__
+#define __templates_h__
+
+struct TemplateVars
+{
+ TemplateVars();
+ ~TemplateVars();
+
+ struct {
+ wchar_t *val;
+ bool del;
+ }
+ vars[256];
+
+ __forceinline wchar_t* GetVar(uint8_t id) {
+ return vars[id].val;
+ }
+
+ void SetNick(wchar_t *v);
+ void SetVar(uint8_t id, wchar_t *v, bool d);
+};
+
+typedef void (*VarFunc)(TemplateVars *vars, MCONTACT hContact, ItemData *item);
+
+struct TemplateInfo
+{
+ char* setting;
+ wchar_t* group;
+ int iIcon;
+ wchar_t* title;
+ wchar_t* defvalue;
+ wchar_t* value;
+ wchar_t* tmpValue;
+ VarFunc vf[6];
+
+ HICON getIcon() const;
+};
+
+enum
+{
+ TPL_TITLE,
+
+ TPL_MESSAGE,
+ TPL_MSG_HEAD,
+ TPL_MSG_GRP,
+ TPL_FILE,
+ TPL_SIGN,
+ TPL_PRESENCE,
+ TPL_OTHER,
+
+ TPL_AUTH,
+ TPL_ADDED,
+ TPL_DELETED,
+
+ TPL_COPY_MESSAGE,
+ TPL_COPY_FILE,
+ TPL_COPY_SIGN,
+ TPL_COPY_OTHER,
+
+ TPL_COPY_AUTH,
+ TPL_COPY_ADDED,
+ TPL_COPY_DELETED,
+ TPL_COPY_PRESENCE,
+
+ TPL_COUNT
+};
+
+extern TemplateInfo templates[TPL_COUNT];
+
+void LoadTemplates();
+void SaveTemplates();
+
#endif // __templates_h__ \ No newline at end of file
diff --git a/plugins/NewStory/src/utils.cpp b/plugins/NewStory/src/utils.cpp
index fc42f07f43..732728bf0b 100644
--- a/plugins/NewStory/src/utils.cpp
+++ b/plugins/NewStory/src/utils.cpp
@@ -1,116 +1,111 @@
-/*
-Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
-Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
-
-uint32_t toggleBit(uint32_t dw, uint32_t bit)
-{
- if (dw & bit)
- return dw & ~bit;
- return dw | bit;
-}
-
-bool CheckFilter(wchar_t *buf, wchar_t *filter)
-{
- // MessageBox(0, buf, filter, MB_OK);
- int l1 = (int)mir_wstrlen(buf);
- int l2 = (int)mir_wstrlen(filter);
- for (int i = 0; i < l1 - l2 + 1; i++)
- if (CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, buf + i, l2, filter, l2) == CSTR_EQUAL)
- return true;
- return false;
-}
-
-int GetFontHeight(const LOGFONTA &lf)
-{
- return 2 * abs(lf.lfHeight) * 74 / g_iPixelY;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-struct
-{
- wchar_t *pStart, *pEnd;
- size_t cbStart, cbEnd;
-}
-static bbcodes[] =
-{
- { L"[b]", nullptr },
- { L"[/b]", nullptr },
- { L"[i]", nullptr },
- { L"[/i]", nullptr },
- { L"[u]", nullptr },
- { L"[/u]", nullptr },
- { L"[s]", nullptr },
- { L"[/s]", nullptr },
-
- { L"[color=", L"]" },
- { L"[/color]", nullptr },
- { L"[c0]", nullptr },
- { L"[c1]", nullptr },
- { L"[c2]", nullptr },
- { L"[c3]", nullptr },
- { L"[c4]", nullptr },
- { L"[c5]", nullptr },
- { L"[c6]", nullptr },
-
- { L"[$hicon=", L"$]" },
-
- { L"[url]", L"[/url]" },
- { L"[url=", L"]", },
- { L"[img]", L"[/img]" },
- { L"[img=", L"]" },
-};
-
-void RemoveBbcodes(CMStringW &wszText)
-{
- if (wszText.IsEmpty())
- return;
-
- if (bbcodes[0].cbStart == 0)
- for (auto &it : bbcodes) {
- it.cbStart = wcslen(it.pStart);
- if (it.pEnd)
- it.cbEnd = wcslen(it.pEnd);
- }
-
- for (int idx = wszText.Find('[', 0); idx != -1; idx = wszText.Find('[', idx)) {
- bool bFound = false;
- for (auto &it : bbcodes) {
- if (wcsncmp(wszText.c_str() + idx, it.pStart, it.cbStart))
- continue;
-
- wszText.Delete(idx, (int)it.cbStart);
-
- if (it.pEnd) {
- int idx2 = wszText.Find(it.pEnd, idx);
- if (idx2 != -1) {
- wszText.Delete(idx, idx2 - idx);
- wszText.Delete(idx, (int)it.cbEnd);
- }
- }
-
- bFound = true;
- break;
- }
-
- // just an occasional square bracket? skip it
- if (!bFound)
- idx++;
- }
-}
+/*
+Copyright (c) 2005 Victor Pavlychko (nullbyte@sotline.net.ua)
+Copyright (C) 2012-24 Miranda NG team (https://miranda-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"
+
+uint32_t toggleBit(uint32_t dw, uint32_t bit)
+{
+ if (dw & bit)
+ return dw & ~bit;
+ return dw | bit;
+}
+
+bool CheckFilter(wchar_t *buf, wchar_t *filter)
+{
+ // MessageBox(0, buf, filter, MB_OK);
+ int l1 = (int)mir_wstrlen(buf);
+ int l2 = (int)mir_wstrlen(filter);
+ for (int i = 0; i < l1 - l2 + 1; i++)
+ if (CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, buf + i, l2, filter, l2) == CSTR_EQUAL)
+ return true;
+ return false;
+}
+
+int GetFontHeight(const LOGFONTA &lf)
+{
+ return 2 * abs(lf.lfHeight) * 74 / g_iPixelY;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct
+{
+ wchar_t *pStart, *pEnd;
+ size_t cbStart, cbEnd;
+}
+static bbcodes[] =
+{
+ { L"[b]", nullptr },
+ { L"[/b]", nullptr },
+ { L"[i]", nullptr },
+ { L"[/i]", nullptr },
+ { L"[u]", nullptr },
+ { L"[/u]", nullptr },
+ { L"[s]", nullptr },
+ { L"[/s]", nullptr },
+
+ { L"[color=", L"]" },
+ { L"[/color]", nullptr },
+ { L"[c0]", nullptr },
+ { L"[c1]", nullptr },
+
+ { L"[$hicon=", L"$]" },
+
+ { L"[url]", L"[/url]" },
+ { L"[url=", L"]", },
+ { L"[img]", L"[/img]" },
+ { L"[img=", L"]" },
+};
+
+void RemoveBbcodes(CMStringW &wszText)
+{
+ if (wszText.IsEmpty())
+ return;
+
+ if (bbcodes[0].cbStart == 0)
+ for (auto &it : bbcodes) {
+ it.cbStart = wcslen(it.pStart);
+ if (it.pEnd)
+ it.cbEnd = wcslen(it.pEnd);
+ }
+
+ for (int idx = wszText.Find('[', 0); idx != -1; idx = wszText.Find('[', idx)) {
+ bool bFound = false;
+ for (auto &it : bbcodes) {
+ if (wcsncmp(wszText.c_str() + idx, it.pStart, it.cbStart))
+ continue;
+
+ wszText.Delete(idx, (int)it.cbStart);
+
+ if (it.pEnd) {
+ int idx2 = wszText.Find(it.pEnd, idx);
+ if (idx2 != -1) {
+ wszText.Delete(idx, idx2 - idx);
+ wszText.Delete(idx, (int)it.cbEnd);
+ }
+ }
+
+ bFound = true;
+ break;
+ }
+
+ // just an occasional square bracket? skip it
+ if (!bFound)
+ idx++;
+ }
+}
diff --git a/plugins/NewStory/src/utils.h b/plugins/NewStory/src/utils.h
index adb479e3f9..7dfa5f7d24 100644
--- a/plugins/NewStory/src/utils.h
+++ b/plugins/NewStory/src/utils.h
@@ -1,12 +1,12 @@
-struct ItemData;
-struct NewstoryListData;
-
-uint32_t toggleBit(uint32_t dw, uint32_t bit);
-bool CheckFilter(wchar_t *buf, wchar_t *filter);
-
-HMENU NSMenu_Build(NewstoryListData *data, ItemData *item);
-bool NSMenu_Process(int iCommand, NewstoryListData *data);
-
-int GetFontHeight(const LOGFONTA &lf);
-
-void RemoveBbcodes(CMStringW &pwszText);
+struct ItemData;
+struct NewstoryListData;
+
+uint32_t toggleBit(uint32_t dw, uint32_t bit);
+bool CheckFilter(wchar_t *buf, wchar_t *filter);
+
+HMENU NSMenu_Build(NewstoryListData *data, ItemData *item);
+bool NSMenu_Process(int iCommand, NewstoryListData *data);
+
+int GetFontHeight(const LOGFONTA &lf);
+
+void RemoveBbcodes(CMStringW &pwszText);