diff options
author | dartraiden <wowemuh@gmail.com> | 2024-02-19 20:13:12 +0300 |
---|---|---|
committer | dartraiden <wowemuh@gmail.com> | 2024-02-19 20:16:52 +0300 |
commit | 2f47988d1ef6672929762fba39fa69ef1b4dfa7a (patch) | |
tree | baa3433b11b1c44ab5a12c919ab004a24d645043 /plugins/NewStory/src | |
parent | f50885f33f7d1e129d719f06a43cc73518e0ffb1 (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.cpp | 128 | ||||
-rw-r--r-- | plugins/NewStory/src/fonts.cpp | 264 | ||||
-rw-r--r-- | plugins/NewStory/src/fonts.h | 114 | ||||
-rw-r--r-- | plugins/NewStory/src/history.h | 84 | ||||
-rw-r--r-- | plugins/NewStory/src/history_array.cpp | 1440 | ||||
-rw-r--r-- | plugins/NewStory/src/history_array.h | 328 | ||||
-rw-r--r-- | plugins/NewStory/src/history_control.cpp | 3082 | ||||
-rw-r--r-- | plugins/NewStory/src/history_control.h | 196 | ||||
-rw-r--r-- | plugins/NewStory/src/history_dlg.cpp | 2446 | ||||
-rw-r--r-- | plugins/NewStory/src/history_log.cpp | 232 | ||||
-rw-r--r-- | plugins/NewStory/src/history_menus.cpp | 602 | ||||
-rw-r--r-- | plugins/NewStory/src/history_svc.cpp | 124 | ||||
-rw-r--r-- | plugins/NewStory/src/main.cpp | 406 | ||||
-rw-r--r-- | plugins/NewStory/src/options.cpp | 669 | ||||
-rw-r--r-- | plugins/NewStory/src/resource.h | 257 | ||||
-rw-r--r-- | plugins/NewStory/src/stdafx.h | 236 | ||||
-rw-r--r-- | plugins/NewStory/src/templates.cpp | 946 | ||||
-rw-r--r-- | plugins/NewStory/src/templates.h | 144 | ||||
-rw-r--r-- | plugins/NewStory/src/utils.cpp | 227 | ||||
-rw-r--r-- | plugins/NewStory/src/utils.h | 24 |
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, ×tamp); /* 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, ×tamp); /* 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)⁢ - 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)⁢
+ 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);
|