summaryrefslogtreecommitdiff
path: root/plugins/BasicHistory/src
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-07-05 13:27:02 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-07-05 13:27:02 +0000
commit3a56ba391bf176c11cc5bde6f860759a3ce4477c (patch)
treee6e82e3d87e43303e1e01fb2b206a812f3683af6 /plugins/BasicHistory/src
parentcde14766d167f10dbad62c66acc0c7cc9d62f518 (diff)
BasicHistory: folder structure change
git-svn-id: http://svn.miranda-ng.org/main/trunk@773 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/BasicHistory/src')
-rw-r--r--plugins/BasicHistory/src/BasicHistory.cpp425
-rw-r--r--plugins/BasicHistory/src/BinaryExport.cpp231
-rw-r--r--plugins/BasicHistory/src/BinaryExport.h47
-rw-r--r--plugins/BasicHistory/src/DatExport.cpp214
-rw-r--r--plugins/BasicHistory/src/DatExport.h53
-rw-r--r--plugins/BasicHistory/src/EventList.cpp907
-rw-r--r--plugins/BasicHistory/src/EventList.h161
-rw-r--r--plugins/BasicHistory/src/ExportManager.cpp391
-rw-r--r--plugins/BasicHistory/src/ExportManager.h59
-rw-r--r--plugins/BasicHistory/src/HistoryWindow.cpp2572
-rw-r--r--plugins/BasicHistory/src/HistoryWindow.h95
-rw-r--r--plugins/BasicHistory/src/HotkeyHelper.cpp105
-rw-r--r--plugins/BasicHistory/src/HotkeyHelper.h24
-rw-r--r--plugins/BasicHistory/src/IExport.h49
-rw-r--r--plugins/BasicHistory/src/IImport.h53
-rw-r--r--plugins/BasicHistory/src/ImageDataObject.cpp171
-rw-r--r--plugins/BasicHistory/src/ImageDataObject.h133
-rw-r--r--plugins/BasicHistory/src/Options.cpp2170
-rw-r--r--plugins/BasicHistory/src/Options.h199
-rw-r--r--plugins/BasicHistory/src/PlainHtmlExport.cpp106
-rw-r--r--plugins/BasicHistory/src/PlainHtmlExport.h37
-rw-r--r--plugins/BasicHistory/src/RichHtmlExport.cpp545
-rw-r--r--plugins/BasicHistory/src/RichHtmlExport.h44
-rw-r--r--plugins/BasicHistory/src/Scheduler.cpp1576
-rw-r--r--plugins/BasicHistory/src/SearchContext.h55
-rw-r--r--plugins/BasicHistory/src/Searcher.cpp388
-rw-r--r--plugins/BasicHistory/src/Searcher.h119
-rw-r--r--plugins/BasicHistory/src/TxtExport.cpp65
-rw-r--r--plugins/BasicHistory/src/TxtExport.h38
-rw-r--r--plugins/BasicHistory/src/codecvt_CodePage.h98
-rw-r--r--plugins/BasicHistory/src/dllmain.cpp14
-rw-r--r--plugins/BasicHistory/src/resource.h157
-rw-r--r--plugins/BasicHistory/src/stdafx.cpp8
-rw-r--r--plugins/BasicHistory/src/stdafx.h88
-rw-r--r--plugins/BasicHistory/src/targetver.h8
-rw-r--r--plugins/BasicHistory/src/version.h20
36 files changed, 11425 insertions, 0 deletions
diff --git a/plugins/BasicHistory/src/BasicHistory.cpp b/plugins/BasicHistory/src/BasicHistory.cpp
new file mode 100644
index 0000000000..e28278e68b
--- /dev/null
+++ b/plugins/BasicHistory/src/BasicHistory.cpp
@@ -0,0 +1,425 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+#include "version.h"
+#include "HistoryWindow.h"
+#include "resource.h"
+#include "Options.h"
+
+// {E25367A2-51AE-4044-BE28-131BC18B71A4}
+#define MIID_BASICHISTORY { 0xe25367a2, 0x51ae, 0x4044, { 0xbe, 0x28, 0x13, 0x1b, 0xc1, 0x8b, 0x71, 0xa4 } }
+
+#define MS_HISTORY_DELETEALLCONTACTHISTORY "BasicHistory/DeleteAllContactHistory"
+#define MS_HISTORY_EXECUTE_TASK "BasicHistory/ExecuteTask"
+
+HCURSOR hCurSplitNS, hCurSplitWE;
+HANDLE g_hMainThread=NULL;
+
+extern HINSTANCE hInst;
+
+HANDLE hModulesLoaded, hOptionsInit, hPrebuildContactMenu, hServiceShowContactHistory, hServiceDeleteAllContactHistory, hServiceExecuteTask, hPreShutdownHistoryModule, hHistoryContactDelete, hFontsChanged,hToolBarLoaded, hSysOK;
+HANDLE *hEventIcons = NULL;
+int iconsNum;
+HANDLE hPlusIcon, hMinusIcon, hFindNextIcon, hFindPrevIcon;
+HANDLE hPlusExIcon, hMinusExIcon;
+HANDLE hToolbarButton;
+HGENMENU hContactMenu, hDeleteContactMenu;
+HGENMENU hTaskMainMenu;
+std::vector<HGENMENU> taskMenus;
+bool g_SmileyAddAvail = false;
+char* metaContactProto = NULL;
+const IID IID_ITextDocument={0x8CC497C0, 0xA1DF, 0x11ce, {0x80, 0x98, 0x00, 0xAA, 0x00, 0x47, 0xBE, 0x5D}};
+
+#define MODULE "BasicHistory"
+
+PLUGININFOEX pluginInfo =
+{
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __AUTHOREMAIL,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ MIID_BASICHISTORY
+};
+
+TIME_API tmi = {0};
+int hLangpack = 0;
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+static const MUUID interfaces[] = {MIID_UIHISTORY, MIID_LAST};
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+void InitScheduler();
+void DeinitScheduler();
+int DoLastTask(WPARAM, LPARAM);
+INT_PTR ExecuteTaskService(WPARAM wParam, LPARAM lParam);
+
+int PrebuildContactMenu(WPARAM wParam, LPARAM lParam)
+{
+ int count = EventList::GetContactMessageNumber((HANDLE)wParam);
+ bool isInList = HistoryWindow::IsInList(GetForegroundWindow());
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS;
+
+ if (!count) mi.flags |= CMIF_HIDDEN;
+ else mi.flags &= ~CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hContactMenu, (LPARAM)&mi);
+
+ mi.flags = CMIM_FLAGS;
+ if (!count || !isInList) mi.flags |= CMIF_HIDDEN;
+ else mi.flags &= ~CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hDeleteContactMenu, (LPARAM)&mi);
+
+ return 0;
+}
+
+int ToolbarModuleLoaded(WPARAM wParam,LPARAM lParam)
+{
+ if(ServiceExists(MS_TB_ADDBUTTON))
+ {
+ TBButton tbb = {0};
+ tbb.cbSize = sizeof(tbb);
+ tbb.pszButtonID = "open_history";
+ tbb.pszButtonName = LPGEN("Open History");
+ tbb.pszServiceName = MS_HISTORY_SHOWCONTACTHISTORY;
+ tbb.lParam = 0;
+ tbb.pszTooltipUp = LPGEN("Open History");
+ tbb.pszTooltipDn = LPGEN("Open History");
+ tbb.defPos = 200;
+ tbb.tbbFlags = TBBF_SHOWTOOLTIP;
+ tbb.hPrimaryIconHandle = LoadSkinnedIconHandle(SKINICON_OTHER_HISTORY);
+ tbb.hSecondaryIconHandle = LoadSkinnedIconHandle(SKINICON_OTHER_HISTORY);
+ hToolbarButton = (HANDLE) CallService(MS_TB_ADDBUTTON,0, (LPARAM)&tbb);
+ }
+ return 0;
+}
+
+void InitMenuItems()
+{
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.position = 1000090000;
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = LoadSkinnedIconHandle(SKINICON_OTHER_HISTORY);
+ mi.pszName = LPGEN("View &History");
+ mi.pszService = MS_HISTORY_SHOWCONTACTHISTORY;
+ hContactMenu = Menu_AddContactMenuItem(&mi);
+
+ mi.position = 500060000;
+ mi.pszService = MS_HISTORY_SHOWCONTACTHISTORY;
+ Menu_AddMainMenuItem(&mi);
+
+ mi.position = 1000090001;
+ mi.flags = CMIF_ICONFROMICOLIB;
+ mi.icolibItem = LoadSkinnedIconHandle(SKINICON_OTHER_DELETE);
+ mi.pszName = LPGEN("Delete All User History");
+ mi.pszService = MS_HISTORY_DELETEALLCONTACTHISTORY;
+ hDeleteContactMenu = Menu_AddContactMenuItem(&mi);
+
+ hPrebuildContactMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PrebuildContactMenu);
+}
+
+void InitTaskMenuItems()
+{
+ if(Options::instance->taskOptions.size() > 0)
+ {
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ if(hTaskMainMenu == NULL)
+ {
+ mi.position = 500060005;
+ mi.flags = CMIF_ROOTPOPUP | CMIF_ICONFROMICOLIB;
+ mi.icolibItem = LoadSkinnedIconHandle(SKINICON_OTHER_HISTORY);
+ mi.pszName = LPGEN("Execute history task");
+ hTaskMainMenu = Menu_AddMainMenuItem(&mi);
+ }
+
+ std::vector<TaskOptions>::iterator taskIt = Options::instance->taskOptions.begin();
+ std::vector<HGENMENU>::iterator it = taskMenus.begin();
+ for(; it != taskMenus.end() && taskIt != Options::instance->taskOptions.end(); ++it, ++taskIt)
+ {
+ memset(&mi, 0, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS | CMIM_NAME | CMIF_CHILDPOPUP | CMIF_ROOTHANDLE | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
+ mi.hParentMenu = hTaskMainMenu;
+ mi.ptszName = (TCHAR*)taskIt->taskName.c_str();
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)(HGENMENU)*it, (LPARAM)&mi);
+ }
+
+ for(; it != taskMenus.end(); ++it)
+ {
+ memset(&mi, 0, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS | CMIF_CHILDPOPUP | CMIF_ROOTHANDLE | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED | CMIF_HIDDEN;
+ mi.hParentMenu = hTaskMainMenu;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)(HGENMENU)*it, (LPARAM)&mi);
+ }
+
+ int pos = (int)taskMenus.size();
+ for(; taskIt != Options::instance->taskOptions.end(); ++taskIt)
+ {
+ memset(&mi, 0, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_CHILDPOPUP | CMIF_ROOTHANDLE | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
+ mi.pszService = MS_HISTORY_EXECUTE_TASK;
+ mi.hParentMenu = hTaskMainMenu;
+ mi.popupPosition = pos++;
+ mi.ptszName = (TCHAR*)taskIt->taskName.c_str();
+ HGENMENU menu = Menu_AddMainMenuItem(&mi);
+ taskMenus.push_back(menu);
+ }
+ }
+ else if(hTaskMainMenu != NULL)
+ {
+ CLISTMENUITEM mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_FLAGS | CMIF_ROOTPOPUP | CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hTaskMainMenu, (LPARAM)&mi);
+ }
+}
+
+void InitIcolib()
+{
+ TCHAR stzFile[MAX_PATH];
+
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(sid);
+ sid.cx = sid.cy = 16;
+ sid.ptszDefaultFile = stzFile;
+ sid.ptszSection = LPGENT("History");
+ sid.flags = SIDF_ALL_TCHAR;
+
+ GetModuleFileName(hInst, stzFile, MAX_PATH);
+
+ iconsNum = 3;
+ hEventIcons = new HANDLE[iconsNum];
+ sid.pszName = "BasicHistory_in";
+ sid.ptszDescription = LPGENT("Incoming message");
+ sid.iDefaultIndex = -IDI_INM;
+ hEventIcons[0] = Skin_AddIcon(&sid);
+
+ sid.pszName = "BasicHistory_out";
+ sid.ptszDescription = LPGENT("Outgoing message");
+ sid.iDefaultIndex = -IDI_OUTM;
+ hEventIcons[1] = Skin_AddIcon(&sid);
+
+ sid.pszName = "BasicHistory_status";
+ sid.ptszDescription = LPGENT("Statuschange");
+ sid.iDefaultIndex = -IDI_STATUS;
+ hEventIcons[2] = Skin_AddIcon(&sid);
+
+ sid.pszName = "BasicHistory_show";
+ sid.ptszDescription = LPGENT("Show Contacts");
+ sid.iDefaultIndex = -IDI_SHOW;
+ hPlusIcon = Skin_AddIcon(&sid);
+
+ sid.pszName = "BasicHistory_hide";
+ sid.ptszDescription = LPGENT("Hide Contacts");
+ sid.iDefaultIndex = -IDI_HIDE;
+ hMinusIcon = Skin_AddIcon(&sid);
+
+ sid.pszName = "BasicHistory_findnext";
+ sid.ptszDescription = LPGENT("Find Next");
+ sid.iDefaultIndex = -IDI_FINDNEXT;
+ hFindNextIcon = Skin_AddIcon(&sid);
+
+ sid.pszName = "BasicHistory_findprev";
+ sid.ptszDescription = LPGENT("Find Previous");
+ sid.iDefaultIndex = -IDI_FINDPREV;
+ hFindPrevIcon = Skin_AddIcon(&sid);
+
+ sid.pszName = "BasicHistory_plusex";
+ sid.ptszDescription = LPGENT("Plus in export");
+ sid.iDefaultIndex = -IDI_PLUSEX;
+ hPlusExIcon = Skin_AddIcon(&sid);
+
+ sid.pszName = "BasicHistory_minusex";
+ sid.ptszDescription = LPGENT("Minus in export");
+ sid.iDefaultIndex = -IDI_MINUSEX;
+ hMinusExIcon = Skin_AddIcon(&sid);
+}
+
+void InitUpdater()
+{
+ if (ServiceExists(MS_UPDATE_REGISTER))
+ {
+ Update update = {0};
+ char szVersion[16];
+ update.cbSize = sizeof(Update);
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionStringPluginEx(&pluginInfo, szVersion);
+ update.cpbVersion = (int)strlen((char *)update.pbVersion);
+
+#ifdef _WIN64
+ update.szUpdateURL = "http://programista.free.of.pl/miranda/BasicHistory64.zip";
+ update.szVersionURL = "http://programista.free.of.pl/miranda/pluginversion.php?plugin=basichistory&x64=yes";
+ update.szBetaUpdateURL = "http://programista.free.of.pl/miranda/BasicHistoryBeta64.zip";
+ update.szBetaVersionURL = "http://programista.free.of.pl/miranda/pluginversion.php?plugin=basichistory&beta=yes&x64=yes";
+ update.szBetaChangelogURL = "http://programista.free.of.pl/miranda/BasicHistoryChangelog.txt";
+#else
+ update.szUpdateURL = "http://programista.free.of.pl/miranda/BasicHistory.zip";
+ update.szVersionURL = "http://programista.free.of.pl/miranda/pluginversion.php?plugin=basichistory";
+ update.szBetaUpdateURL = "http://programista.free.of.pl/miranda/BasicHistoryBeta.zip";
+ update.szBetaVersionURL = "http://programista.free.of.pl/miranda/pluginversion.php?plugin=basichistory&beta=yes";
+ update.szBetaChangelogURL = "http://programista.free.of.pl/miranda/BasicHistoryChangelog.txt";
+
+#endif
+ update.pbBetaVersionPrefix = update.pbVersionPrefix = (BYTE *)"Basic History ";
+ update.cpbBetaVersionPrefix = update.cpbVersionPrefix = (int)strlen((char *)update.pbVersionPrefix);
+ CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update);
+ }
+}
+
+INT_PTR ShowContactHistory(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ HistoryWindow::Open(hContact);
+ return 0;
+}
+
+int PreShutdownHistoryModule(WPARAM, LPARAM)
+{
+ HistoryWindow::Deinit();
+ DeinitScheduler();
+ return 0;
+}
+
+int HistoryContactDelete(WPARAM wParam, LPARAM)
+{
+ HistoryWindow::Close((HANDLE)wParam);
+ return 0;
+}
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ InitMenuItems();
+ InitUpdater();
+
+ TCHAR ftpExe[MAX_PATH];
+ if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, ftpExe)))
+ {
+ _tcscat_s(ftpExe, _T("\\WinSCP\\WinSCP.exe"));
+ DWORD atr = GetFileAttributes(ftpExe);
+ if(atr == INVALID_FILE_ATTRIBUTES || atr & FILE_ATTRIBUTE_DIRECTORY)
+ {
+#ifdef _WIN64
+ if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILESX86, NULL, SHGFP_TYPE_CURRENT, ftpExe)))
+ {
+ _tcscat_s(ftpExe, _T("\\WinSCP\\WinSCP.exe"));
+ atr = GetFileAttributes(ftpExe);
+ if(!(atr == INVALID_FILE_ATTRIBUTES || atr & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ Options::instance->ftpExePathDef = ftpExe;
+ }
+ }
+#endif
+ }
+ else
+ Options::instance->ftpExePathDef = ftpExe;
+ }
+
+ TCHAR* log = _T("%miranda_logpath%\\BasicHistory\\ftplog.txt");
+ TCHAR* logAbsolute = Utils_ReplaceVarsT(log);
+ Options::instance->ftpLogPath = logAbsolute;
+ mir_free(logAbsolute);
+ Options::instance->Load();
+ InitTaskMenuItems();
+
+ hPreShutdownHistoryModule = HookEvent(ME_SYSTEM_PRESHUTDOWN,PreShutdownHistoryModule);
+ hHistoryContactDelete = HookEvent(ME_DB_CONTACT_DELETED,HistoryContactDelete);
+ hFontsChanged = HookEvent(ME_FONT_RELOAD, HistoryWindow::FontsChanged);
+ hSysOK = HookEvent(ME_SYSTEM_OKTOEXIT, DoLastTask);
+ if (ServiceExists(MS_SMILEYADD_REPLACESMILEYS))
+ {
+ g_SmileyAddAvail = true;
+ }
+ if (ServiceExists(MS_MC_GETPROTOCOLNAME))
+ {
+ metaContactProto = (char*)CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+ }
+
+ InitScheduler();
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Load(void)
+{
+ hTaskMainMenu = NULL;
+ DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),&g_hMainThread,0,FALSE,DUPLICATE_SAME_ACCESS);
+ mir_getTMI(&tmi);
+ mir_getLP(&pluginInfo);
+ hCurSplitNS = LoadCursor(NULL, IDC_SIZENS);
+ hCurSplitWE = LoadCursor(NULL, IDC_SIZEWE);
+ hServiceShowContactHistory = CreateServiceFunction(MS_HISTORY_SHOWCONTACTHISTORY, ShowContactHistory);
+ hServiceDeleteAllContactHistory = CreateServiceFunction(MS_HISTORY_DELETEALLCONTACTHISTORY, HistoryWindow::DeleteAllUserHistory);
+ hServiceExecuteTask = CreateServiceFunction(MS_HISTORY_EXECUTE_TASK, ExecuteTaskService);
+ Options::instance = new Options();
+ hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hOptionsInit = HookEvent(ME_OPT_INITIALISE, Options::InitOptions);
+ hToolBarLoaded = HookEvent(ME_TB_MODULELOADED,ToolbarModuleLoaded);
+ EventList::Init();
+ InitIcolib();
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ if(g_hMainThread) CloseHandle(g_hMainThread);
+ g_hMainThread=NULL;
+ UnhookEvent(hModulesLoaded);
+ UnhookEvent(hPrebuildContactMenu);
+ UnhookEvent(hPreShutdownHistoryModule);
+ UnhookEvent(hHistoryContactDelete);
+ UnhookEvent(hOptionsInit);
+ UnhookEvent(hFontsChanged);
+ UnhookEvent(hToolBarLoaded);
+ UnhookEvent(hSysOK);
+ DestroyServiceFunction(hServiceShowContactHistory);
+ DestroyServiceFunction(hServiceDeleteAllContactHistory);
+ DestroyServiceFunction(hServiceExecuteTask);
+ HistoryWindow::Deinit();
+ DestroyCursor(hCurSplitNS);
+ DestroyCursor(hCurSplitWE);
+ EventList::Deinit();
+ if(Options::instance != NULL)
+ {
+ Options::instance->Unload();
+ delete Options::instance;
+ Options::instance = NULL;
+ }
+
+ if(hEventIcons != NULL)
+ {
+ delete [] hEventIcons;
+ }
+
+ return 0;
+}
diff --git a/plugins/BasicHistory/src/BinaryExport.cpp b/plugins/BasicHistory/src/BinaryExport.cpp
new file mode 100644
index 0000000000..f57343cff0
--- /dev/null
+++ b/plugins/BasicHistory/src/BinaryExport.cpp
@@ -0,0 +1,231 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "BinaryExport.h"
+
+#define EXP_FILE (*((std::ofstream*)IExport::stream))
+#define IMP_FILE (*((std::ifstream*)IImport::stream))
+
+std::wstring GetProtocolName(HANDLE hContact);
+std::wstring GetContactId(HANDLE hContact);
+
+#pragma pack(push, 1)
+
+struct BinaryFileHeader
+{
+ unsigned char signature[4];
+ unsigned char version;
+ unsigned char extraFlags;
+ unsigned short int reserved;
+ unsigned int codepage;
+ unsigned short int dataStart;
+};
+
+struct BinaryFileMessageHeader
+{
+ DWORD timestamp;
+ WORD eventType;
+ WORD flags;
+};
+
+#pragma pack(pop)
+
+BinaryExport::~BinaryExport()
+{
+}
+
+void BinaryExport::WriteString(const std::wstring &str)
+{
+ int conv = WideCharToMultiByte(codepage, 0, str.c_str(), (int)str.length() + 1, NULL, 0, NULL, NULL);
+ char* buf = new char[conv];
+ conv = WideCharToMultiByte(codepage, 0, str.c_str(), (int)str.length() + 1, buf, conv, NULL, NULL);
+ EXP_FILE.write(buf, conv);
+ delete[] buf;
+}
+
+bool BinaryExport::ReadString(std::wstring &str)
+{
+ std::string buf;
+ int size = 1024;
+ int pos = 0;
+ int totalSize = 0;
+ while(1)
+ {
+ buf.resize(size);
+ if(IMP_FILE.peek() == 0)
+ {
+ IMP_FILE.get();
+ break;
+ }
+
+ IMP_FILE.get(((char*)buf.c_str()) + pos, size - pos, 0);
+ if(!IMP_FILE.good())
+ return false;
+
+ int readed = IMP_FILE.gcount();
+ totalSize += readed;
+ char end;
+ IMP_FILE.get(end);
+ if(!IMP_FILE.good())
+ return false;
+ if(end == 0)
+ break;
+ if(size - pos - 1 != readed)
+ return false;
+ buf[size - 1] = end;
+ ++totalSize;
+ size += 1024;
+ pos += 1024;
+ }
+
+ if(totalSize == 0)
+ return true;
+ int sizeW = MultiByteToWideChar(codepage, 0, (char*)buf.c_str(), totalSize, NULL, 0);
+ str.resize(sizeW);
+ MultiByteToWideChar(codepage, 0, (char*)buf.c_str(), totalSize, (wchar_t*)str.c_str(), sizeW);
+ return true;
+}
+
+void BinaryExport::WriteHeader(const std::wstring &fileName, const std::wstring &filterName, const std::wstring &myName, const std::wstring &myId, const std::wstring &name1, const std::wstring &proto1, const std::wstring &id1, const std::string& baseProto1, const std::wstring& encoding)
+{
+ BinaryFileHeader header;
+ memset(&header, 0, sizeof(BinaryFileHeader));
+ memcpy(header.signature, "BHBF", 4);
+ header.codepage = codepage = CP_UTF8;
+ EXP_FILE.write((char*)&header, sizeof(BinaryFileHeader));
+ WriteString(filterName);
+ WriteString(myName);
+ WriteString(myId);
+ WriteString(name1);
+ WriteString(proto1);
+ WriteString(id1);
+ size_t pos = EXP_FILE.tellp();
+ header.dataStart = (unsigned short)pos;
+ EXP_FILE.seekp(offsetof(BinaryFileHeader, dataStart), std::ios_base::beg);
+ EXP_FILE.write((char*)&(header.dataStart), sizeof(header.dataStart));
+ EXP_FILE.seekp(pos, std::ios_base::beg);
+ lTime = 0;
+}
+
+void BinaryExport::WriteFooter()
+{
+}
+
+void BinaryExport::WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText)
+{
+}
+
+void BinaryExport::WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei)
+{
+ if(dbei.timestamp >= lTime)
+ {
+ BinaryFileMessageHeader header;
+ header.eventType = dbei.eventType;
+ header.flags = dbei.flags & (~(0x800));
+ header.timestamp = dbei.timestamp;
+ EXP_FILE.write((char*)&header, sizeof(BinaryFileMessageHeader));
+ WriteString(message);
+ lTime = dbei.timestamp;
+ }
+}
+
+bool ReadHeader(BinaryFileHeader& header, std::istream* stream)
+{
+ stream->read((char*)&header, sizeof(BinaryFileHeader));
+ if(!stream->good())
+ return false;
+ if(memcmp(header.signature, "BHBF", 4) != 0)
+ return false;
+ if(header.version != 0 || header.codepage == 12000 || header.codepage == 12001)
+ return false;
+
+ return true;
+}
+
+int BinaryExport::IsContactInFile(const std::vector<HANDLE>& contacts)
+{
+ BinaryFileHeader header;
+ if(!ReadHeader(header, IImport::stream))
+ return -2;
+ codepage = header.codepage;
+ std::wstring filterName;
+ std::wstring myName;
+ std::wstring myId;
+ std::wstring name1;
+ std::wstring proto1;
+ std::wstring id1;
+ if(!ReadString(filterName))
+ return -2;
+ if(!ReadString(myName))
+ return -2;
+ if(!ReadString(myId))
+ return -2;
+ if(!ReadString(name1))
+ return -2;
+ if(!ReadString(proto1))
+ return -2;
+ if(!ReadString(id1))
+ return -2;
+
+ size_t pos = IMP_FILE.tellg();
+ if(header.dataStart < pos)
+ return -2;
+
+ IMP_FILE.seekg(0, std::ios_base::beg);
+ for(int i = 0; i < (int)contacts.size(); ++i)
+ {
+ std::wstring pn = GetProtocolName(contacts[i]);
+ std::wstring id = GetContactId(contacts[i]);
+ if(pn == proto1 && id == id1)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+bool BinaryExport::GetEventList(std::vector<IImport::ExternalMessage>& eventList)
+{
+ BinaryFileHeader header;
+ if(!ReadHeader(header, IImport::stream))
+ return false;
+ codepage = header.codepage;
+ IMP_FILE.seekg(header.dataStart, std::ios_base::beg);
+ BinaryFileMessageHeader messageHeader;
+ while(1)
+ {
+ IMP_FILE.read((char*)&messageHeader, sizeof(BinaryFileMessageHeader));
+ if(IMP_FILE.eof())
+ break;
+ if(!IMP_FILE.good())
+ return false;
+
+ IImport::ExternalMessage exMsg;
+ exMsg.eventType = messageHeader.eventType;
+ exMsg.flags = messageHeader.flags;
+ exMsg.timestamp = messageHeader.timestamp;
+ if(!ReadString(exMsg.message))
+ return false;
+
+ eventList.push_back(exMsg);
+ }
+
+ return true;
+}
diff --git a/plugins/BasicHistory/src/BinaryExport.h b/plugins/BasicHistory/src/BinaryExport.h
new file mode 100644
index 0000000000..386625226f
--- /dev/null
+++ b/plugins/BasicHistory/src/BinaryExport.h
@@ -0,0 +1,47 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+#include "IExport.h"
+#include "IImport.h"
+
+class BinaryExport :
+ public IExport,
+ public IImport
+{
+public:
+ virtual const TCHAR* GetExt()
+ {
+ return _T("bin");
+ }
+
+ virtual void WriteHeader(const std::wstring &fileName, const std::wstring &filterName, const std::wstring &myName, const std::wstring &myId, const std::wstring &name1, const std::wstring &proto1, const std::wstring &id1, const std::string& baseProto1, const std::wstring& encoding);
+ virtual void WriteFooter();
+ virtual void WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText);
+ virtual void WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei);
+ virtual int IsContactInFile(const std::vector<HANDLE>& contacts);
+ virtual bool GetEventList(std::vector<IImport::ExternalMessage>& eventList);
+
+ virtual ~BinaryExport();
+private:
+ unsigned int codepage;
+ inline void WriteString(const std::wstring &str);
+ inline bool ReadString(std::wstring &str);
+ DWORD lTime;
+};
+
diff --git a/plugins/BasicHistory/src/DatExport.cpp b/plugins/BasicHistory/src/DatExport.cpp
new file mode 100644
index 0000000000..bd74546393
--- /dev/null
+++ b/plugins/BasicHistory/src/DatExport.cpp
@@ -0,0 +1,214 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "DatExport.h"
+#include "EventList.h"
+
+#define EXP_FILE (*((std::ofstream*)IExport::stream))
+#define IMP_FILE (*((std::ifstream*)IImport::stream))
+
+std::wstring GetProtocolName(HANDLE hContact);
+std::wstring GetContactId(HANDLE hContact);
+
+#pragma pack(push, 1)
+
+struct MCHeader
+{
+ unsigned char signature[2];
+ unsigned int version;
+ unsigned int dataSize;
+};
+
+#pragma pack(pop)
+
+typedef struct {
+ int cbSize; //size of the structure in bytes
+ DWORD szModule; //pointer to name of the module that 'owns' this
+ //event, ie the one that is in control of the data format
+ DWORD timestamp; //seconds since 00:00, 01/01/1970. Gives us times until
+ //2106 unless you use the standard C library which is
+ //signed and can only do until 2038. In GMT.
+ DWORD flags; //the omnipresent flags
+ WORD eventType; //module-defined event type field
+ DWORD cbBlob; //size of pBlob in bytes
+ DWORD pBlob; //pointer to buffer containing module-defined event data
+} DBEVENTINFO86;
+
+
+DatExport::~DatExport()
+{
+}
+
+
+int DatExport::WriteString(const std::wstring &str)
+{
+ int conv = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), (int)str.length() + 1, NULL, 0, NULL, NULL);
+ if(conv > (int)memBuf.size())
+ {
+ memBuf.resize(conv);
+ }
+
+ conv = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), (int)str.length() + 1, (char*)memBuf.c_str(), conv, NULL, NULL);
+ return conv;
+}
+
+void DatExport::WriteHeader(const std::wstring &fileName, const std::wstring &filterName, const std::wstring &myName, const std::wstring &myId, const std::wstring &name1, const std::wstring &proto1, const std::wstring &id1, const std::string& baseProto1, const std::wstring& encoding)
+{
+ MCHeader header;
+ memset(&header, 0, sizeof(MCHeader));
+ memcpy(header.signature, "HB", 2);
+ header.version = -1;
+ header.dataSize = 0;
+ dataSize = 0;
+ EXP_FILE.write((char*)&header, sizeof(MCHeader));
+}
+
+void DatExport::WriteFooter()
+{
+ size_t pos = EXP_FILE.tellp();
+ EXP_FILE.seekp(offsetof(MCHeader, dataSize), std::ios_base::beg);
+ EXP_FILE.write((char*)&dataSize, sizeof(dataSize));
+ EXP_FILE.seekp(pos, std::ios_base::beg);
+ memBuf.resize(0);
+#ifdef _WIN64
+ memBuf.shrink_to_fit();
+#endif
+}
+
+void DatExport::WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText)
+{
+}
+
+void DatExport::WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei)
+{
+ DBEVENTINFO86 header;
+ header.cbSize = sizeof(DBEVENTINFO86);
+ header.eventType = dbei.eventType;
+ header.flags = dbei.flags & (~(0x800));
+ header.timestamp = dbei.timestamp;
+ header.szModule = 0;
+ header.pBlob = 0;
+ if(dbei.flags & 0x800)
+ {
+ //Imported
+ header.flags |= DBEF_UTF;
+ header.cbBlob = WriteString(message);
+ EXP_FILE.write((char*)&header, header.cbSize);
+ EXP_FILE.write(memBuf.c_str(), header.cbBlob);
+ }
+ else
+ {
+ //Internal
+ header.cbBlob = dbei.cbBlob;
+ EXP_FILE.write((char*)&header, header.cbSize);
+ EXP_FILE.write((char*)dbei.pBlob, header.cbBlob);
+ }
+
+ dataSize += header.cbSize + header.cbBlob;
+}
+
+bool ReadHeader(MCHeader& header, std::istream* stream)
+{
+ stream->read((char*)&header, sizeof(MCHeader));
+ if(!stream->good())
+ return false;
+ if(memcmp(header.signature, "HB", 2) != 0)
+ return false;
+
+ return true;
+}
+
+int DatExport::IsContactInFile(const std::vector<HANDLE>& contacts)
+{
+ MCHeader header;
+ if(!ReadHeader(header, IImport::stream))
+ return -2;
+
+ if(contacts.size() == 1)
+ {
+ hContact = contacts[0];
+ }
+
+ IMP_FILE.seekg(0, std::ios_base::beg);
+ return -3;
+}
+
+bool DatExport::GetEventList(std::vector<IImport::ExternalMessage>& eventList)
+{
+ MCHeader header;
+ if(!ReadHeader(header, IImport::stream))
+ return false;
+ dataSize = header.dataSize;
+ DBEVENTINFO86 messageHeader;
+ DBEVENTINFO info = {0};
+ info.cbSize = sizeof(DBEVENTINFO);
+ info.szModule = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ TCHAR _str[MAXSELECTSTR + 8]; // for safety reason
+ std::multimap<DWORD, IImport::ExternalMessage> sortedEvents;
+ while(dataSize > 0)
+ {
+ messageHeader.cbSize = 0;
+ IMP_FILE.read((char*)&messageHeader, sizeof(DBEVENTINFO86));
+ if(!IMP_FILE.good())
+ return false;
+
+ if(messageHeader.cbSize < sizeof(DBEVENTINFO86))
+ return false;
+
+ if(messageHeader.cbSize > sizeof(DBEVENTINFO86))
+ {
+ IMP_FILE.seekg(messageHeader.cbSize - sizeof(DBEVENTINFO86), std::ios_base::cur);
+ }
+
+ IImport::ExternalMessage exMsg;
+ exMsg.eventType = messageHeader.eventType;
+ exMsg.flags = messageHeader.flags;
+ exMsg.timestamp = messageHeader.timestamp;
+ if(messageHeader.cbBlob > memBuf.size())
+ {
+ memBuf.resize(messageHeader.cbBlob);
+ }
+
+ IMP_FILE.read((char*)memBuf.c_str(), messageHeader.cbBlob);
+ if(!IMP_FILE.good())
+ return false;
+
+ info.eventType = messageHeader.eventType;
+ info.flags = messageHeader.flags;
+ info.timestamp = messageHeader.timestamp;
+ info.cbBlob = messageHeader.cbBlob;
+ info.pBlob = (PBYTE)memBuf.c_str();
+ EventList::GetObjectDescription(&info, _str, MAXSELECTSTR);
+ exMsg.message = _str;
+ sortedEvents.insert(std::pair<DWORD, IImport::ExternalMessage>(messageHeader.timestamp, exMsg));
+ dataSize -= messageHeader.cbSize + messageHeader.cbBlob;
+ }
+
+ memBuf.resize(0);
+#ifdef _WIN64
+ memBuf.shrink_to_fit();
+#endif
+
+ for(std::multimap<DWORD, IImport::ExternalMessage>::iterator it = sortedEvents.begin(); it != sortedEvents.end(); ++it)
+ {
+ eventList.push_back(it->second);
+ }
+
+ return true;
+}
diff --git a/plugins/BasicHistory/src/DatExport.h b/plugins/BasicHistory/src/DatExport.h
new file mode 100644
index 0000000000..980a12d148
--- /dev/null
+++ b/plugins/BasicHistory/src/DatExport.h
@@ -0,0 +1,53 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+#include "IExport.h"
+#include "IImport.h"
+
+class DatExport :
+ public IExport,
+ public IImport
+{
+public:
+ virtual const TCHAR* GetExt()
+ {
+ return _T("dat");
+ }
+
+ virtual void WriteHeader(const std::wstring &fileName, const std::wstring &filterName, const std::wstring &myName, const std::wstring &myId, const std::wstring &name1, const std::wstring &proto1, const std::wstring &id1, const std::string& baseProto1, const std::wstring& encoding);
+ virtual void WriteFooter();
+ virtual void WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText);
+ virtual void WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei);
+ virtual int IsContactInFile(const std::vector<HANDLE>& contacts);
+ virtual bool GetEventList(std::vector<IImport::ExternalMessage>& eventList);
+
+ DatExport()
+ {
+ hContact = NULL;
+ }
+
+ virtual ~DatExport();
+private:
+ int dataSize;
+ HANDLE hContact;
+ std::string memBuf;
+ inline int WriteString(const std::wstring &str);
+ //inline bool ReadString(std::wstring &str, int size);
+};
+
diff --git a/plugins/BasicHistory/src/EventList.cpp b/plugins/BasicHistory/src/EventList.cpp
new file mode 100644
index 0000000000..8ac9e2eb37
--- /dev/null
+++ b/plugins/BasicHistory/src/EventList.cpp
@@ -0,0 +1,907 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "EventList.h"
+#include "Options.h"
+#include "ExportManager.h"
+#include <assert.h>
+
+extern int iconsNum;
+
+bool DeleteDirectory(LPCTSTR lpszDir, bool noRecycleBin = true);
+std::wstring GetName(const std::wstring &path);
+
+EventList::EventList()
+ :hWnd(NULL),
+ isWnd(false),
+ hContact(NULL),
+ deltaTime(0),
+ isFlat(false),
+ useImportedMessages(true)
+{
+ memset(&gdbei, 0, sizeof(DBEVENTINFO));
+ gdbei.cbSize = sizeof(DBEVENTINFO);
+ goldBlobSize = 0;
+}
+
+EventList::EventList(HANDLE _hContact, int filter)
+ :hWnd(NULL),
+ isWnd(false),
+ hContact(_hContact),
+ deltaTime(0),
+ isFlat(false),
+ useImportedMessages(true)
+{
+ memset(&gdbei, 0, sizeof(DBEVENTINFO));
+ gdbei.cbSize = sizeof(DBEVENTINFO);
+ goldBlobSize = 0;
+ SetDefFilter(filter);
+}
+
+
+EventList::~EventList()
+{
+ mir_free(gdbei.pBlob);
+ eventList.clear();
+}
+
+bool EventList::CanShowHistory(DBEVENTINFO* dbei)
+{
+ if(deltaTime != 0)
+ {
+ if(deltaTime > 0)
+ {
+ if(now - deltaTime < dbei->timestamp)
+ return false;
+ }
+ else
+ {
+ if(now + deltaTime > dbei->timestamp)
+ return false;
+ }
+ }
+
+ if(hContact == NULL || defFilter == 1)
+ return true;
+
+ else if(defFilter < 1)
+ {
+ switch( dbei->eventType )
+ {
+ case EVENTTYPE_MESSAGE:
+ case EVENTTYPE_URL:
+ case EVENTTYPE_FILE:
+ return true;
+
+ default:
+ {
+ DBEVENTTYPEDESCR* et = ( DBEVENTTYPEDESCR* )CallService( MS_DB_EVENT_GETTYPE, ( WPARAM )dbei->szModule, ( LPARAM )dbei->eventType );
+ if ( et && ( et->flags & DETF_HISTORY ))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ else
+ {
+ if(filterMap.find(dbei->eventType) != filterMap.end())
+ {
+ if(onlyInFilter)
+ {
+ return !(dbei->flags & DBEF_SENT);
+ }
+ else if(onlyOutFilter)
+ {
+ return (dbei->flags & DBEF_SENT) != 0;
+ }
+ return true;
+ }
+ return false;
+ }
+}
+
+bool EventList::CanShowHistory(const IImport::ExternalMessage& message)
+{
+ if(deltaTime != 0)
+ {
+ if(deltaTime > 0)
+ {
+ if(now - deltaTime < message.timestamp)
+ return false;
+ }
+ else
+ {
+ if(now + deltaTime > message.timestamp)
+ return false;
+ }
+ }
+
+ if(hContact == NULL || defFilter == 1)
+ return true;
+
+ else if(defFilter < 1)
+ {
+ switch(message.eventType )
+ {
+ case EVENTTYPE_MESSAGE:
+ case EVENTTYPE_URL:
+ case EVENTTYPE_FILE:
+ return true;
+ }
+
+ return false;
+ }
+ else
+ {
+ if(filterMap.find(message.eventType) != filterMap.end())
+ {
+ if(onlyInFilter)
+ {
+ return !(message.flags & DBEF_SENT);
+ }
+ else if(onlyOutFilter)
+ {
+ return (message.flags & DBEF_SENT) != 0;
+ }
+ return true;
+ }
+ return false;
+ }
+}
+
+void EventList::InitFilters()
+{
+ filterMap.clear();
+ onlyInFilter = false;
+ onlyOutFilter = false;
+ if(defFilter >= 2)
+ {
+ defFilter = 0;
+ for(int i = 0; i < (int)Options::instance->customFilters.size(); ++i)
+ {
+ if(filterName == Options::instance->customFilters[i].name)
+ {
+ defFilter = i + 2;
+ if(Options::instance->customFilters[i].onlyIncomming && !Options::instance->customFilters[i].onlyOutgoing)
+ {
+ onlyInFilter = true;
+ }
+ else if(Options::instance->customFilters[i].onlyOutgoing && !Options::instance->customFilters[i].onlyIncomming)
+ {
+ onlyOutFilter = true;
+ }
+
+ for(std::vector<int>::iterator it = Options::instance->customFilters[i].events.begin(); it != Options::instance->customFilters[i].events.end(); ++it)
+ {
+ filterMap[*it] = true;
+ }
+
+ break;
+ }
+ }
+ }
+ else
+ filterName = L"";
+}
+
+void EventList::SetDefFilter(int filter)
+{
+ defFilter = filter;
+ if(filter >= 2 && filter - 2 < (int)Options::instance->customFilters.size())
+ {
+ filterName = Options::instance->customFilters[filter - 2].name;
+ }
+ else if(filter == 1)
+ {
+ filterName = TranslateT("All events");
+ }
+ else
+ {
+ filterName = TranslateT("Default history events");
+ }
+}
+
+int EventList::GetFilterNr()
+{
+ return defFilter;
+}
+
+std::wstring EventList::GetFilterName()
+{
+ return filterName;
+}
+
+void EventList::GetTempList(std::list<EventTempIndex>& tempList, bool noFilter, bool noExt, HANDLE _hContact)
+{
+ HANDLE hDbEvent;
+ bool isWndLocal = isWnd;
+ EventTempIndex ti;
+ EventIndex ei;
+ EventData data;
+ ti.isExternal = false;
+ ei.isExternal = false;
+ hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDFIRST,(WPARAM)_hContact,0);
+ while ( hDbEvent != NULL )
+ {
+ if (isWndLocal && !IsWindow( hWnd ))
+ break;
+ ei.hEvent = hDbEvent;
+ if(GetEventData(ei, data))
+ {
+ if(noFilter || CanShowHistory(&gdbei))
+ {
+ ti.hEvent = hDbEvent;
+ ti.timestamp = data.timestamp;
+ tempList.push_back(ti);
+ }
+ }
+ hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDNEXT,(WPARAM)hDbEvent,0);
+ }
+
+ if(!noExt)
+ {
+ std::list<EventTempIndex>::iterator itL = tempList.begin();
+ ti.isExternal = true;
+ for(int i = 0; i < (int)importedMessages.size(); ++i)
+ {
+ if(noFilter || CanShowHistory(importedMessages[i]))
+ {
+ DWORD ts = importedMessages[i].timestamp;
+ while(itL != tempList.end() && itL->timestamp < ts)++itL;
+ if(itL == tempList.end() || itL->timestamp > ts)
+ {
+ ti.exIdx = i;
+ ti.timestamp = ts;
+ tempList.insert(itL, ti);
+ }
+ }
+ }
+ }
+}
+
+void EventList::RefreshEventList()
+{
+ InitNames();
+ InitFilters();
+
+ if(useImportedMessages)
+ {
+ std::vector<IImport::ExternalMessage> messages;
+
+ EnterCriticalSection(&criticalSection);
+ std::map<HANDLE, EventList::ImportDiscData>::iterator it = contactFileMap.find(hContact);
+ if(it != contactFileMap.end())
+ {
+ ExportManager imp(hWnd, hContact, 1);
+ imp.SetAutoImport(it->second.file);
+ if(!imp.Import(it->second.type, messages, NULL))
+ {
+ messages.clear();
+ }
+ }
+
+ LeaveCriticalSection(&criticalSection);
+
+ ImportMessages(messages);
+ }
+
+ std::list<EventTempIndex> tempList;
+ GetTempList(tempList, false, false, hContact);
+ std::list<EventTempIndex> revTempList;
+ std::list<EventTempIndex>& nrTempList = tempList;
+ bool isNewOnTop = Options::instance->groupNewOnTop;
+ if(isNewOnTop)
+ {
+ revTempList.insert(revTempList.begin(), tempList.rbegin(), tempList.rend());
+ nrTempList = revTempList;
+ }
+
+ eventList.clear();
+ eventList.push_back(std::deque<EventIndex>());
+ DWORD lastTime = MAXDWORD;
+ DWORD groupTime = Options::instance->groupTime * 60 * 60;
+ int maxMess = Options::instance->groupMessagesNumber;
+ int limitator = 0;
+ EventIndex ei;
+ for(std::list<EventTempIndex>::iterator itL = nrTempList.begin(); itL != nrTempList.end(); ++itL)
+ {
+ DWORD tm = isNewOnTop ? lastTime - itL->timestamp : itL->timestamp - lastTime;
+ if(isFlat || tm < groupTime && limitator < maxMess)
+ {
+ lastTime = itL->timestamp;
+ ei.isExternal = itL->isExternal;
+ ei.hEvent = itL->hEvent;
+ if(isNewOnTop)
+ eventList.back().push_front(ei);
+ else
+ eventList.back().push_back(ei);
+ ++limitator;
+ }
+ else
+ {
+ limitator = 0;
+ lastTime = itL->timestamp;
+ if(!eventList.back().empty())
+ {
+ ei = eventList.back().front();
+ AddGroup(ei);
+ eventList.push_back(std::deque<EventIndex>());
+ }
+ ei.isExternal = itL->isExternal;
+ ei.hEvent = itL->hEvent;
+ eventList.back().push_front(ei);
+ }
+ }
+
+ if(!eventList.back().empty())
+ {
+ ei = eventList.back().front();
+ AddGroup(ei);
+ }
+}
+
+bool EventList::SearchInContact(HANDLE hContact, TCHAR *strFind, ComparatorInterface* compFun)
+{
+ InitFilters();
+
+ if(useImportedMessages)
+ {
+ std::vector<IImport::ExternalMessage> messages;
+
+ EnterCriticalSection(&criticalSection);
+ std::map<HANDLE, EventList::ImportDiscData>::iterator it = contactFileMap.find(hContact);
+ if(it != contactFileMap.end())
+ {
+ ExportManager imp(hWnd, hContact, 1);
+ imp.SetAutoImport(it->second.file);
+ if(!imp.Import(it->second.type, messages, NULL))
+ {
+ messages.clear();
+ }
+ }
+
+ LeaveCriticalSection(&criticalSection);
+ for(int i = 0; i < (int)importedMessages.size(); ++i)
+ {
+ if(compFun->Compare((importedMessages[i].flags & DBEF_SENT) != 0, importedMessages[i].message, strFind))
+ {
+ return true;
+ }
+ }
+ }
+
+ std::list<EventTempIndex> tempList;
+ GetTempList(tempList, false, true, hContact);
+
+ EventIndex ei;
+ EventData ed;
+ TCHAR str[MAXSELECTSTR + 8]; // for safety reason
+ for(std::list<EventTempIndex>::iterator itL = tempList.begin(); itL != tempList.end(); ++itL)
+ {
+ ei.isExternal = itL->isExternal;
+ ei.hEvent = itL->hEvent;
+ if(GetEventData(ei, ed))
+ {
+ GetEventMessage(ei, str);
+ if(compFun->Compare(ed.isMe, str, strFind))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void EventList::InitNames()
+{
+ TCHAR str[200];
+ if(hContact)
+ {
+ _tcscpy_s(contactName, 256, (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, GCDNF_TCHAR ));
+ mir_sntprintf(str,200,TranslateT("History for %s"),contactName);
+ }
+ else
+ {
+ _tcscpy_s(contactName, 256, TranslateT("System"));
+ mir_sntprintf(str,200,TranslateT("History"));
+ }
+
+ if(isWnd)
+ {
+ SetWindowText(hWnd,str);
+ }
+
+ _tcscpy_s(myName, GetMyName().c_str());
+}
+
+void EventList::AddGroup(const EventIndex& ev)
+{
+ EventData data;
+ GetEventData(ev, data);
+ TCHAR eventText[256];
+ int i;
+ eventText[0] = 0;
+ tmi.printTimeStamp(NULL, data.timestamp, _T("d t"), eventText, 64, 0);
+ std::wstring time = eventText;
+ std::wstring user;
+ if(data.isMe)
+ user = myName;
+ else
+ user = contactName;
+ GetEventMessage(ev, eventText, 256);
+ for(i = 0; eventText[i] != 0 && eventText[i] != _T('\r') && eventText[i] != _T('\n'); ++i);
+ eventText[i] = 0;
+ if(i > Options::instance->groupMessageLen)
+ {
+ eventText[Options::instance->groupMessageLen - 3] = '.';
+ eventText[Options::instance->groupMessageLen - 2] = '.';
+ eventText[Options::instance->groupMessageLen - 1] = '.';
+ eventText[Options::instance->groupMessageLen] = 0;
+ }
+
+ int ico = 0;
+ GetEventIcon(data.isMe, data.eventType, ico);
+ AddGroup(data.isMe, time, user, eventText, ico);
+}
+
+std::wstring EventList::GetContactName()
+{
+ if(hContact)
+ {
+ return (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, GCDNF_TCHAR );
+ }
+ else
+ {
+ return TranslateT("System");
+ }
+}
+
+void GetInfo(CONTACTINFO& ci, std::wstring& str)
+{
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci))
+ {
+ if (ci.type == CNFT_ASCIIZ)
+ {
+ str = ci.pszVal;
+ mir_free(ci.pszVal);
+
+ }
+ else if (ci.type == CNFT_DWORD)
+ {
+ TCHAR buf[20];
+ _ltot_s(ci.dVal, buf, 10 );
+ str = buf;
+ }
+ else if (ci.type == CNFT_WORD)
+ {
+ TCHAR buf[20];
+ _ltot_s(ci.wVal, buf, 10 );
+ str = buf;
+ }
+ }
+}
+
+std::wstring EventList::GetMyName()
+{
+ std::wstring myName;
+ CONTACTINFO ci;
+ ZeroMemory(&ci, sizeof(ci));
+ ci.cbSize = sizeof(ci);
+ ci.szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ ci.hContact = 0;
+ ci.dwFlag = CNF_DISPLAY | CNF_TCHAR;
+ GetInfo(ci, myName);
+ if(myName.empty())
+ {
+ return TranslateT("Me");
+ }
+
+ return myName;
+}
+
+inline std::wstring GetProtocolName(HANDLE hContact)
+{
+ char* ac = (char *)CallService(MS_PROTO_GETCONTACTBASEACCOUNT, (WPARAM)hContact, 0);
+ std::wstring proto1;
+ if(ac != NULL)
+ {
+ PROTOACCOUNT* acnt = ProtoGetAccount(ac);
+ if(acnt != NULL && acnt->szModuleName != NULL)
+ {
+ wchar_t* proto = mir_a2u(acnt->szProtoName);
+ proto1 = proto;
+ mir_free(proto);
+ }
+ }
+
+ return proto1;
+}
+
+std::wstring EventList::GetProtocolName()
+{
+ return ::GetProtocolName(hContact);
+}
+
+std::string EventList::GetBaseProtocol()
+{
+ char* proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ return proto == NULL ? "" : proto;
+}
+
+std::wstring EventList::GetMyId()
+{
+ std::wstring myId;
+ CONTACTINFO ci;
+ ZeroMemory(&ci, sizeof(ci));
+ ci.cbSize = sizeof(ci);
+ ci.szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ ci.hContact = 0;
+ ci.dwFlag = CNF_DISPLAYUID | CNF_TCHAR;
+ GetInfo(ci, myId);
+ return myId;
+}
+
+inline std::wstring GetContactId(HANDLE hContact)
+{
+ std::wstring id;
+ CONTACTINFO ci;
+ ZeroMemory(&ci, sizeof(ci));
+ ci.cbSize = sizeof(ci);
+ ci.szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ ci.hContact = hContact;
+ ci.dwFlag = CNF_DISPLAYUID | CNF_TCHAR;
+ GetInfo(ci, id);
+ return id;
+}
+
+std::wstring EventList::GetContactId()
+{
+ return ::GetContactId(hContact);
+}
+
+static void GetMessageDescription( DBEVENTINFO *dbei, TCHAR* buf, int cbBuf )
+{
+ TCHAR* msg = DbGetEventTextT( dbei, CP_ACP );
+ _tcsncpy_s(buf, cbBuf, msg ? msg : TranslateT("Invalid Message"), cbBuf - 1 );
+ buf[ cbBuf-1 ] = 0;
+ mir_free( msg );
+}
+
+static void GetAuthRequestDescription( DBEVENTINFO *dbei, TCHAR* buf, int cbBuf )
+{
+ std::wstring allName;
+ buf[0] = 0;
+ size_t pos = sizeof( DWORD ) + sizeof( HANDLE );
+ if(pos >= dbei->cbBlob)
+ return;
+ DWORD uin = *((DWORD*)dbei->pBlob);
+ HANDLE hContact = *((HANDLE*)(dbei->pBlob + sizeof( DWORD )));
+ char* nick, *firstName, *lastName, *jid, *reason;
+ nick = ( char* )( dbei->pBlob + sizeof( DWORD )+ sizeof( HANDLE ));
+ pos += strnlen_s(nick, dbei->cbBlob - pos) + 1;
+ if(pos >= dbei->cbBlob)
+ return;
+ firstName = ( char* )dbei->pBlob + pos;
+ pos += strnlen_s(firstName, dbei->cbBlob - pos) + 1;
+ if(pos >= dbei->cbBlob)
+ return;
+ lastName = ( char* )dbei->pBlob + pos;
+ pos += strnlen_s(lastName, dbei->cbBlob - pos) + 1;
+ if(pos >= dbei->cbBlob)
+ return;
+ jid = (char*)dbei->pBlob + pos;
+ pos += strnlen_s(jid, dbei->cbBlob - pos) + 1;
+ if(pos >= dbei->cbBlob)
+ return;
+ reason = (char*)dbei->pBlob + pos;
+ TCHAR *newNick, *newFirstName, *newLastName, *newJid, *newReason;
+ if(dbei->flags & DBEF_UTF)
+ {
+ newNick = mir_utf8decodeT( nick );
+ newFirstName = mir_utf8decodeT( firstName );
+ newLastName = mir_utf8decodeT( lastName );
+ newJid = mir_utf8decodeT( jid );
+ newReason = mir_utf8decodeT( reason );
+ }
+ else
+ {
+ newNick = mir_a2t( nick );
+ newFirstName = mir_a2t( firstName );
+ newLastName = mir_a2t( lastName );
+ newJid = mir_a2t( jid );
+ newReason = mir_a2t( reason );
+ }
+
+ if(newFirstName[0] != 0)
+ {
+ allName += newFirstName;
+ if(newLastName[0] != 0)
+ allName += _T(" ");
+ }
+
+ if(newLastName[0] != 0)
+ allName += newLastName;
+ if(!allName.empty())
+ allName += _T(", ");
+ if(newJid[0] != 0)
+ {
+ allName += newJid;
+ allName += _T(", ");
+ }
+
+ _sntprintf_s(buf, cbBuf, _TRUNCATE, TranslateT("Authorisation request by %s (%s%d): %s"),
+ (newNick[0] == 0 ? (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, GCDNF_TCHAR) : newNick),
+ allName.c_str(), uin, newReason);
+ mir_free( newNick );
+ mir_free( newFirstName );
+ mir_free( newLastName );
+ mir_free( newJid );
+ mir_free( newReason );
+}
+
+void EventList::GetObjectDescription( DBEVENTINFO *dbei, TCHAR* str, int cbStr )
+{
+ switch( dbei->eventType )
+ {
+ case EVENTTYPE_AUTHREQUEST:
+ GetAuthRequestDescription( dbei, str, cbStr );
+ break;
+
+ default:
+ GetMessageDescription( dbei, str, cbStr );
+ }
+}
+
+bool EventList::GetEventIcon(bool isMe, int eventType, int &id)
+{
+ switch(eventType)
+ {
+ case EVENTTYPE_MESSAGE:
+ id = isMe ? 1 : 0;
+ return true;
+ case EVENTTYPE_STATUSCHANGE:
+ id = 2;
+ return true;
+ case EVENTTYPE_FILE:
+ id = iconsNum;
+ return true;
+ case EVENTTYPE_URL:
+ id = iconsNum + 1;
+ return true;
+ case EVENTTYPE_AUTHREQUEST:
+ id = iconsNum + 2;
+ return true;
+ default:
+ id = isMe ? 1 : 0;
+ return false;
+ }
+}
+
+void EventList::ImportMessages(const std::vector<IImport::ExternalMessage>& messages)
+{
+ DWORD lastTime = 0;
+ importedMessages.clear();
+ for(int i = 0; i < (int)messages.size(); ++i)
+ {
+ if(messages[i].timestamp >= lastTime)
+ {
+ importedMessages.push_back(messages[i]);
+ lastTime = messages[i].timestamp;
+ }
+ else
+ {
+ assert(FALSE);
+ }
+ }
+}
+
+void EventList::MargeMessages(const std::vector<IImport::ExternalMessage>& messages)
+{
+ ImportMessages(messages);
+ std::list<EventTempIndex> tempList;
+ GetTempList(tempList, true, false, hContact);
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(DBEVENTINFO);
+ dbei.szModule = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ CallService(MS_DB_SETSAFETYMODE, (WPARAM)FALSE, 0);
+ for(std::list<EventTempIndex>::iterator it = tempList.begin(); it != tempList.end(); ++it)
+ {
+ if(it->isExternal)
+ {
+ IImport::ExternalMessage& msg = importedMessages[it->exIdx];
+ dbei.flags = msg.flags & (~(DBEF_FIRST));
+ dbei.flags |= DBEF_READ;
+ dbei.timestamp = msg.timestamp;
+ // For now I do not convert event data from string to blob, and event type must be message to handle it properly
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ UINT cp = dbei.flags & DBEF_UTF ? CP_UTF8 : CP_ACP;
+ dbei.cbBlob = WideCharToMultiByte(cp, 0, msg.message.c_str(), (int)msg.message.length() + 1, NULL, 0, NULL, NULL);
+ char* buf = new char[dbei.cbBlob];
+ dbei.cbBlob = WideCharToMultiByte(cp, 0, msg.message.c_str(), (int)msg.message.length() + 1, buf, dbei.cbBlob, NULL, NULL);
+ dbei.pBlob = (PBYTE)buf;
+ CallService(MS_DB_EVENT_ADD, (WPARAM) hContact, (LPARAM) & dbei);
+ delete buf;
+ }
+ }
+
+ CallService(MS_DB_SETSAFETYMODE, (WPARAM)TRUE, 0);
+ std::vector<IImport::ExternalMessage> emessages;
+ ImportMessages(emessages);
+}
+
+bool EventList::GetEventData(const EventIndex& ev, EventData& data)
+{
+ if(!ev.isExternal)
+ {
+ DWORD newBlobSize=CallService(MS_DB_EVENT_GETBLOBSIZE,(WPARAM)ev.hEvent,0);
+ if(newBlobSize>goldBlobSize)
+ {
+ gdbei.pBlob=(PBYTE)mir_realloc(gdbei.pBlob,newBlobSize);
+ goldBlobSize=newBlobSize;
+ }
+
+ gdbei.cbBlob = goldBlobSize;
+ if (CallService(MS_DB_EVENT_GET,(WPARAM)ev.hEvent,(LPARAM)&gdbei) == 0)
+ {
+ data.isMe = (gdbei.flags & DBEF_SENT) != 0;
+ data.eventType = gdbei.eventType;
+ data.timestamp = gdbei.timestamp;
+ return true;
+ }
+ }
+ else
+ {
+ if(ev.exIdx >= 0 && ev.exIdx < (int)importedMessages.size())
+ {
+ IImport::ExternalMessage& em = importedMessages[ev.exIdx];
+ data.isMe = (em.flags & DBEF_SENT) != 0;
+ data.eventType = em.eventType;
+ data.timestamp = em.timestamp;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void EventList::GetExtEventDBei(const EventIndex& ev)
+{
+ IImport::ExternalMessage& em = importedMessages[ev.exIdx];
+ gdbei.flags = (em.flags & (~(DBEF_FIRST))) | 0x800;
+ gdbei.eventType = em.eventType;
+ gdbei.timestamp = em.timestamp;
+}
+
+HICON EventList::GetEventCoreIcon(const EventIndex& ev)
+{
+ if(ev.isExternal)
+ return NULL;
+ HICON ico;
+ ico = (HICON)CallService(MS_DB_EVENT_GETICON, LR_SHARED, (LPARAM)&gdbei);
+ HICON icoMsg = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE);
+ if(icoMsg == ico)
+ {
+ return NULL;
+ }
+
+ return ico;
+}
+
+void EventList::RebuildGroup(int selected)
+{
+ std::deque<EventIndex> newGroup;
+ for(size_t i = 0; i < eventList[selected].size(); ++i)
+ {
+ EventIndex& ev = eventList[selected][i];
+ if(!ev.isExternal)
+ {
+ if(CallService(MS_DB_EVENT_GETBLOBSIZE,(WPARAM)(HANDLE)ev.hEvent,0) >= 0)
+ {
+ // If event exist, we add it to new group
+ newGroup.push_back(eventList[selected][i]);
+ }
+ }
+ else
+ {
+ newGroup.push_back(eventList[selected][i]);
+ }
+ }
+ eventList[selected].clear();
+ eventList[selected].insert(eventList[selected].begin(), newGroup.begin(), newGroup.end());
+}
+
+CRITICAL_SECTION EventList::criticalSection;
+std::map<HANDLE, EventList::ImportDiscData> EventList::contactFileMap;
+std::wstring EventList::contactFileDir;
+
+void EventList::AddImporter(HANDLE hContact, IImport::ImportType type, const std::wstring& file)
+{
+ EnterCriticalSection(&criticalSection);
+ TCHAR buf[32];
+ _stprintf_s(buf, _T("%016llx"), (unsigned long long int)hContact);
+ std::wstring internalFile = contactFileDir;
+ ImportDiscData data;
+ data.file = contactFileDir + buf;
+ data.type = type;
+ CopyFile(file.c_str(), data.file.c_str(), FALSE);
+ contactFileMap[hContact] = data;
+ LeaveCriticalSection(&criticalSection);
+}
+
+void EventList::Init()
+{
+ InitializeCriticalSection(&EventList::criticalSection);
+ TCHAR temp[MAX_PATH];
+ temp[0] = 0;
+ GetTempPath(MAX_PATH, temp);
+ contactFileDir = temp;
+ contactFileDir += L"BasicHistoryImportDir\\";
+ DeleteDirectory(contactFileDir.c_str());
+ CreateDirectory(contactFileDir.c_str(), NULL);
+}
+
+void EventList::Deinit()
+{
+ DeleteCriticalSection(&EventList::criticalSection);
+}
+
+int EventList::GetContactMessageNumber(HANDLE hContact)
+{
+ int count = CallService(MS_DB_EVENT_GETCOUNT,(WPARAM)hContact,0);
+ EnterCriticalSection(&criticalSection);
+ std::map<HANDLE, EventList::ImportDiscData>::iterator it = contactFileMap.find(hContact);
+ if(it != contactFileMap.end())
+ {
+ ++count;
+ }
+
+ LeaveCriticalSection(&criticalSection);
+ return count;
+}
+
+bool EventList::IsImportedHistory(HANDLE hContact)
+{
+ bool count = false;
+ EnterCriticalSection(&criticalSection);
+ std::map<HANDLE, EventList::ImportDiscData>::iterator it = contactFileMap.find(hContact);
+ if(it != contactFileMap.end())
+ {
+ count = true;
+ }
+
+ LeaveCriticalSection(&criticalSection);
+ return count;
+}
+
+void EventList::DeleteImporter(HANDLE hContact)
+{
+ EnterCriticalSection(&criticalSection);
+ std::map<HANDLE, EventList::ImportDiscData>::iterator it = contactFileMap.find(hContact);
+ if(it != contactFileMap.end())
+ {
+ DeleteFile(it->second.file.c_str());
+ contactFileMap.erase(it);
+ }
+
+ LeaveCriticalSection(&criticalSection);
+}
diff --git a/plugins/BasicHistory/src/EventList.h b/plugins/BasicHistory/src/EventList.h
new file mode 100644
index 0000000000..36628ff278
--- /dev/null
+++ b/plugins/BasicHistory/src/EventList.h
@@ -0,0 +1,161 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+#include "IImport.h"
+
+#define MAXSELECTSTR 8184
+
+class ComparatorInterface
+{
+public:
+ virtual bool Compare(const bool isMe, const std::wstring& message, TCHAR *strFind) = 0;
+};
+
+class EventList
+{
+public:
+ struct EventData
+ {
+ bool isMe;
+ WORD eventType;
+ DWORD timestamp;
+ };
+ struct EventIndex
+ {
+ union
+ {
+ HANDLE hEvent;
+ int exIdx;
+ };
+ bool isExternal;
+ };
+private:
+ std::map<int, bool> filterMap;
+ bool onlyInFilter;
+ bool onlyOutFilter;
+ int defFilter;
+ std::wstring filterName;
+ std::vector<IImport::ExternalMessage> importedMessages;
+ DWORD goldBlobSize;
+ static CRITICAL_SECTION criticalSection;
+
+ struct EventTempIndex
+ {
+ union
+ {
+ HANDLE hEvent;
+ int exIdx;
+ };
+ bool isExternal;
+ DWORD timestamp;
+ };
+
+ struct ImportDiscData
+ {
+ IImport::ImportType type;
+ std::wstring file;
+ };
+
+ static std::map<HANDLE, ImportDiscData> contactFileMap;
+ static std::wstring contactFileDir;
+
+ bool CanShowHistory(DBEVENTINFO* dbei);
+ bool CanShowHistory(const IImport::ExternalMessage& message);
+ void InitFilters();
+ void InitNames();
+ void AddGroup(const EventIndex& ev);
+ void GetTempList(std::list<EventTempIndex>& tempList, bool noFilter, bool noExt, HANDLE _hContact);
+ void ImportMessages(const std::vector<IImport::ExternalMessage>& messages);
+protected:
+ TCHAR contactName[256];
+ TCHAR myName[256];
+ bool isWnd;
+ int deltaTime;
+ DWORD now;
+ bool isFlat;
+ DBEVENTINFO gdbei;
+
+ virtual void AddGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText, int ico) = 0;
+ bool GetEventIcon(bool isMe, int eventType, int &id);
+ void DeleteEvent(const EventIndex& ev)
+ {
+ if(!ev.isExternal)
+ CallService(MS_DB_EVENT_DELETE,(WPARAM)hContact,(LPARAM)(HANDLE)ev.hEvent);
+ }
+
+ void RebuildGroup(int selected);
+public:
+ EventList();
+ EventList(HANDLE _hContact, int filter);
+ ~EventList();
+
+ HWND hWnd;
+ HANDLE hContact;
+ std::vector<std::deque<EventIndex> > eventList;
+ bool useImportedMessages;
+
+ static void Init();
+ static void Deinit();
+ void SetDefFilter(int filter);
+ int GetFilterNr();
+ std::wstring GetFilterName();
+ void RefreshEventList();
+ bool SearchInContact(HANDLE hContact, TCHAR *strFind, ComparatorInterface* compFun);
+ std::wstring GetContactName();
+ std::wstring GetMyName();
+ std::wstring GetProtocolName();
+ std::wstring GetMyId();
+ std::wstring GetContactId();
+ std::string GetBaseProtocol();
+ void MargeMessages(const std::vector<IImport::ExternalMessage>& messages);
+ static void AddImporter(HANDLE hContact, IImport::ImportType type, const std::wstring& file);
+ static int GetContactMessageNumber(HANDLE hContact);
+ static bool IsImportedHistory(HANDLE hContact);
+ static void DeleteImporter(HANDLE hContact);
+ static void GetObjectDescription( DBEVENTINFO *dbei, TCHAR* str, int cbStr );
+ bool GetEventData(const EventIndex& ev, EventData& data);
+ void GetExtEventDBei(const EventIndex& ev);
+ HICON GetEventCoreIcon(const EventIndex& ev);
+ void GetEventMessage(const EventIndex& ev, TCHAR* message) // must be allocated with MAXSELECTSTR len
+ {
+ if(!ev.isExternal)
+ GetObjectDescription(&gdbei, message, MAXSELECTSTR);
+ else
+ _tcscpy_s(message, MAXSELECTSTR, importedMessages[ev.exIdx].message.c_str());
+ }
+ void GetEventMessage(const EventIndex& ev, TCHAR* message, int strLen)
+ {
+ if(!ev.isExternal)
+ GetObjectDescription(&gdbei, message, strLen);
+ else
+ {
+ std::wstring& meg = importedMessages[ev.exIdx].message;
+ if((int)meg.size() >= strLen)
+ {
+ memcpy_s(message, strLen * sizeof(TCHAR), meg.c_str(), (strLen - 1) * sizeof(TCHAR));
+ message[strLen - 1] = 0;
+ }
+ else
+ {
+ _tcscpy_s(message, strLen, meg.c_str());
+ }
+ }
+ }
+};
+
diff --git a/plugins/BasicHistory/src/ExportManager.cpp b/plugins/BasicHistory/src/ExportManager.cpp
new file mode 100644
index 0000000000..897561ef26
--- /dev/null
+++ b/plugins/BasicHistory/src/ExportManager.cpp
@@ -0,0 +1,391 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "ExportManager.h"
+#include "TxtExport.h"
+#include "PlainHtmlExport.h"
+#include "RichHtmlExport.h"
+#include "BinaryExport.h"
+#include "DatExport.h"
+#include "Options.h"
+#include "codecvt_CodePage.h"
+
+ExportManager::ExportManager(HWND _hwnd, HANDLE _hContact, int filter)
+ :EventList(_hContact, filter),
+ hwnd(_hwnd),
+ oldOnTop(false)
+{
+}
+
+std::wstring GetFile(const TCHAR* ext, HWND hwnd, bool open)
+{
+ TCHAR filter[512];
+ std::locale loc;
+ TCHAR extUpper[32];
+ _tcscpy_s(extUpper, ext);
+ extUpper[0] = std::toupper(ext[0], loc);
+ _stprintf_s(filter, TranslateT("%s Files (*.%s)"), extUpper, ext);
+ size_t len = _tcslen(filter) + 1;
+ _stprintf_s(filter + len, 512 - len, TranslateT("*.%s"), ext);
+ len += _tcslen(filter + len);
+ filter[++len] = 0;
+ TCHAR stzFilePath[1024];
+ _tcscpy_s(stzFilePath, TranslateT("History"));
+ _tcscat_s(stzFilePath, _T("."));
+ _tcscat_s(stzFilePath, ext);
+ len = _tcslen(stzFilePath) + 1;
+ stzFilePath[len] = 0;
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = hwnd;
+ ofn.lpstrFilter = filter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = stzFilePath;
+ ofn.lpstrTitle = open ? TranslateT("Import") : TranslateT("Export");
+ ofn.nMaxFile = 1024;
+ ofn.lpstrDefExt = ext;
+ if(open)
+ {
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+ if(GetOpenFileName(&ofn))
+ {
+ return stzFilePath;
+ }
+ }
+ else
+ {
+ ofn.Flags = OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;
+ if(GetSaveFileName(&ofn))
+ {
+ return stzFilePath;
+ }
+ }
+
+ return L"";
+}
+
+std::wstring ReplaceExt(const std::wstring& file, const TCHAR* ext)
+{
+ size_t pos = file.find(_T("<ext>"));
+ if(pos < file.length())
+ {
+ std::wstring fileName = file.substr(0, pos);
+ fileName += ext;
+ fileName += file.substr(pos + 5);
+ return fileName;
+ }
+
+ return file;
+}
+
+bool ExportManager::Export(IExport::ExportType type)
+{
+ exp = NULL;
+ UINT cp;
+ std::wstring encoding;
+ bool isBin = false;
+ switch(type)
+ {
+ case IExport::Txt:
+ exp = new TxtExport();
+ cp = Options::instance->codepageTxt;
+ encoding = Options::instance->encodingTxt;
+ isFlat = true;
+ break;
+ case IExport::PlainHtml:
+ exp = new PlainHtmlExport();
+ cp = Options::instance->codepageHtml1;
+ encoding = Options::instance->encodingHtml1;
+ break;
+ case IExport::RichHtml:
+ exp = new RichHtmlExport();
+ cp = Options::instance->codepageHtml2;
+ encoding = Options::instance->encodingHtml2;
+ break;
+ case IExport::Binary:
+ exp = new BinaryExport();
+ cp = CP_UTF8;
+ encoding = L"UTF8";
+ isFlat = true;
+ oldOnTop = true;
+ isBin = true;
+ break;
+ case IExport::Dat:
+ exp = new DatExport();
+ cp = CP_UTF8;
+ encoding = L"UTF8";
+ isFlat = true;
+ oldOnTop = true;
+ isBin = true;
+ break;
+ default:
+ return false;
+ }
+
+ std::wstring fileName;
+ if(file.empty())
+ fileName = GetFile(exp->GetExt(), hwnd, false);
+ else
+ {
+ fileName = ReplaceExt(file, exp->GetExt());
+ }
+
+ if(fileName.empty())
+ return false;
+
+ std::wofstream* stream;
+ if(!isBin)
+ {
+ stream = new std::wofstream (fileName.c_str());
+ if(!stream->is_open())
+ return false;
+
+ std::locale filelocale(std::locale(), new codecvt_CodePage<wchar_t>(cp));
+ stream->imbue(filelocale);
+ exp->SetStream(stream);
+ }
+ else
+ {
+ std::ofstream* cstream = new std::ofstream (fileName.c_str(), std::ios_base::binary);
+ if(!cstream->is_open())
+ return false;
+
+ stream = (std::wofstream*)cstream;
+ exp->SetStream(stream);
+ }
+
+ exp->WriteHeader(fileName, GetFilterName(), GetMyName(), GetMyId(), GetContactName(), GetProtocolName(), GetContactId(), GetBaseProtocol(), encoding);
+
+ RefreshEventList();
+
+ exp->WriteFooter();
+ if(!isBin)
+ {
+ stream->close();
+ delete stream;
+ }
+ else
+ {
+ std::ofstream* cstream = (std::ofstream*)stream;
+ cstream->close();
+ delete cstream;
+ }
+
+ delete exp;
+ return true;
+}
+
+const TCHAR* ExportManager::GetExt(IImport::ImportType type)
+{
+ IImport* imp = NULL;
+ switch(type)
+ {
+ case IImport::Binary:
+ imp = new BinaryExport();
+ break;
+ case IImport::Dat:
+ imp = new DatExport();
+ break;
+ default:
+ return L"";
+ }
+
+ const TCHAR* ext = imp->GetExt();
+ delete imp;
+ return ext;
+}
+
+int ExportManager::Import(IImport::ImportType type, const std::vector<HANDLE>& contacts)
+{
+ IImport* imp = NULL;
+ switch(type)
+ {
+ case IImport::Binary:
+ imp = new BinaryExport();
+ break;
+ case IImport::Dat:
+ imp = new DatExport();
+ break;
+ default:
+ return -2;
+ }
+
+ std::wstring fileName;
+ if(file.empty())
+ return -2;
+ else
+ {
+ fileName = ReplaceExt(file, imp->GetExt());
+ }
+
+ if(fileName.empty())
+ return -2;
+
+ std::ifstream* stream = new std::ifstream (fileName.c_str(), std::ios_base::binary);
+ if(!stream->is_open())
+ return -2;
+
+ imp->SetStream(stream);
+ int t = imp->IsContactInFile(contacts);
+ stream->close();
+ delete stream;
+ delete imp;
+ return t;
+}
+
+bool ExportManager::Import(IImport::ImportType type, std::vector<IImport::ExternalMessage>& eventList, std::wstring* err)
+{
+ IImport* imp = NULL;
+ switch(type)
+ {
+ case IImport::Binary:
+ imp = new BinaryExport();
+ break;
+ case IImport::Dat:
+ imp = new DatExport();
+ break;
+ default:
+ return false;
+ }
+
+ std::wstring fileName;
+ if(file.empty())
+ file = fileName = GetFile(imp->GetExt(), hwnd, true);
+ else
+ {
+ fileName = ReplaceExt(file, imp->GetExt());
+ }
+
+ std::ifstream* stream = new std::ifstream (fileName.c_str(), std::ios_base::binary);
+ if(!stream->is_open())
+ return false;
+
+ imp->SetStream(stream);
+ std::vector<HANDLE> v;
+ v.push_back(hContact);
+ bool ret = true;
+ int contInFile = imp->IsContactInFile(v);
+ if(contInFile != 0 && contInFile != -3)
+ {
+ ret = false;
+ if(err != NULL)
+ *err = TranslateT("File do not contain selected contact");
+ }
+ else
+ {
+ ret = imp->GetEventList(eventList);
+ if(!ret && err != NULL)
+ *err = TranslateT("File is corrupted");
+ }
+ stream->close();
+ delete stream;
+ delete imp;
+ return ret;
+}
+
+void ExportManager::AddGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText, int ico)
+{
+ if(exp == NULL)
+ return;
+ exp->WriteGroup(isMe, time, user, eventText);
+ TCHAR str[MAXSELECTSTR + 8]; // for safety reason
+ str[0] = 0;
+ tm lastTime;
+ bool isFirst = true;
+ bool lastMe = false;
+ EventData data;
+ std::deque<EventIndex> revDeq;
+ std::deque<EventIndex>& deq = eventList.back();
+ if(!oldOnTop && Options::instance->messagesNewOnTop)
+ {
+ revDeq.insert(revDeq.begin(), deq.rbegin(), deq.rend());
+ deq = revDeq;
+ }
+ for(std::deque<EventIndex>::iterator it = deq.begin(); it != deq.end(); ++it)
+ {
+ EventIndex hDbEvent = *it;
+ if(GetEventData(hDbEvent, data))
+ {
+ lastMe = data.isMe;
+
+ TCHAR* formatDate = Options::instance->messagesShowSec ? _T("d s") : _T("d t");
+ TCHAR* longFormatDate = Options::instance->messagesShowSec ? _T("d s") : _T("d t");
+ if(!Options::instance->messagesShowDate)
+ {
+ if(isFirst)
+ {
+ isFirst = false;
+ formatDate = Options::instance->messagesShowSec ? _T("s") : _T("t");
+ time_t tt = data.timestamp;
+ localtime_s(&lastTime, &tt);
+ }
+ else
+ {
+ time_t tt = data.timestamp;
+ tm t;
+ localtime_s(&t, &tt);
+ if(lastTime.tm_yday == t.tm_yday && lastTime.tm_year == t.tm_year)
+ formatDate = Options::instance->messagesShowSec ? _T("s") : _T("t");
+ }
+ }
+
+ tmi.printTimeStamp(NULL, data.timestamp, longFormatDate, str , MAXSELECTSTR, 0);
+ std::wstring longDate = str;
+ tmi.printTimeStamp(NULL, data.timestamp, formatDate, str , MAXSELECTSTR, 0);
+ std::wstring shortDate = str;
+
+ std::wstring user;
+ if(lastMe)
+ user = myName;
+ else
+ user = contactName;
+
+ GetEventMessage(hDbEvent, str);
+ std::wstring strMessage = str;
+ if(strMessage.length() + 1 >= MAXSELECTSTR)
+ continue;
+
+ if(hDbEvent.isExternal)
+ {
+ GetExtEventDBei(hDbEvent);
+ }
+
+ exp->WriteMessage(lastMe, longDate, shortDate, user, strMessage, gdbei);
+ }
+ }
+}
+
+void ExportManager::DeleteExportedEvents()
+{
+ for(size_t j = 0; j < eventList.size(); ++j)
+ {
+ for(size_t i = 0; i < eventList[j].size(); ++i)
+ {
+ DeleteEvent(eventList[j][i]);
+ }
+ }
+}
+
+void ExportManager::SetDeleteWithoutExportEvents(int _deltaTime, DWORD _now)
+{
+ exp = NULL;
+ deltaTime = _deltaTime;
+ now = _now;
+ RefreshEventList();
+}
diff --git a/plugins/BasicHistory/src/ExportManager.h b/plugins/BasicHistory/src/ExportManager.h
new file mode 100644
index 0000000000..d99ff077a6
--- /dev/null
+++ b/plugins/BasicHistory/src/ExportManager.h
@@ -0,0 +1,59 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+#include "EventList.h"
+#include "IExport.h"
+#include "IImport.h"
+class ExportManager : public EventList
+{
+private:
+ IExport* exp;
+ std::wstring file;
+ HWND hwnd;
+ bool oldOnTop;
+protected:
+ virtual void AddGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText, int ico);
+public:
+ ExportManager(HWND _hwnd, HANDLE _hContact, int filter);
+
+ void SetAutoExport(const std::wstring _file, int _deltaTime, DWORD _now)
+ {
+ file = _file;
+ deltaTime = _deltaTime;
+ now = _now;
+ }
+
+ void SetAutoImport(const std::wstring _file)
+ {
+ file = _file;
+ }
+
+ std::wstring GetFileName()
+ {
+ return file;
+ }
+
+ bool Export(IExport::ExportType type);
+ void SetDeleteWithoutExportEvents(int _deltaTime, DWORD _now);
+ void DeleteExportedEvents();
+ int Import(IImport::ImportType type, const std::vector<HANDLE>& contacts);
+ bool Import(IImport::ImportType type, std::vector<IImport::ExternalMessage>& eventList, std::wstring* err = NULL);
+ static const TCHAR* GetExt(IImport::ImportType type);
+};
+
diff --git a/plugins/BasicHistory/src/HistoryWindow.cpp b/plugins/BasicHistory/src/HistoryWindow.cpp
new file mode 100644
index 0000000000..d5c3948cb7
--- /dev/null
+++ b/plugins/BasicHistory/src/HistoryWindow.cpp
@@ -0,0 +1,2572 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "HistoryWindow.h"
+#include "resource.h"
+#include "Options.h"
+#include "HotkeyHelper.h"
+#include "ImageDataObject.h"
+#include "ExportManager.h"
+
+#define MODULE "BasicHistory"
+extern HINSTANCE hInst;
+extern HCURSOR hCurSplitNS, hCurSplitWE;
+extern HANDLE *hEventIcons;
+extern int iconsNum;
+extern HANDLE hPlusIcon, hMinusIcon, hFindNextIcon, hFindPrevIcon;
+extern bool g_SmileyAddAvail;
+extern char* metaContactProto;
+#define DM_HREBUILD (WM_USER+11)
+#define DM_SPLITTERMOVED (WM_USER+15)
+
+#define MIN_PANELHEIGHT 40
+
+void ResetCList(HWND hWnd);
+
+HistoryWindow::HistoryWindow(HANDLE _hContact) :
+ isDestroyed(true),
+ OldSplitterProc(0),
+ splitterY(0),
+ splitterOrgY(0),
+ splitterX(0),
+ splitterOrgX(0),
+ plusIco(NULL),
+ minusIco(NULL),
+ findNextIco(NULL),
+ findPrevIco(NULL),
+ configIco(NULL),
+ deleteIco(NULL),
+ isContactList(false),
+ isLoading(false),
+ isGroupImages(false),
+ allIconNumber(0),
+ eventIcoms(NULL),
+ bkBrush(NULL),
+ bkFindBrush(NULL),
+ hSystem(NULL),
+ splitterXhWnd(NULL),
+ splitterYhWnd(NULL)
+{
+ searcher.SetContect(this);
+ hContact = _hContact;
+ selected = -1;
+ searcher.SetMatchCase(Options::instance->searchMatchCase);
+ searcher.SetMatchWholeWords(Options::instance->searchMatchWhole);
+ searcher.SetOnlyIn(Options::instance->searchOnlyIn);
+ searcher.SetOnlyOut(Options::instance->searchOnlyOut);
+ searcher.SetOnlyGroup(Options::instance->searchOnlyGroup);
+ searcher.SetAllUsers(Options::instance->searchAllContacts);
+ searcher.SetSearchForInLG(Options::instance->searchForInList);
+ searcher.SetSearchForInMes(Options::instance->searchForInMess);
+}
+
+
+HistoryWindow::~HistoryWindow()
+{
+ if(eventIcoms != NULL)
+ {
+ for(int i = 0; i < iconsNum; ++i)
+ {
+ if(eventIcoms[i] != NULL)
+ {
+ CallService(MS_SKIN2_RELEASEICON, (LPARAM)eventIcoms[i], 0);
+ }
+ }
+
+ delete[] eventIcoms;
+ }
+
+ if(plusIco != NULL)
+ {
+ CallService(MS_SKIN2_RELEASEICON, (LPARAM)plusIco, 0);
+ }
+ if(minusIco != NULL)
+ {
+ CallService(MS_SKIN2_RELEASEICON, (LPARAM)minusIco, 0);
+ }
+ if(findNextIco != NULL)
+ {
+ CallService(MS_SKIN2_RELEASEICON, (LPARAM)findNextIco, 0);
+ }
+ if(findPrevIco != NULL)
+ {
+ CallService(MS_SKIN2_RELEASEICON, (LPARAM)findPrevIco, 0);
+ }
+ if(himlSmall != NULL)
+ {
+ ImageList_Destroy(himlSmall);
+ }
+ if(himlNone != NULL)
+ {
+ ImageList_Destroy(himlNone);
+ }
+ if(bkBrush != NULL)
+ {
+ DeleteObject(bkBrush);
+ }
+ if(bkFindBrush != NULL)
+ {
+ DeleteObject(bkFindBrush);
+ }
+}
+
+std::map<HANDLE, HistoryWindow*> HistoryWindow::windows;
+std::vector<HistoryWindow*> HistoryWindow::freeWindows;
+
+void HistoryWindow::Deinit()
+{
+ bool destroyed = true;
+ std::vector<HANDLE> keys;
+ for(std::map<HANDLE, HistoryWindow*>::iterator it = windows.begin(); it != windows.end(); ++it)
+ {
+ if(!it->second->isDestroyed)
+ {
+ keys.push_back(it->first);
+ }
+ }
+ for(std::vector<HANDLE>::iterator it = keys.begin(); it != keys.end(); ++it)
+ {
+ std::map<HANDLE, HistoryWindow*>::iterator it1 = windows.find(*it);
+ if(it1 != windows.end())
+ {
+ DestroyWindow(it1->second->hWnd);
+ it1 = windows.find(*it);
+ destroyed &= it1 == windows.end();
+ }
+ }
+
+ std::vector<HistoryWindow*> keys1;
+ for(std::vector<HistoryWindow*>::iterator it = freeWindows.begin(); it != freeWindows.end(); ++it)
+ {
+ if(!(*it)->isDestroyed)
+ {
+ keys1.push_back(*it);
+ }
+ }
+ for(std::vector<HistoryWindow*>::iterator it = keys1.begin(); it != keys1.end(); ++it)
+ {
+ DestroyWindow((*it)->hWnd);
+ }
+ for(std::vector<HistoryWindow*>::iterator it = freeWindows.begin(); it != freeWindows.end(); ++it)
+ {
+ if(!(*it)->isDestroyed)
+ {
+ destroyed = false;
+ break;
+ }
+ }
+
+ if(destroyed)
+ {
+ for(std::map<HANDLE, HistoryWindow*>::iterator it = windows.begin(); it != windows.end(); ++it)
+ {
+ delete it->second;
+ }
+
+ windows.clear();
+
+ for(std::vector<HistoryWindow*>::iterator it = freeWindows.begin(); it != freeWindows.end(); ++it)
+ {
+ delete *it;
+ }
+
+ freeWindows.clear();
+ }
+}
+
+void HistoryWindow::Open(HANDLE hContact)
+{
+ if(hContact == NULL)
+ {
+ HistoryWindow *hw = new HistoryWindow(hContact);
+ freeWindows.push_back(hw);
+ hw->Show();
+ }
+ else
+ {
+ std::map<HANDLE, HistoryWindow*>::iterator it = windows.find(hContact);
+ if(it != windows.end())
+ {
+ it->second->Focus();
+ }
+ else
+ {
+ windows[hContact] = new HistoryWindow(hContact);
+ windows[hContact]->Show();
+ }
+ }
+}
+
+void HistoryWindow::Close(HANDLE hContact)
+{
+ std::map<HANDLE, HistoryWindow*>::iterator it = windows.find(hContact);
+ if(it != windows.end())
+ {
+ if(it->second->isDestroyed)
+ {
+ delete it->second;
+ windows.erase(it);
+ }
+ else
+ {
+ DestroyWindow(it->second->hWnd);
+ }
+ }
+}
+
+void HistoryWindow::Close(HistoryWindow* historyWindow)
+{
+ if(!historyWindow->isDestroyed)
+ {
+ DestroyWindow(historyWindow->hWnd);
+ return;
+ }
+ std::map<HANDLE, HistoryWindow*>::iterator it = windows.find(historyWindow->hContact);
+ if(it != windows.end() && it->second == historyWindow)
+ {
+ delete it->second;
+ windows.erase(it);
+ }
+ else
+ {
+ for(std::vector<HistoryWindow*>::iterator it = freeWindows.begin(); it != freeWindows.end(); ++it)
+ {
+ if(*it == historyWindow)
+ {
+ freeWindows.erase(it);
+ delete historyWindow;
+ return;
+ }
+ }
+ }
+}
+
+void HistoryWindow::RebuildEvents(HANDLE hContact)
+{
+ if(hContact != NULL)
+ {
+ std::map<HANDLE, HistoryWindow*>::iterator it = windows.find(hContact);
+ if(it != windows.end() && !it->second->isDestroyed)
+ {
+ SendMessage(it->second->hWnd,DM_HREBUILD,0,0);
+ }
+ }
+
+ for(std::vector<HistoryWindow*>::iterator it = freeWindows.begin(); it != freeWindows.end(); ++it)
+ {
+ if((*it)->hContact == hContact && !(*it)->isDestroyed)
+ {
+ SendMessage((*it)->hWnd,DM_HREBUILD,0,0);
+ }
+ }
+}
+
+void HistoryWindow::ChangeToFreeWindow(HistoryWindow* historyWindow)
+{
+ std::map<HANDLE, HistoryWindow*>::iterator it = windows.find(historyWindow->hContact);
+ if(it != windows.end() && it->second == historyWindow)
+ {
+ windows.erase(it);
+ freeWindows.push_back(historyWindow);
+ }
+}
+
+void HistoryWindow::Show()
+{
+ CreateDialogParam(hInst,MAKEINTRESOURCE(IDD_HISTORY),NULL,HistoryWindow::DlgProcHistory,(LPARAM)this);
+}
+
+void HistoryWindow::Focus()
+{
+ if(IsIconic(hWnd))
+ {
+ ShowWindow(hWnd, SW_RESTORE);
+ }
+ else
+ {
+ SetForegroundWindow(hWnd);
+ SetFocus(hWnd);
+ }
+ SendMessage(hWnd,DM_HREBUILD,0,0);
+}
+
+int HistoryWindow::FontsChanged(WPARAM wParam, LPARAM lParam)
+{
+ for(std::map<HANDLE, HistoryWindow*>::iterator it = windows.begin(); it != windows.end(); ++it)
+ {
+ if(!it->second->isDestroyed)
+ {
+ it->second->FontsChanged();
+ }
+ }
+
+ for(std::vector<HistoryWindow*>::iterator it = freeWindows.begin(); it != freeWindows.end(); ++it)
+ {
+ if(!(*it)->isDestroyed)
+ {
+ (*it)->FontsChanged();
+ }
+ }
+
+ return 0;
+}
+
+void HistoryWindow::FontsChanged()
+{
+ if(bkBrush != NULL)
+ {
+ DeleteObject(bkBrush);
+ }
+ if(bkFindBrush != NULL)
+ {
+ DeleteObject(bkFindBrush);
+ }
+
+ bkBrush = CreateSolidBrush(Options::instance->GetColor(Options::WindowBackground));
+ bkFindBrush = CreateSolidBrush(Options::instance->GetColor(Options::FindBackground));
+
+ ResetCList(hWnd);
+ COLORREF bkColor = Options::instance->GetColor(Options::GroupListBackground);
+ ListView_SetBkColor(listWindow, bkColor);
+ ListView_SetTextBkColor(listWindow, bkColor);
+ LOGFONT font;
+ ListView_SetTextColor(listWindow, Options::instance->GetFont(Options::GroupList, &font));
+ InvalidateRect(listWindow, NULL, TRUE);
+ InvalidateRect(hWnd, NULL, TRUE);
+ SelectEventGroup(selected);
+}
+
+void OptionsGroupChanged()
+{
+ HistoryWindow::OptionsGroupChanged();
+}
+
+void HistoryWindow::OptionsGroupChanged()
+{
+ for(std::map<HANDLE, HistoryWindow*>::iterator it = windows.begin(); it != windows.end(); ++it)
+ {
+ if(!it->second->isDestroyed)
+ {
+ it->second->GroupImagesChanged();
+ SendMessage(it->second->hWnd,DM_HREBUILD,0,0);
+ }
+ }
+
+ for(std::vector<HistoryWindow*>::iterator it = freeWindows.begin(); it != freeWindows.end(); ++it)
+ {
+ if(!(*it)->isDestroyed)
+ {
+ (*it)->GroupImagesChanged();
+ SendMessage((*it)->hWnd,DM_HREBUILD,0,0);
+ }
+ }
+}
+
+void OptionsMainChanged()
+{
+ HistoryWindow::OptionsMainChanged();
+}
+
+void HistoryWindow::OptionsMainChanged()
+{
+ for(std::map<HANDLE, HistoryWindow*>::iterator it = windows.begin(); it != windows.end(); ++it)
+ {
+ if(!it->second->isDestroyed)
+ {
+ it->second->ReloadMainOptions();
+ }
+ }
+
+ for(std::vector<HistoryWindow*>::iterator it = freeWindows.begin(); it != freeWindows.end(); ++it)
+ {
+ if(!(*it)->isDestroyed)
+ {
+ (*it)->ReloadMainOptions();
+ }
+ }
+}
+
+void HistoryWindow::ReloadMainOptions()
+{
+ SendDlgItemMessage(hWnd, IDC_LIST_CONTACTS, CLM_SETUSEGROUPS, Options::instance->showContactGroups, 0);
+ SendMessage(hWnd,DM_HREBUILD,0,0);
+}
+
+void OptionsMessageChanged()
+{
+ HistoryWindow::FontsChanged(0, 0);
+}
+
+void OptionsSearchingChanged()
+{
+ HistoryWindow::OptionsSearchingChanged();
+}
+
+void HistoryWindow::OptionsSearchingChanged()
+{
+ for(std::map<HANDLE, HistoryWindow*>::iterator it = windows.begin(); it != windows.end(); ++it)
+ {
+ if(!it->second->isDestroyed)
+ {
+ it->second->searcher.SetSearchForInLG(Options::instance->searchForInList);
+ it->second->searcher.SetSearchForInMes(Options::instance->searchForInMess);
+ }
+ }
+
+ for(std::vector<HistoryWindow*>::iterator it = freeWindows.begin(); it != freeWindows.end(); ++it)
+ {
+ if(!(*it)->isDestroyed)
+ {
+ (*it)->searcher.SetSearchForInLG(Options::instance->searchForInList);
+ (*it)->searcher.SetSearchForInMes(Options::instance->searchForInMess);
+ }
+ }
+}
+
+INT_PTR HistoryWindow::DeleteAllUserHistory(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ HWND hWnd = NULL;
+ int start = 0;
+ int end = 0;
+ int count = EventList::GetContactMessageNumber(hContact);
+ if(!count)
+ return FALSE;
+
+ for(std::map<HANDLE, HistoryWindow*>::iterator it = windows.begin(); it != windows.end(); ++it)
+ {
+ if(!it->second->isDestroyed)
+ {
+ if(it->second->hContact == hContact)
+ {
+ if(hWnd == NULL)
+ {
+ hWnd = it->second->hWnd;
+ }
+ else if(GetForegroundWindow() == it->second->hWnd)
+ {
+ hWnd = it->second->hWnd;
+ }
+ }
+ }
+ }
+
+ for(std::vector<HistoryWindow*>::iterator it = freeWindows.begin(); it != freeWindows.end(); ++it)
+ {
+ if(!(*it)->isDestroyed)
+ {
+ if((*it)->hContact == hContact)
+ {
+ if(hWnd == NULL)
+ {
+ hWnd = (*it)->hWnd;
+ }
+ else if(GetForegroundWindow() == (*it)->hWnd)
+ {
+ hWnd = (*it)->hWnd;
+ }
+ }
+ }
+ }
+
+ TCHAR *message = TranslateT("This operation will PERMANENTLY REMOVE all history for this contact.\nAre you sure you want to do this?");
+ if(MessageBox(hWnd, message, TranslateT("Are You sure?"), MB_OKCANCEL | MB_ICONERROR) != IDOK)
+ return FALSE;
+
+ std::deque<HANDLE> toRemove;
+ HANDLE hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDFIRST,(WPARAM)hContact,0);
+ while ( hDbEvent != NULL )
+ {
+ toRemove.push_back(hDbEvent);
+ hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDNEXT,(WPARAM)hDbEvent,0);
+ }
+
+ for(std::deque<HANDLE>::iterator it = toRemove.begin(); it != toRemove.end(); ++it)
+ {
+ CallService(MS_DB_EVENT_DELETE,(WPARAM)hContact,(LPARAM)(HANDLE)*it);
+ }
+
+ if(EventList::IsImportedHistory(hContact))
+ {
+ TCHAR *message = TranslateT("Do you want delete all imported messages for this contact?\nNote that next scheduler task import this messages again.");
+ if(MessageBox(hWnd, message, TranslateT("Are You sure?"), MB_YESNO | MB_ICONERROR) == IDYES)
+ {
+ EventList::DeleteImporter(hContact);
+ }
+ }
+
+ RebuildEvents(hContact);
+ return TRUE;
+}
+
+bool HistoryWindow::IsInList(HWND hWnd)
+{
+ for(std::map<HANDLE, HistoryWindow*>::iterator it = windows.begin(); it != windows.end(); ++it)
+ {
+ if(!it->second->isDestroyed)
+ {
+ if(it->second->hWnd == hWnd)
+ {
+ return true;
+ }
+ }
+ }
+
+ for(std::vector<HistoryWindow*>::iterator it = freeWindows.begin(); it != freeWindows.end(); ++it)
+ {
+ if(!(*it)->isDestroyed)
+ {
+ if((*it)->hWnd == hWnd)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void ClickLink(HWND hwnd, ENLINK *penLink)
+{
+ TCHAR buf[1024];
+ if(penLink->msg != WM_LBUTTONUP)
+ return;
+ if(penLink->chrg.cpMin >= 0 && penLink->chrg.cpMax > penLink->chrg.cpMin)
+ {
+ // selection
+ int len = penLink->chrg.cpMax - penLink->chrg.cpMin;
+ if(len < 1023)
+ {
+ TEXTRANGE tr;
+ CHARRANGE sel;
+
+ SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM) & sel);
+ if (sel.cpMin != sel.cpMax)
+ return;
+ tr.chrg = penLink->chrg;
+ tr.lpstrText = buf;
+ SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) & tr);
+ CallService(MS_UTILS_OPENURL, (penLink->nmhdr.code == IDM_OPENNEW ? OUF_NEWWINDOW : 0) | OUF_TCHAR, (LPARAM) tr.lpstrText);
+ }
+ }
+}
+
+void ConvertSize(HWND hwndSrc, HWND hwndDest, RECT& rc)
+{
+ POINT pt;
+ pt.x = rc.left;
+ pt.y = rc.top;
+ ClientToScreen(hwndSrc, &pt);
+ ScreenToClient(hwndDest, &pt);
+ rc.left = pt.x;
+ rc.top = pt.y;
+
+ pt.x = rc.right;
+ pt.y = rc.bottom;
+ ClientToScreen(hwndSrc, &pt);
+ ScreenToClient(hwndDest, &pt);
+ rc.right = pt.x;
+ rc.bottom = pt.y;
+}
+
+void OpenOptions(char* group, char* page, char* tab = NULL)
+{
+ OPENOPTIONSDIALOG op;
+ op.cbSize = sizeof(OPENOPTIONSDIALOG);
+ op.pszGroup = group;
+ op.pszPage = page;
+ op.pszTab = tab;
+ CallService(MS_OPT_OPENOPTIONS, 0, (LPARAM)&op);
+}
+
+#define DlgReturn(ret){\
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (ret));\
+ return (ret);\
+}
+
+class ShowMessageData
+{
+public:
+ ShowMessageData(HANDLE _hContact)
+ :hContact(_hContact)
+ {
+ }
+
+ ShowMessageData(HANDLE _hContact, const std::wstring &_str)
+ :hContact(_hContact),
+ str(_str)
+ {
+ }
+
+ HANDLE hContact;
+ std::wstring str;
+};
+
+void __stdcall ShowMessageWindow(void* arg)
+{
+ ShowMessageData* dt = (ShowMessageData*)arg;
+ if(dt->str.empty())
+ CallService(MS_MSG_SENDMESSAGE, (WPARAM)dt->hContact, 0);
+ else
+ CallService(MS_MSG_SENDMESSAGET, (WPARAM)dt->hContact, (LPARAM)dt->str.c_str());
+ delete dt;
+}
+
+INT_PTR CALLBACK HistoryWindow::DlgProcHistory(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_GETMINMAXINFO:
+ ((MINMAXINFO*)lParam)->ptMinTrackSize.x=500;
+ ((MINMAXINFO*)lParam)->ptMinTrackSize.y=380;
+
+ case WM_SIZE:
+ {
+ UTILRESIZEDIALOG urd={0};
+ urd.cbSize=sizeof(urd);
+ urd.hwndDlg=hwndDlg;
+ urd.hInstance=hInst;
+ urd.lpTemplate=MAKEINTRESOURCEA(IDD_HISTORY);
+ urd.lParam=(LPARAM)NULL;
+ urd.pfnResizer=HistoryWindow::HistoryDlgResizer;
+ CallService(MS_UTILS_RESIZEDIALOG,0,(LPARAM)&urd);
+ ListView_SetColumnWidth(GetDlgItem(hwndDlg,IDC_LIST), 0, LVSCW_AUTOSIZE_USEHEADER);
+ DlgReturn(TRUE);
+ }
+ case WM_COMMAND:
+ switch ( LOWORD( wParam )) {
+ case IDOK:
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ DlgReturn(TRUE);
+
+ case IDM_FIND:
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ historyWindow->searcher.Find();
+ DlgReturn(TRUE);
+ }
+
+ case IDM_CONFIG:
+ {
+ OPENOPTIONSDIALOG opd = {0};
+ opd.cbSize = sizeof(OPENOPTIONSDIALOG);
+ opd.pszPage = LPGEN("History");
+ CallService(MS_OPT_OPENOPTIONS, 0, (LPARAM)&opd);
+ DlgReturn(TRUE);
+ }
+
+ case IDM_DELETE:
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ historyWindow->Delete(0);
+ DlgReturn(TRUE);
+ }
+
+ case IDC_FIND_TEXT:
+ if(HIWORD(wParam) == EN_CHANGE)
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ historyWindow->searcher.ClearFind();
+ }
+
+ DlgReturn(TRUE);
+
+ case IDC_SHOWHIDE:
+ {
+ if(HIWORD( wParam ) == BN_CLICKED)
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if(Button_GetCheck(GetDlgItem(hwndDlg,IDC_SHOWHIDE)) & BST_CHECKED)
+ {
+ SendDlgItemMessage( hwndDlg, IDC_SHOWHIDE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)historyWindow->minusIco);
+ SendDlgItemMessage( hwndDlg, IDC_SHOWHIDE, BUTTONADDTOOLTIP, (WPARAM)LPGENT("Hide Contacts"), BATF_TCHAR);
+ historyWindow->isContactList = true;
+ ShowWindow(GetDlgItem(hwndDlg,IDC_LIST_CONTACTS), SW_SHOW);
+ ShowWindow(historyWindow->splitterYhWnd, SW_SHOW);
+ }
+ else
+ {
+ SendDlgItemMessage( hwndDlg, IDC_SHOWHIDE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)historyWindow->plusIco);
+ SendDlgItemMessage( hwndDlg, IDC_SHOWHIDE, BUTTONADDTOOLTIP, (WPARAM)LPGENT("Show Contacts"), BATF_TCHAR);
+ historyWindow->isContactList = false;
+ ShowWindow(GetDlgItem(hwndDlg,IDC_LIST_CONTACTS), SW_HIDE);
+ ShowWindow(historyWindow->splitterYhWnd, SW_HIDE);
+ }
+
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ }
+
+ DlgReturn(TRUE);
+ }
+ }
+ break;
+ case WM_NOTIFY:
+ {
+ LPNMHDR pNmhdr;
+
+ pNmhdr = (LPNMHDR)lParam;
+ switch(pNmhdr->idFrom)
+ {
+ case IDC_LIST_CONTACTS:
+ if(pNmhdr->code == CLN_LISTREBUILT)// || pNmhdr->code == CLN_CONTACTMOVED || pNmhdr->code == CLN_NEWCONTACT)
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if(historyWindow != NULL)
+ {
+ historyWindow->ReloadContacts();
+ }
+
+ DlgReturn(TRUE);
+ }
+ else if(pNmhdr->code == CLN_MYSELCHANGED)
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if(historyWindow->ContactChanged())
+ {
+ MSGFILTER* msgFilter = (MSGFILTER *) lParam;
+ if(msgFilter->msg == WM_LBUTTONDOWN)
+ {
+ SendMessage(pNmhdr->hwndFrom, WM_LBUTTONUP, msgFilter->wParam, msgFilter->lParam);
+ }
+ }
+
+ DlgReturn(TRUE);
+ }
+ else if(pNmhdr->code == CLN_OPTIONSCHANGED)
+ {
+ ResetCList(hwndDlg);
+ return FALSE;
+ }
+
+ //fall through
+ //case IDC_LIST_CONTACTS:
+ case IDC_SHOWHIDE:
+ case IDC_FIND_TEXT:
+ case IDC_EDIT:
+ if ( pNmhdr->code == EN_LINK )
+ {
+ ClickLink(GetDlgItem(hwndDlg, IDC_EDIT), (ENLINK *) lParam);
+ return FALSE;
+ }
+ else if( pNmhdr->code == EN_SELCHANGE)
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ historyWindow->searcher.ClearFind();
+ }
+ else if(pNmhdr->code == EN_MSGFILTER)
+ {
+ MSGFILTER* msgFilter = (MSGFILTER *) lParam;
+ if (msgFilter->msg == WM_KEYDOWN || msgFilter->msg == WM_SYSKEYDOWN)
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if(historyWindow->DoHotkey(msgFilter->msg, msgFilter->lParam, msgFilter->wParam, pNmhdr->idFrom))
+ DlgReturn(TRUE);
+ }
+ else if (msgFilter->msg == WM_RBUTTONDOWN || msgFilter->msg == WM_RBUTTONDBLCLK || msgFilter->msg == WM_NCRBUTTONUP || msgFilter->msg == WM_NCRBUTTONDBLCLK || msgFilter->msg == WM_NCRBUTTONDOWN)
+ {
+ DlgReturn(TRUE);
+ }
+ else if (msgFilter->msg == WM_RBUTTONUP)
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ POINT clicked;
+ LPNMITEMACTIVATE nmlv = (LPNMITEMACTIVATE)lParam;
+ HWND window = historyWindow->editWindow;
+ POINTL p;
+ POINT scrool;
+ LVHITTESTINFO info = {0};
+ p.x = clicked.x = info.pt.x = GET_X_LPARAM(msgFilter->lParam);
+ p.y = clicked.y = info.pt.y = GET_Y_LPARAM(msgFilter->lParam);
+ ClientToScreen(window, &clicked);
+ SetFocus(window);
+ int selChar = SendMessage(window, EM_CHARFROMPOS, 0, (LPARAM)&p);
+ CHARRANGE chrg;
+ SendMessage(window,EM_EXGETSEL,0,(LPARAM)&chrg);
+ SendMessage(window,EM_GETSCROLLPOS,0,(LPARAM)&scrool);
+ if(selChar < chrg.cpMin || selChar > chrg.cpMax)
+ {
+ chrg.cpMin = chrg.cpMax = selChar;
+ }
+
+ if(chrg.cpMin == chrg.cpMax)
+ {
+ CHARRANGE chrgNew;
+ chrgNew.cpMin = chrg.cpMin;
+ chrgNew.cpMax = chrg.cpMax + 1;
+ SendMessage(window,EM_EXSETSEL,0,(LPARAM)&chrgNew);
+ }
+ CHARFORMAT2 chf;
+ memset(&chf, 0, sizeof(CHARFORMAT2));
+ chf.cbSize = sizeof(CHARFORMAT2);
+ chf.dwMask = CFM_LINK;
+ SendMessage(window, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf);
+ if(chrg.cpMin == chrg.cpMax)
+ SendMessage(window,EM_EXSETSEL,0,(LPARAM)&chrg);
+
+ HMENU hPopupMenu = CreatePopupMenu();
+ if(hPopupMenu != NULL)
+ {
+ if(chf.dwEffects & CFE_LINK)
+ {
+ AppendMenu(hPopupMenu, MF_STRING, IDM_OPENNEW, TranslateT("Open in &new window"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_OPENEXISTING, TranslateT("&Open in existing window"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_COPYLINK, TranslateT("&Copy link"));
+ }
+ else
+ {
+ AppendMenu(hPopupMenu, MF_STRING, IDM_COPY, TranslateT("Copy"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_DELETE, TranslateT("Delete"));
+ AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, NULL);
+ AppendMenu(hPopupMenu, MF_STRING, IDM_MESSAGE, TranslateT("Send Message"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_QUOTE, TranslateT("Reply &Quoted"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEGROUP, TranslateT("Delete Group"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEUSER, TranslateT("Delete All User History"));
+ }
+
+ int selected = TrackPopupMenu(hPopupMenu, TPM_RETURNCMD, clicked.x, clicked.y, 0, hwndDlg, 0);
+ switch (selected)
+ {
+ case IDM_COPY:
+ {
+ if(chrg.cpMax == chrg.cpMin && historyWindow->currentGroup.size() > 0)
+ {
+ size_t start = 0;
+ while(start < historyWindow->currentGroup.size() && chrg.cpMin >= historyWindow->currentGroup[start].endPos) ++start;
+ if(start < historyWindow->currentGroup.size())
+ {
+ CHARRANGE chrgNew;
+ chrgNew.cpMin = 0;
+ if(start > 0)
+ chrgNew.cpMin = historyWindow->currentGroup[start - 1].endPos;
+ chrgNew.cpMax = historyWindow->currentGroup[start].endPos;
+ SendMessage(window,EM_EXSETSEL,0,(LPARAM)&chrgNew);
+ SendMessage(window,WM_COPY,0,0);
+ SendMessage(window,EM_EXSETSEL,0,(LPARAM)&chrg);
+ }
+ }
+ else
+ {
+ SendMessage(window,WM_COPY,0,0);
+ }
+ }
+ break;
+ case IDM_MESSAGE:
+ //CallService(MS_MSG_SENDMESSAGE, (WPARAM)historyWindow->hContact, 0);
+ CallFunctionAsync(ShowMessageWindow, new ShowMessageData(historyWindow->hContact));
+ break;
+ case IDM_QUOTE:
+ {
+ if(historyWindow->currentGroup.size() > 0)
+ {
+ std::wstring quote;
+ if(chrg.cpMax == chrg.cpMin)
+ {
+ size_t start = 0;
+ while(start < historyWindow->currentGroup.size() && chrg.cpMin >= historyWindow->currentGroup[start].endPos) ++start;
+ if(start < historyWindow->currentGroup.size())
+ {
+ historyWindow->FormatQuote(quote, historyWindow->currentGroup[start], historyWindow->currentGroup[start].description);
+ }
+ }
+ else
+ {
+ size_t start = 0;
+ while(start < historyWindow->currentGroup.size() && chrg.cpMin >= historyWindow->currentGroup[start].endPos) ++start;
+ size_t end = 0;
+ while(end < historyWindow->currentGroup.size() && chrg.cpMax > historyWindow->currentGroup[end].endPos) ++end;
+ if(end >= historyWindow->currentGroup.size())
+ end = historyWindow->currentGroup.size() - 1;
+ if(start == end && start < historyWindow->currentGroup.size())
+ {
+ int iStart = historyWindow->currentGroup[start].startPos;
+ if(chrg.cpMin > iStart)
+ iStart = chrg.cpMin;
+ int iEnd = historyWindow->currentGroup[start].endPos;
+ if(chrg.cpMax < iEnd)
+ iEnd = chrg.cpMax;
+ if(iEnd > iStart)
+ {
+ TEXTRANGE tr;
+ tr.chrg.cpMin = iStart;
+ tr.chrg.cpMax = iEnd;
+ tr.lpstrText = new TCHAR[iEnd - iStart + 1];
+ SendMessage(historyWindow->editWindow, EM_GETTEXTRANGE, 0, (LPARAM) & tr);
+ historyWindow->FormatQuote(quote, historyWindow->currentGroup[start], tr.lpstrText);
+ delete [] tr.lpstrText;
+ }
+ }
+ else
+ {
+ while(start <= end)
+ {
+ historyWindow->FormatQuote(quote, historyWindow->currentGroup[start], historyWindow->currentGroup[start].description);
+ ++start;
+ }
+ }
+ }
+
+ if(!quote.empty())
+ {
+ CallFunctionAsync(ShowMessageWindow, new ShowMessageData(historyWindow->hContact, quote));
+ }
+ }
+ }
+ break;
+ case IDM_DELETE:
+ historyWindow->Delete(0);
+ break;
+ case IDM_DELETEGROUP:
+ historyWindow->Delete(1);
+ break;
+ case IDM_DELETEUSER:
+ historyWindow->Delete(2);
+ break;
+ case IDM_OPENNEW:
+ case IDM_OPENEXISTING:
+ case IDM_COPYLINK:
+ {
+ int start = chrg.cpMin, end = chrg.cpMin;
+ CHARRANGE chrgNew;
+ chrgNew.cpMin = start-1;
+ chrgNew.cpMax = start;
+ do
+ {
+ memset(&chf, 0, sizeof(CHARFORMAT2));
+ chf.cbSize = sizeof(CHARFORMAT2);
+ chf.dwMask = CFM_LINK;
+ int sel = SendMessage(window,EM_EXSETSEL,0,(LPARAM)&chrgNew);
+ if(sel != chrgNew.cpMax)
+ break;
+ SendMessage(window, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf);
+ --chrgNew.cpMin;
+ --chrgNew.cpMax;
+ --start;
+ } while(start >= 0 && chf.dwEffects & CFE_LINK);
+
+ ++start;
+ chrgNew.cpMin = end;
+ chrgNew.cpMax = end + 1;
+ do
+ {
+ memset(&chf, 0, sizeof(CHARFORMAT2));
+ chf.cbSize = sizeof(CHARFORMAT2);
+ chf.dwMask = CFM_LINK;
+ int sel = SendMessage(window,EM_EXSETSEL,0,(LPARAM)&chrgNew);
+ if(sel != chrgNew.cpMax)
+ break;
+ SendMessage(window, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf);
+ ++chrgNew.cpMin;
+ ++chrgNew.cpMax;
+ ++end;
+ } while(chf.dwEffects & CFE_LINK);
+
+ --end;
+ if(selected == IDM_COPYLINK)
+ {
+ chrgNew.cpMin = start;
+ chrgNew.cpMax = end;
+ SendMessage(window,EM_EXSETSEL,0,(LPARAM)&chrgNew);
+ SendMessage(window,WM_COPY,0,0);
+ SendMessage(window,EM_EXSETSEL,0,(LPARAM)&chrg);
+ }
+ else
+ {
+ ENLINK link;
+ link.chrg.cpMin = start;
+ link.chrg.cpMax = end;
+ link.msg = WM_LBUTTONUP;
+ link.nmhdr.code = selected;
+ SendMessage(window,EM_EXSETSEL,0,(LPARAM)&chrg);
+ ClickLink(window, &link);
+ }
+ }
+ break;
+ }
+
+ DestroyMenu(hPopupMenu);
+ }
+ SendMessage(window,EM_SETSCROLLPOS,0,(LPARAM)&scrool);
+ DlgReturn(TRUE);
+ }
+ }
+ break;
+ case IDC_LIST:
+ if( pNmhdr->code == LVN_ITEMCHANGED)
+ {
+ NMLISTVIEW *nmlv = (NMLISTVIEW*)lParam;
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if((nmlv->uChanged & LVIF_STATE) && (nmlv->uNewState & LVIS_SELECTED) && historyWindow->selected != nmlv->iItem && nmlv->iItem >= 0)
+ {
+ historyWindow->SelectEventGroup(nmlv->iItem);
+ DlgReturn(TRUE);
+ }
+ }
+ else if( pNmhdr->code == LVN_KEYDOWN)
+ {
+ LPNMLVKEYDOWN nmlv = (LPNMLVKEYDOWN)lParam;
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if(historyWindow->DoHotkey(WM_KEYDOWN, 0, nmlv->wVKey, IDC_LIST))
+ DlgReturn(TRUE);
+ }
+ else if(pNmhdr->code == NM_RCLICK)
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ POINT clicked;
+ LPNMITEMACTIVATE nmlv = (LPNMITEMACTIVATE)lParam;
+ HWND window = historyWindow->listWindow;
+ LVHITTESTINFO info = {0};
+ clicked.x = info.pt.x = nmlv->ptAction.x;
+ clicked.y = info.pt.y = nmlv->ptAction.y;
+ ClientToScreen(window, &clicked);
+ int newSel = SendMessage(window, LVM_SUBITEMHITTEST, 0, (LPARAM)&info);
+ int curSel = historyWindow->selected;
+
+ if(newSel >= 0)
+ {
+ HMENU hPopupMenu = CreatePopupMenu();
+ if(hPopupMenu != NULL)
+ {
+ AppendMenu(hPopupMenu, MF_STRING, IDM_COPY, TranslateT("Copy"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEGROUP, TranslateT("Delete Group"));
+ AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, NULL);
+ AppendMenu(hPopupMenu, MF_STRING, IDM_MESSAGE, TranslateT("Send Message"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEUSER, TranslateT("Delete All User History"));
+
+ int selected = TrackPopupMenu(hPopupMenu, TPM_RETURNCMD, clicked.x, clicked.y, 0, hwndDlg, 0);
+ switch (selected)
+ {
+ case IDM_COPY:
+ {
+ CHARRANGE chrg;
+ SendMessage(historyWindow->editWindow,EM_EXGETSEL,0,(LPARAM)&chrg);
+ CHARRANGE chrgNew;
+ chrgNew.cpMin = 0;
+ chrgNew.cpMax = -1;
+ SendMessage(historyWindow->editWindow,EM_EXSETSEL,0,(LPARAM)&chrgNew);
+ SendMessage(historyWindow->editWindow,WM_COPY,0,0);
+ SendMessage(historyWindow->editWindow,EM_EXSETSEL,0,(LPARAM)&chrg);
+ }
+ break;
+ case IDM_MESSAGE:
+ CallService(MS_MSG_SENDMESSAGE, (WPARAM)historyWindow->hContact, 0);
+ break;
+ case IDM_DELETEGROUP:
+ historyWindow->Delete(1);
+ break;
+ case IDM_DELETEUSER:
+ historyWindow->Delete(2);
+ break;
+ }
+
+ DestroyMenu(hPopupMenu);
+ }
+ }
+
+ DlgReturn(TRUE);
+ }
+
+ break;
+ case IDC_TOOLBAR:
+ if( pNmhdr->code == TBN_DROPDOWN)
+ {
+ LPNMTOOLBAR lpnmTB= (LPNMTOOLBAR)lParam;
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if(lpnmTB->iItem == IDM_FIND)
+ {
+ historyWindow->FindToolbarClicked(lpnmTB);
+ }
+ else if(lpnmTB->iItem == IDM_CONFIG)
+ {
+ historyWindow->ConfigToolbarClicked(lpnmTB);
+ }
+ else if(lpnmTB->iItem == IDM_DELETE)
+ {
+ historyWindow->DeleteToolbarClicked(lpnmTB);
+ }
+
+ DlgReturn(TBDDRET_DEFAULT);
+ }
+ else if( pNmhdr->code == NM_KEYDOWN)
+ {
+ LPNMKEY nmlv = (LPNMKEY)lParam;
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if(historyWindow->DoHotkey(WM_KEYDOWN, 0, nmlv->nVKey, IDC_TOOLBAR))
+ DlgReturn(TRUE);
+ }
+ break;
+ }
+ break;
+ }
+ case WM_CTLCOLORDLG:
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ DlgReturn((LONG_PTR)historyWindow->bkBrush);
+ }
+ case WM_CTLCOLORSTATIC:
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ HWND curhWnd = (HWND)lParam;
+ if(historyWindow->splitterXhWnd == curhWnd || historyWindow->splitterYhWnd == curhWnd)
+ {
+ DlgReturn((LONG_PTR)historyWindow->bkBrush);
+ }
+
+ break;
+ }
+ case WM_CTLCOLOREDIT:
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ HWND curhWnd = (HWND)lParam;
+ if(historyWindow->findWindow == curhWnd)
+ {
+ HDC edithdc = (HDC)wParam;
+ LOGFONT font;
+ SetTextColor(edithdc, Options::instance->GetFont(Options::Find, &font));
+ SetBkColor(edithdc, Options::instance->GetColor(Options::FindBackground));
+ DlgReturn((LONG_PTR)historyWindow->bkFindBrush);
+ }
+
+ break;
+ }
+ case DM_SPLITTERMOVED:
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ historyWindow->SplitterMoved((HWND)lParam, wParam, true);
+ break;
+ }
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ HistoryWindow* historyWindow = (HistoryWindow*)lParam;
+ historyWindow->hWnd = hwndDlg;
+ historyWindow->isWnd = true;
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG_PTR)lParam);
+ historyWindow->Initialise();
+ }
+ DlgReturn(TRUE);
+
+ case DM_HREBUILD:
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if(!historyWindow->isLoading)
+ {
+ historyWindow->isLoading = true;
+ historyWindow->ReloadContacts();
+ mir_forkthread(HistoryWindow::FillHistoryThread, historyWindow);
+ }
+ }
+ DlgReturn(TRUE);
+
+ case WM_DESTROY:
+ {
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ historyWindow->Destroy();
+ }
+ DlgReturn(TRUE);
+ }
+ return FALSE;
+}
+
+void HistoryWindow::Initialise()
+{
+ splitterXhWnd = GetDlgItem(hWnd, IDC_SPLITTER);
+ splitterYhWnd = GetDlgItem(hWnd, IDC_SPLITTERV);
+ OldSplitterProc = (WNDPROC)SetWindowLongPtr(splitterXhWnd, GWLP_WNDPROC, (LONG_PTR) SplitterSubclassProc);
+ SetWindowLongPtr(splitterYhWnd, GWLP_WNDPROC, (LONG_PTR) SplitterSubclassProc);
+
+ editWindow = GetDlgItem(hWnd, IDC_EDIT);
+ findWindow = GetDlgItem(hWnd, IDC_FIND_TEXT);
+ toolbarWindow = GetDlgItem(hWnd, IDC_TOOLBAR);
+ listWindow = GetDlgItem(hWnd, IDC_LIST);
+
+ RECT rc;
+ POINT pt;
+ GetWindowRect(splitterXhWnd, &rc);
+ pt.y = (rc.top + rc.bottom) / 2;
+ pt.x = 0;
+ ScreenToClient(hWnd, &pt);
+ splitterOrgY = pt.y;
+ splitterY = pt.y;
+ GetWindowRect(splitterYhWnd, &rc);
+ pt.y = 0;
+ pt.x = (rc.left + rc.right) / 2;
+ ScreenToClient(hWnd, &pt);
+ splitterOrgX = pt.x;
+ splitterX = pt.x;
+ GetWindowRect(GetDlgItem(hWnd, IDC_LIST_CONTACTS), &rc);
+ pt.y = rc.top;
+ pt.x = rc.left;
+ ScreenToClient(hWnd, &pt);
+ listOryginalPos = pt.x;
+
+ plusIco = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 1, (LPARAM)hPlusIcon);
+ minusIco = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 1, (LPARAM)hMinusIcon);
+ SendDlgItemMessage( hWnd, IDC_SHOWHIDE, BUTTONSETASPUSHBTN, TRUE, 0 );
+ SendDlgItemMessage( hWnd, IDC_SHOWHIDE, BUTTONSETASFLATBTN, TRUE, 0 );
+ if(hContact == NULL || Options::instance->showContacts)
+ {
+ SendDlgItemMessage( hWnd, IDC_SHOWHIDE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)minusIco);
+ SendDlgItemMessage( hWnd, IDC_SHOWHIDE, BUTTONADDTOOLTIP, (WPARAM)LPGENT("Hide Contacts"), BATF_TCHAR);
+ Button_SetCheck(GetDlgItem(hWnd,IDC_SHOWHIDE), BST_CHECKED);
+ isContactList = true;
+ }
+ else
+ {
+ SendDlgItemMessage( hWnd, IDC_SHOWHIDE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)plusIco);
+ SendDlgItemMessage( hWnd, IDC_SHOWHIDE, BUTTONADDTOOLTIP, (WPARAM)LPGENT("Show Contacts"), BATF_TCHAR);
+ Button_SetCheck(GetDlgItem(hWnd,IDC_SHOWHIDE), BST_UNCHECKED);
+ ShowWindow(GetDlgItem(hWnd,IDC_LIST_CONTACTS), SW_HIDE);
+ ShowWindow(splitterYhWnd, SW_HIDE);
+ isContactList = false;
+ }
+ RegisterHotkeyControl(GetDlgItem(hWnd, IDC_SHOWHIDE));
+ RegisterHotkeyControl(GetDlgItem(hWnd, IDC_LIST_CONTACTS));
+
+ ResetCList(hWnd);
+
+ RestorePos();
+ SendMessage(hWnd, WM_SETICON, ICON_BIG, ( LPARAM )LoadSkinnedIconBig( SKINICON_OTHER_HISTORY ));
+ SendMessage(hWnd, WM_SETICON, ICON_SMALL, ( LPARAM )LoadSkinnedIcon( SKINICON_OTHER_HISTORY ));
+ SendMessage(editWindow,EM_AUTOURLDETECT,TRUE,0);
+ SendMessage(editWindow,EM_SETEVENTMASK,0,ENM_LINK | ENM_SELCHANGE | ENM_KEYEVENTS | ENM_MOUSEEVENTS);
+ SendMessage(editWindow,EM_SETEDITSTYLE,SES_EXTENDBACKCOLOR,SES_EXTENDBACKCOLOR);
+
+ himlSmall = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 2);
+ himlNone = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 2);
+ ImageList_SetIconSize(himlNone, 0, 16);
+ if(himlSmall)
+ {
+ allIconNumber = iconsNum + 3;
+ eventIcoms = new HICON[allIconNumber];
+ for(int i = 0; i < iconsNum; ++i)
+ {
+ eventIcoms[i] = hEventIcons[i] == NULL ? NULL : (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hEventIcons[i]);
+ ImageList_AddIcon(himlSmall, eventIcoms[i]);
+ }
+
+ int id = iconsNum;
+ eventIcoms[id] = LoadSkinnedIcon(SKINICON_EVENT_FILE);
+ ImageList_AddIcon(himlSmall, eventIcoms[id]);
+
+ eventIcoms[++id] = LoadSkinnedIcon(SKINICON_EVENT_URL);
+ ImageList_AddIcon(himlSmall, eventIcoms[id]);
+
+ eventIcoms[++id] = LoadSkinnedIcon(SKINICON_OTHER_WINDOWS);
+ ImageList_AddIcon(himlSmall, eventIcoms[id]);
+
+ if((isGroupImages = Options::instance->groupShowEvents) != false)
+ ListView_SetImageList(listWindow, himlSmall, LVSIL_SMALL);
+ }
+
+ bkBrush = CreateSolidBrush(Options::instance->GetColor(Options::WindowBackground));
+ bkFindBrush = CreateSolidBrush(Options::instance->GetColor(Options::FindBackground));
+
+ LVCOLUMN col = {0};
+ col.mask = LVCF_WIDTH | LVCF_TEXT;
+ col.cx = 470;
+ col.pszText = _T("");
+ ListView_InsertColumn(listWindow, 0, &col);
+ ListView_SetColumnWidth(listWindow, 0, LVSCW_AUTOSIZE_USEHEADER);
+ ListView_SetExtendedListViewStyleEx(listWindow, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
+ COLORREF bkColor = Options::instance->GetColor(Options::GroupListBackground);
+ ListView_SetBkColor(listWindow, bkColor);
+ ListView_SetTextBkColor(listWindow, bkColor);
+ LOGFONT font;
+ ListView_SetTextColor(listWindow, Options::instance->GetFont(Options::GroupList, &font));
+
+ Edit_LimitText(findWindow, 100);
+ RegisterHotkeyControl(findWindow);
+
+ HIMAGELIST himlButtons = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 3, 3);
+ if(himlButtons)
+ {
+ findNextIco = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hFindNextIcon);
+ ImageList_AddIcon(himlButtons, findNextIco);
+ findPrevIco = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hFindPrevIcon);
+ ImageList_AddIcon(himlButtons, findPrevIco);
+ configIco = LoadSkinnedIcon(SKINICON_OTHER_OPTIONS);
+ ImageList_AddIcon(himlButtons, configIco);
+ deleteIco = LoadSkinnedIcon(SKINICON_OTHER_DELETE);
+ ImageList_AddIcon(himlButtons, deleteIco);
+
+ // Set the image list.
+ SendMessage(toolbarWindow, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)himlButtons);
+
+ // Load the button images.
+ SendMessage(toolbarWindow, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);
+ }
+
+ TBBUTTON tbButtons[] =
+ {
+ { 0, IDM_FIND, TBSTATE_ENABLED, BTNS_DROPDOWN, {0}, 0, (INT_PTR)TranslateT("Find Next") },
+ { 3, IDM_DELETE, TBSTATE_ENABLED, BTNS_DROPDOWN, {0}, 0, (INT_PTR)TranslateT("Delete")},
+ { 2, IDM_CONFIG, TBSTATE_ENABLED, BTNS_DROPDOWN, {0}, 0, (INT_PTR)TranslateT("Options")},
+ };
+ SendMessage(toolbarWindow, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
+ SendMessage(toolbarWindow, TB_ADDBUTTONS, (WPARAM)SIZEOF(tbButtons), (LPARAM)&tbButtons);
+ SendMessage(toolbarWindow, TB_SETBUTTONSIZE, 0, MAKELPARAM(16, 16));
+ SendMessage(toolbarWindow, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS);
+ SendMessage(toolbarWindow, TB_SETMAXTEXTROWS, 0, 0);
+
+ SetDefFilter(Options::instance->defFilter);
+
+ SendMessage(hWnd, DM_SETDEFID, IDM_FIND, 0);
+ SendMessage(hWnd, WM_SIZE, 0, 0);
+ SendMessage(hWnd,DM_HREBUILD,0,0);
+ isDestroyed = false;
+}
+
+void HistoryWindow::Destroy()
+{
+ HICON hIcon = (HICON)SendMessage(hWnd, WM_SETICON, ICON_BIG, 0);
+ CallService(MS_SKIN2_RELEASEICON, (WPARAM)hIcon, 0);
+
+ hIcon = (HICON)SendMessage(hWnd, WM_SETICON, ICON_SMALL, 0);
+ CallService(MS_SKIN2_RELEASEICON, (WPARAM)hIcon, 0);
+
+ UnregisterHotkeyControl(GetDlgItem(hWnd, IDC_SHOWHIDE));
+ UnregisterHotkeyControl(GetDlgItem(hWnd, IDC_LIST_CONTACTS));
+ UnregisterHotkeyControl(findWindow);
+
+ isDestroyed = true;
+ HistoryWindow::Close(this);
+}
+
+void HistoryWindow::SplitterMoved(HWND splitter, LONG pos, bool screenPos)
+{
+ POINT pt;
+ RECT rc1;
+ RECT rc2;
+ POINT pt1;
+ POINT pt2;
+
+ if(splitter == splitterXhWnd)
+ {
+ GetWindowRect(listWindow, &rc1);
+ GetWindowRect(editWindow, &rc2);
+ pt.x = 0;
+ pt.y = pos;
+ pt1.x = rc1.left;
+ pt1.y = rc1.top;
+ pt2.x = rc2.right;
+ pt2.y = rc2.bottom;
+ if(screenPos)
+ ScreenToClient(hWnd, &pt);
+ ScreenToClient(hWnd, &pt1);
+ ScreenToClient(hWnd, &pt2);
+ if ((pt.y >= pt1.y + MIN_PANELHEIGHT) && (pt.y < pt2.y - MIN_PANELHEIGHT))
+ {
+ splitterY = pt.y;
+ if(!screenPos)
+ SendMessage(hWnd, WM_SIZE, 0, 0);
+ //if(M->isAero())
+ // InvalidateRect(GetParent(hwndDlg), NULL, FALSE);
+ }
+ }
+ else
+ {
+ GetWindowRect(GetDlgItem(hWnd, IDC_LIST_CONTACTS), &rc1);
+ GetWindowRect(listWindow, &rc2);
+ pt.x = pos;
+ pt.y = 0;
+ pt1.x = rc1.left;
+ pt1.y = rc1.top;
+ pt2.x = rc2.right;
+ pt2.y = rc2.bottom;
+ if(screenPos)
+ ScreenToClient(hWnd, &pt);
+ ScreenToClient(hWnd, &pt1);
+ ScreenToClient(hWnd, &pt2);
+ if ((pt.x >= pt1.x + MIN_PANELHEIGHT) && (pt.x < pt2.x - MIN_PANELHEIGHT))
+ {
+ splitterX = pt.x;
+ if(!screenPos)
+ SendMessage(hWnd, WM_SIZE, 0, 0);
+ //if(M->isAero())
+ // InvalidateRect(GetParent(hwndDlg), NULL, FALSE);
+ }
+ }
+}
+
+int HistoryWindow::HistoryDlgResizer(HWND hwnd, LPARAM, UTILRESIZECONTROL *urc)
+{
+ HistoryWindow* historyWindow =(HistoryWindow*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
+ switch(urc->wId) {
+ case IDC_LIST:
+ {
+ urc->rcItem.bottom += historyWindow->splitterY - historyWindow->splitterOrgY;
+ urc->rcItem.left += historyWindow->splitterX - historyWindow->splitterOrgX;
+ if(!historyWindow->isContactList)
+ urc->rcItem.left = historyWindow->listOryginalPos;
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_TOP;
+ }
+ case IDC_LIST_CONTACTS:
+ {
+ urc->rcItem.right += historyWindow->splitterX - historyWindow->splitterOrgX;
+ return RD_ANCHORX_LEFT|RD_ANCHORY_HEIGHT;
+ }
+ case IDC_SPLITTER:
+ {
+ urc->rcItem.top += historyWindow->splitterY - historyWindow->splitterOrgY;
+ urc->rcItem.bottom += historyWindow->splitterY - historyWindow->splitterOrgY;
+ urc->rcItem.left += historyWindow->splitterX - historyWindow->splitterOrgX;
+ if(!historyWindow->isContactList)
+ urc->rcItem.left = 0;
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_TOP;
+ }
+ case IDC_SPLITTERV:
+ {
+ urc->rcItem.left += historyWindow->splitterX - historyWindow->splitterOrgX;
+ urc->rcItem.right += historyWindow->splitterX - historyWindow->splitterOrgX;
+ return RD_ANCHORX_LEFT|RD_ANCHORY_HEIGHT;
+ }
+ case IDC_EDIT:
+ {
+ urc->rcItem.top += historyWindow->splitterY - historyWindow->splitterOrgY;
+ urc->rcItem.left += historyWindow->splitterX - historyWindow->splitterOrgX;
+ if(!historyWindow->isContactList)
+ urc->rcItem.left = historyWindow->listOryginalPos;
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
+ }
+ case IDC_FIND_TEXT:
+ return RD_ANCHORX_WIDTH|RD_ANCHORY_TOP;
+ case IDC_SHOWHIDE:
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP;
+ case IDC_TOOLBAR:
+ return RD_ANCHORX_RIGHT|RD_ANCHORY_TOP;
+ }
+ return RD_ANCHORX_LEFT|RD_ANCHORY_TOP;
+}
+
+void HistoryWindow::FillHistoryThread(void* param)
+{
+ HistoryWindow *hInfo = ( HistoryWindow* )param;
+ HWND hwndList = hInfo->listWindow;
+ ListView_DeleteAllItems(hwndList);
+ hInfo->SelectEventGroup(-1);
+ hInfo->EnableWindows(FALSE);
+ bool isNewOnTop = Options::instance->groupNewOnTop;
+
+ hInfo->RefreshEventList();
+
+ LVITEM item = {0};
+ item.mask = LVIF_STATE;
+ item.iItem = 0;
+ item.state = LVIS_SELECTED;
+ item.stateMask = LVIS_SELECTED;
+ if(!isNewOnTop)
+ {
+ item.iItem = ListView_GetItemCount(hwndList) - 1;
+ if(item.iItem < 0)
+ item.iItem = 0;
+ }
+
+ ListView_SetItem(hwndList, &item);
+ ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE_USEHEADER);
+ ListView_EnsureVisible(hwndList, item.iItem, FALSE);
+ hInfo->EnableWindows(TRUE);
+ SetFocus(hwndList);
+}
+
+void HistoryWindow::AddGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText, int ico)
+{
+ TCHAR msg[256];
+ msg[0] = 0;
+ if(Options::instance->groupShowTime)
+ {
+ _tcscpy_s(msg, time.c_str());
+ }
+ if(Options::instance->groupShowName)
+ {
+ if(msg[0] != 0)
+ _tcscat_s(msg, _T(" "));
+ _tcscat_s(msg, user.c_str());
+ }
+
+ if(Options::instance->groupShowMessage)
+ {
+ if(msg[0] != 0)
+ _tcscat_s(msg, _T(" "));
+ _tcscat_s(msg, eventText.c_str());
+ }
+
+ LVITEM item = {0};
+ item.mask = LVIF_TEXT | LVIF_IMAGE;
+ item.iItem = MAXINT;
+ item.pszText = msg;
+ item.iImage = ico;
+ ListView_InsertItem(listWindow, &item);
+}
+
+void HistoryWindow::ReplaceIcons(HWND hwndDlg, int selStart, BOOL isSent)
+{
+ if(g_SmileyAddAvail)
+ {
+ CHARRANGE sel;
+ SMADD_RICHEDIT3 smadd = {0};
+
+ sel.cpMin = selStart;
+ sel.cpMax = -1;
+
+ smadd.cbSize = sizeof(smadd);
+ smadd.hwndRichEditControl = hwndDlg;
+ smadd.Protocolname = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ smadd.hContact = hContact;
+ smadd.flags = isSent ? SAFLRE_OUTGOING : 0;
+ if (selStart > 0)
+ smadd.rangeToReplace = &sel;
+ CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&smadd);
+ }
+}
+
+void SetFontFromOptions(ITextFont *TextFont, int caps, Options::Fonts fontId)
+{
+ COLORREF fontColor;
+ LOGFONT font;
+ fontColor = Options::instance->GetFont(fontId, &font);
+ BSTR bstrFont = SysAllocString(font.lfFaceName);
+ TextFont->SetName(bstrFont);
+ SysFreeString(bstrFont);
+ TextFont->SetForeColor(fontColor);
+ TextFont->SetWeight(font.lfWeight);
+ font.lfHeight = (font.lfHeight * 72) / caps;
+ TextFont->SetSize(font.lfHeight < 0 ? -font.lfHeight : font.lfHeight);
+ TextFont->SetItalic(font.lfItalic ? tomTrue : tomFalse);
+ TextFont->SetItalic(font.lfUnderline ? tomTrue : tomFalse);
+ TextFont->SetItalic(font.lfStrikeOut ? tomTrue : tomFalse);
+}
+
+void HistoryWindow::SelectEventGroup(int sel)
+{
+ SendMessage(editWindow, WM_SETTEXT, 0, (LPARAM)_T(""));
+ currentGroup.clear();
+ selected = sel;
+ if(sel < 0 || sel >= (int)eventList.size())
+ return;
+
+ TCHAR _str[MAXSELECTSTR + 8]; // for safety reason
+ TCHAR *str = _str + sizeof(int)/sizeof(TCHAR);
+ BSTR pStr = str;
+ unsigned int *strLen = (unsigned int*)_str;
+ str[0] = 0;
+ tm lastTime;
+ bool isFirst = true;
+ bool lastMe = false;
+ long startAt, endAt;
+ long cnt;
+ std::wstring strStl;
+ IRichEditOle* RichEditOle;
+ EventData data;
+ if (SendMessage(editWindow, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle) == 0)
+ return;
+ ITextDocument* TextDocument;
+ if (RichEditOle->QueryInterface(IID_ITextDocument, (void**)&TextDocument) != S_OK)
+ {
+ RichEditOle->Release();
+ return;
+ }
+ ITextSelection* TextSelection;
+ ITextFont *TextFont;
+ SendMessage(editWindow, EM_SETREADONLY, FALSE, 0);
+ TextDocument->Freeze(&cnt);
+ TextDocument->GetSelection(&TextSelection);
+ HDC hDC = GetDC(NULL);
+ int caps = GetDeviceCaps(hDC, LOGPIXELSY);
+ std::deque<EventIndex> revDeq;
+ std::deque<EventIndex>& deq = eventList[sel];
+ if(Options::instance->messagesNewOnTop)
+ {
+ revDeq.insert(revDeq.begin(), deq.rbegin(), deq.rend());
+ deq = revDeq;
+ }
+ COLORREF backColor = GetSysColor(COLOR_WINDOW);
+ for(std::deque<EventIndex>::iterator it = deq.begin(); it != deq.end(); ++it)
+ {
+ EventIndex hDbEvent = *it;
+ if(GetEventData(hDbEvent, data))
+ {
+ bool isUser = Options::instance->messagesShowName && (isFirst || (!lastMe && data.isMe) || (lastMe && !data.isMe));
+ lastMe = data.isMe;
+ backColor = Options::instance->GetColor(lastMe ? Options::OutBackground : Options::InBackground);
+ if(Options::instance->messagesShowEvents)
+ {
+ str[0] = _T('>');
+ str[1] = 0;
+ *strLen = 1 * sizeof(TCHAR);
+ TextSelection->SetStart(MAXLONG);
+ TextSelection->GetFont(&TextFont);
+ TextFont->SetBackColor(backColor);
+ TextSelection->SetText(pStr);
+ TextFont->Release();
+ int imId;
+ HICON ico;
+ if(GetEventIcon(lastMe, data.eventType, imId))
+ {
+ ico = eventIcoms[imId];
+ }
+ else
+ {
+ ico = GetEventCoreIcon(hDbEvent);
+ if(ico == NULL)
+ {
+ ico = eventIcoms[imId];
+ }
+ }
+
+ ImageDataObject::InsertIcon(RichEditOle, ico, backColor, 16, 16);
+ }
+
+ TCHAR* formatDate = Options::instance->messagesShowSec ? (isUser ? _T("d s ") : _T("d s\n")) : (isUser ? _T("d t ") : _T("d t\n"));
+ if(!Options::instance->messagesShowDate)
+ {
+ if(isFirst)
+ {
+ isFirst = false;
+ formatDate = Options::instance->messagesShowSec ? (isUser ? _T("s ") : _T("s\n")) : (isUser ? _T("t ") : _T("t\n"));
+ time_t tt = data.timestamp;
+ localtime_s(&lastTime, &tt);
+ }
+ else
+ {
+ time_t tt = data.timestamp;
+ tm t;
+ localtime_s(&t, &tt);
+ if(lastTime.tm_yday == t.tm_yday && lastTime.tm_year == t.tm_year)
+ formatDate = Options::instance->messagesShowSec ? (isUser ? _T("s ") : _T("s\n")) : (isUser ? _T("t ") : _T("t\n"));
+ }
+ }
+
+ tmi.printTimeStamp(NULL, data.timestamp, formatDate, str , MAXSELECTSTR, 0);
+ *strLen = (unsigned int)_tcslen(str) * sizeof(TCHAR);
+ TextSelection->SetStart(MAXLONG);
+ TextSelection->GetFont(&TextFont);
+ SetFontFromOptions(TextFont, caps, lastMe ? Options::OutTimestamp : Options::InTimestamp);
+ TextFont->SetBackColor(backColor);
+ TextSelection->SetText(pStr);
+ TextFont->Release();
+
+ if(isUser)
+ {
+ if(lastMe)
+ mir_sntprintf( str, MAXSELECTSTR, _T("%s\n"), myName );
+ else
+ mir_sntprintf( str, MAXSELECTSTR, _T("%s\n"), contactName );
+ *strLen = (unsigned int)_tcslen(str) * sizeof(TCHAR);
+ TextSelection->SetStart(MAXLONG);
+ TextSelection->GetFont(&TextFont);
+ SetFontFromOptions(TextFont, caps, lastMe ? Options::OutName : Options::InName);
+ TextSelection->SetText(pStr);
+ TextFont->Release();
+ }
+
+ GetEventMessage(hDbEvent, str);
+ strStl = str;
+ size_t i = strStl.length();
+ if(i + 1 >= MAXSELECTSTR)
+ continue;
+ str[i++] = _T('\n');
+ str[i] = 0;
+ *strLen = (unsigned int)i * sizeof(TCHAR);
+ TextSelection->SetStart(MAXLONG);
+ TextSelection->GetFont(&TextFont);
+ SetFontFromOptions(TextFont, caps, lastMe ? Options::OutMessages : Options::InMessages);
+ TextFont->SetBackColor(backColor);
+ TextSelection->GetStart(&startAt);
+ TextSelection->SetText(pStr);
+ TextFont->Release();
+
+ if(Options::instance->messagesUseSmileys)
+ ReplaceIcons(editWindow, startAt, lastMe);
+ TextSelection->SetStart(MAXLONG);
+ TextSelection->GetStart(&endAt);
+ currentGroup.push_back(MessageData(strStl, startAt, endAt, lastMe, data.timestamp));
+ }
+ }
+
+ TextSelection->SetRange(0, 0);
+ TextSelection->Release();
+ TextDocument->Unfreeze(&cnt);
+ TextDocument->Release();
+ RichEditOle->Release();
+ SendMessage(editWindow, EM_SETREADONLY, TRUE, 0);
+ SendMessage(editWindow,EM_SETBKGNDCOLOR,0, backColor);
+ if (cnt == 0)
+ {
+ UpdateWindow(editWindow);
+ }
+}
+
+LRESULT CALLBACK HistoryWindow::SplitterSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndParent = GetParent(hwnd);
+ HistoryWindow *dat = (HistoryWindow*)GetWindowLongPtr(hwndParent, GWLP_USERDATA);
+ if(dat == NULL || dat->isDestroyed)
+ return FALSE;
+
+ switch (msg) {
+ case WM_NCHITTEST:
+ return HTCLIENT;
+ case WM_SETCURSOR: {
+ RECT rc;
+ GetClientRect(hwnd, &rc);
+ SetCursor(rc.right > rc.bottom ? hCurSplitNS : hCurSplitWE);
+ return TRUE;
+ }
+ case WM_LBUTTONDOWN: {
+ SetCapture(hwnd);
+ return 0;
+ }
+ case WM_MOUSEMOVE:
+ if (GetCapture() == hwnd) {
+ RECT rc;
+ GetClientRect(hwnd, &rc);
+ SendMessage(hwndParent, DM_SPLITTERMOVED, rc.right > rc.bottom ? (short) HIWORD(GetMessagePos()) + rc.bottom / 2 : (short) LOWORD(GetMessagePos()) + rc.right / 2, (LPARAM) hwnd);
+ }
+ return 0;
+ case WM_ERASEBKGND:
+ return(1);
+ case WM_LBUTTONUP: {
+ HWND hwndCapture = GetCapture();
+
+ ReleaseCapture();
+ SendMessage(hwndParent, WM_SIZE, 0, 0);
+ RedrawWindow(hwndParent, NULL, NULL, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW);
+ return 0;
+ }
+ }
+ return CallWindowProc(dat->OldSplitterProc, hwnd, msg, wParam, lParam);
+}
+
+void HistoryWindow::EnableWindows(BOOL enable)
+{
+ EnableWindow(GetDlgItem(hWnd,IDC_LIST_CONTACTS), enable);
+ EnableWindow(listWindow, enable);
+ EnableWindow(findWindow, enable);
+ EnableWindow(toolbarWindow, enable);
+ isLoading = !enable;
+}
+
+void HistoryWindow::ReloadContacts()
+{
+ HWND contactList = GetDlgItem(hWnd,IDC_LIST_CONTACTS);
+ if(EventList::GetContactMessageNumber(NULL))
+ {
+ if(hSystem == NULL)
+ {
+ CLCINFOITEM cii = { 0 };
+ cii.cbSize = sizeof(cii);
+ cii.flags = CLCIIF_GROUPFONT | CLCIIF_BELOWCONTACTS;
+ cii.pszText = TranslateT("System");
+ hSystem = (HANDLE) SendMessage(contactList, CLM_ADDINFOITEM, 0, (LPARAM) & cii);
+ }
+ }
+ else
+ {
+ if(hSystem != NULL)
+ {
+ SendMessage(contactList, CLM_DELETEITEM, (WPARAM)hSystem, 0);
+ hSystem = NULL;
+ }
+ }
+
+ HANDLE _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while(_hContact)
+ {
+ if(EventList::GetContactMessageNumber(_hContact) && (metaContactProto == NULL || DBGetContactSettingByte(_hContact, metaContactProto, "IsSubcontact", 0) == 0))
+ {
+ HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, (WPARAM)_hContact, 0);
+ if(hItem == NULL)
+ {
+ SendMessage(contactList, CLM_ADDCONTACT, (WPARAM)_hContact, 0);
+ }
+ }
+ else
+ {
+ HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, (WPARAM)_hContact, 0);
+ if(hItem != NULL)
+ {
+ SendMessage(contactList, CLM_DELETEITEM, (WPARAM)_hContact, 0);
+ }
+ }
+
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)_hContact, 0);
+ }
+
+ if(hContact != NULL)
+ {
+ HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, (WPARAM)hContact, 0);
+ if(hItem != NULL)
+ {
+ SendMessage(contactList, CLM_ENSUREVISIBLE, (WPARAM)hItem, 0);
+ SendMessage(contactList, CLM_SELECTITEM, (WPARAM)hItem, 0);
+ }
+ }
+ else if(hSystem != NULL)
+ {
+ SendMessage(contactList, CLM_ENSUREVISIBLE, (WPARAM)hSystem, 0);
+ SendMessage(contactList, CLM_SELECTITEM, (WPARAM)hSystem, 0);
+ }
+}
+
+bool HistoryWindow::DoHotkey(UINT msg, LPARAM lParam, WPARAM wParam, int window)
+{
+ MSG message;
+ message.hwnd = hWnd;
+ message.message = msg;
+ message.lParam = lParam;
+ message.wParam = wParam;
+ LRESULT mim_hotkey_check = CallService(MS_HOTKEY_CHECK, (WPARAM)&message, (LPARAM)("History"));
+ switch(mim_hotkey_check)
+ {
+ case HISTORY_HK_FIND:
+ SetFocus(findWindow);
+ Edit_SetSel(findWindow, 0, -1);
+ break;
+ case HISTORY_HK_FINDNEXT:
+ searcher.ChangeFindDirection(false);
+ break;
+ case HISTORY_HK_FINDPREV:
+ searcher.ChangeFindDirection(true);
+ break;
+ case HISTORY_HK_MATCHCASE:
+ searcher.SetMatchCase(!searcher.IsMatchCase());
+ break;
+ case HISTORY_HK_MATCHWHOLE:
+ searcher.SetMatchWholeWords(!searcher.IsMatchWholeWords());
+ break;
+ case HISTORY_HK_SHOWCONTACTS:
+ Button_SetCheck(GetDlgItem(hWnd, IDC_SHOWHIDE), Button_GetCheck(GetDlgItem(hWnd, IDC_SHOWHIDE)) & BST_CHECKED ? BST_UNCHECKED : BST_CHECKED);
+ SendMessage(hWnd, WM_COMMAND, MAKELONG(IDC_SHOWHIDE, BN_CLICKED), NULL);
+ break;
+ case HISTORY_HK_ONLYIN:
+ searcher.SetOnlyIn(!searcher.IsOnlyIn());
+ break;
+ case HISTORY_HK_ONLYOUT:
+ searcher.SetOnlyOut(!searcher.IsOnlyOut());
+ break;
+ case HISTORY_HK_ONLYGROUP:
+ searcher.SetOnlyGroup(!searcher.IsOnlyGroup());
+ break;
+ case HISTORY_HK_ALLCONTACTS:
+ searcher.SetAllUsers(!searcher.IsAllUsers());
+ break;
+ case HISTORY_HK_EXRHTML:
+ {
+ ExportManager exp(hWnd, hContact, GetFilterNr());
+ exp.Export(IExport::RichHtml);
+ }
+ break;
+ case HISTORY_HK_EXPHTML:
+ {
+ ExportManager exp(hWnd, hContact, GetFilterNr());
+ exp.Export(IExport::PlainHtml);
+ }
+ break;
+ case HISTORY_HK_EXTXT:
+ {
+ ExportManager exp(hWnd, hContact, GetFilterNr());
+ exp.Export(IExport::Txt);
+ }
+ break;
+ case HISTORY_HK_EXBIN:
+ {
+ ExportManager exp(hWnd, hContact, GetFilterNr());
+ exp.Export(IExport::Binary);
+ }
+ break;
+ case HISTORY_HK_EXDAT:
+ {
+ ExportManager exp(hWnd, hContact, GetFilterNr());
+ exp.Export(IExport::Dat);
+ }
+ break;
+ case HISTORY_HK_IMPBIN:
+ {
+ DoImport(IImport::Binary);
+ }
+ break;
+ case HISTORY_HK_IMPDAT:
+ {
+ DoImport(IImport::Dat);
+ }
+ break;
+ case HISTORY_HK_DELETE:
+ {
+ int what = window == IDC_EDIT ? 0 : (window == IDC_LIST ? 1 : (window == IDC_LIST_CONTACTS ? 2 : -1));
+ Delete(what);
+ return what != -1;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+void HistoryWindow::RestorePos()
+{
+ HANDLE contactToLoad = hContact;
+ if(hContact == NULL)
+ {
+ Utils_RestoreWindowPosition(hWnd,NULL,MODULE,"history_");
+ contactToLoad = NULL;
+ }
+ else if(Utils_RestoreWindowPosition(hWnd,hContact,MODULE,"history_") != 0)
+ {
+ Utils_RestoreWindowPosition(hWnd,NULL,MODULE,"history_");
+ contactToLoad = NULL;
+ }
+ if(DBGetContactSettingByte(contactToLoad, MODULE, "history_ismax", 0))
+ {
+ ShowWindow(hWnd, SW_SHOWMAXIMIZED);
+ }
+
+ LONG pos = DBGetContactSettingDword(contactToLoad, MODULE, "history_splitterv", 0);
+ if(pos > 0)
+ {
+ SplitterMoved(splitterYhWnd, pos, false);
+ }
+
+ pos = DBGetContactSettingDword(contactToLoad, MODULE, "history_splitter", 0);
+ if(pos > 0)
+ {
+ SplitterMoved(splitterXhWnd, pos, false);
+ }
+}
+
+void HistoryWindow::SavePos(bool all)
+{
+ HANDLE contactToSave = hContact;
+ if(all)
+ {
+ HANDLE _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while(_hContact)
+ {
+ DBDeleteContactSetting(_hContact, MODULE, "history_x");
+ DBDeleteContactSetting(_hContact, MODULE, "history_y");
+ DBDeleteContactSetting(_hContact, MODULE, "history_width");
+ DBDeleteContactSetting(_hContact, MODULE, "history_height");
+ DBDeleteContactSetting(_hContact, MODULE, "history_ismax");
+ DBDeleteContactSetting(_hContact, MODULE, "history_splitterv");
+ DBDeleteContactSetting(_hContact, MODULE, "history_splitter");
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)_hContact, 0);
+ }
+
+ contactToSave = NULL;
+ }
+
+ Utils_SaveWindowPosition(hWnd,contactToSave,MODULE,"history_");
+ WINDOWPLACEMENT wp;
+ wp.length=sizeof(wp);
+ GetWindowPlacement(hWnd,&wp);
+ DBWriteContactSettingByte(contactToSave, MODULE, "history_ismax", wp.showCmd == SW_MAXIMIZE ? 1 : 0);
+ DBWriteContactSettingDword(contactToSave, MODULE, "history_splitterv", splitterX);
+ DBWriteContactSettingDword(contactToSave, MODULE, "history_splitter", splitterY);
+}
+
+#define DEF_FILTERS_START 50000
+void HistoryWindow::FindToolbarClicked(LPNMTOOLBAR lpnmTB)
+{
+ RECT rc;
+ SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT, (WPARAM)lpnmTB->iItem, (LPARAM)&rc);
+ MapWindowPoints(lpnmTB->hdr.hwndFrom, HWND_DESKTOP, (LPPOINT)&rc, 2);
+ HMENU hPopupMenu = CreatePopupMenu();
+ if(hPopupMenu != NULL)
+ {
+ AppendMenu(hPopupMenu, MF_STRING, IDM_FINDNEXT, TranslateT("Find Next"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_FINDPREV, TranslateT("Find Previous"));
+ AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, NULL);
+ AppendMenu(hPopupMenu, searcher.IsMatchCase() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_MATCHCASE, TranslateT("Match Case"));
+ AppendMenu(hPopupMenu, searcher.IsMatchWholeWords() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_MATCHWHOLE, TranslateT("Match Whole Word"));
+ AppendMenu(hPopupMenu, searcher.IsOnlyIn() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_ONLYIN, TranslateT("Only Incomming Messages"));
+ AppendMenu(hPopupMenu, searcher.IsOnlyOut() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_ONLYOUT, TranslateT("Only Outgoing Messages"));
+ AppendMenu(hPopupMenu, searcher.IsOnlyGroup() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_ONLYGROUP, TranslateT("Only Selected Group"));
+ AppendMenu(hPopupMenu, searcher.IsAllUsers() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_ALLUSERS, TranslateT("All Contacts"));
+ AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, NULL);
+ HMENU hFilterMenu = CreatePopupMenu();
+ int filter = GetFilterNr();
+ AppendMenu(hFilterMenu, filter == 0 ? MF_STRING | MF_CHECKED : MF_STRING, IDM_FILTERDEF, TranslateT("Default history events"));
+ AppendMenu(hFilterMenu, filter == 1 ? MF_STRING | MF_CHECKED : MF_STRING, IDM_FILTERALL, TranslateT("All events"));
+ for(size_t i = 0 ; i < Options::instance->customFilters.size(); ++i)
+ {
+ UINT flags = MF_STRING;
+ if(filter - 2 == i)
+ flags |= MF_CHECKED;
+
+ AppendMenu(hFilterMenu, flags, DEF_FILTERS_START + i, Options::instance->customFilters[i].name.c_str());
+ }
+ AppendMenu(hPopupMenu, MF_STRING | MF_POPUP, (UINT_PTR)hFilterMenu, TranslateT("Filters"));
+ if(searcher.IsFindBack())
+ SetMenuDefaultItem(hPopupMenu, IDM_FINDPREV, FALSE);
+ else
+ SetMenuDefaultItem(hPopupMenu, IDM_FINDNEXT, FALSE);
+
+ int selected = TrackPopupMenu(hPopupMenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, hWnd, 0);
+ switch (selected)
+ {
+ case IDM_FINDNEXT:
+ searcher.ChangeFindDirection(false);
+ break;
+ case IDM_FINDPREV:
+ searcher.ChangeFindDirection(true);
+ break;
+ case IDM_MATCHCASE:
+ searcher.SetMatchCase(!searcher.IsMatchCase());
+ break;
+ case IDM_MATCHWHOLE:
+ searcher.SetMatchWholeWords(!searcher.IsMatchWholeWords());
+ break;
+ case IDM_ONLYIN:
+ searcher.SetOnlyIn(!searcher.IsOnlyIn());
+ break;
+ case IDM_ONLYOUT:
+ searcher.SetOnlyOut(!searcher.IsOnlyOut());
+ break;
+ case IDM_ONLYGROUP:
+ searcher.SetOnlyGroup(!searcher.IsOnlyGroup());
+ break;
+ case IDM_ALLUSERS:
+ searcher.SetAllUsers(!searcher.IsAllUsers());
+ break;
+ case IDM_FILTERDEF:
+ SetDefFilter(0);
+ SendMessage(hWnd,DM_HREBUILD,0,0);
+ break;
+ case IDM_FILTERALL:
+ SetDefFilter(1);
+ SendMessage(hWnd,DM_HREBUILD,0,0);
+ break;
+ default:
+ if(selected >= DEF_FILTERS_START)
+ {
+ SetDefFilter(selected - DEF_FILTERS_START + 2);
+ SendMessage(hWnd,DM_HREBUILD,0,0);
+ }
+ break;
+ }
+
+ DestroyMenu(hFilterMenu);
+ DestroyMenu(hPopupMenu);
+ }
+}
+
+void HistoryWindow::ConfigToolbarClicked(LPNMTOOLBAR lpnmTB)
+{
+ RECT rc;
+ SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT, (WPARAM)lpnmTB->iItem, (LPARAM)&rc);
+ MapWindowPoints(lpnmTB->hdr.hwndFrom, HWND_DESKTOP, (LPPOINT)&rc, 2);
+ HMENU hPopupMenu = CreatePopupMenu();
+ if(hPopupMenu != NULL)
+ {
+ AppendMenu(hPopupMenu, MF_STRING, IDM_OPTIONS, TranslateT("Options"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_FONTS, TranslateT("Fonts & Colors"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_ICONS, TranslateT("Icons"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_HOTKEYS, TranslateT("Hotkeys"));
+ AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, NULL);
+
+ HMENU hExportMenu = CreatePopupMenu();
+ AppendMenu(hExportMenu, MF_STRING, IDM_EXPORTRHTML, TranslateT("Rich Html"));
+ AppendMenu(hExportMenu, MF_STRING, IDM_EXPORTPHTML, TranslateT("Plain Html"));
+ AppendMenu(hExportMenu, MF_STRING, IDM_EXPORTTXT, TranslateT("Txt"));
+ AppendMenu(hExportMenu, MF_STRING, IDM_EXPORTBINARY, TranslateT("Binary"));
+ AppendMenu(hExportMenu, MF_STRING, IDM_EXPORTDAT, TranslateT("Dat (mContacts)"));
+
+ HMENU hImportMenu = CreatePopupMenu();
+ AppendMenu(hImportMenu, MF_STRING, IDM_IMPORTBINARY, TranslateT("Binary"));
+ AppendMenu(hImportMenu, MF_STRING, IDM_IMPORTDAT, TranslateT("Dat (mContacts)"));
+
+ AppendMenu(hPopupMenu, MF_STRING | MF_POPUP, (UINT_PTR)hExportMenu, TranslateT("Export"));
+ AppendMenu(hPopupMenu, MF_STRING | MF_POPUP, (UINT_PTR)hImportMenu, TranslateT("Import"));
+
+ AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, NULL);
+ AppendMenu(hPopupMenu, MF_STRING, IDM_SAVEPOS, TranslateT("Save window position as default"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_SAVEPOSALL, TranslateT("Save window position for all contacts"));
+ SetMenuDefaultItem(hPopupMenu, IDM_OPTIONS, FALSE);
+
+ int selected = TrackPopupMenu(hPopupMenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, hWnd, 0);
+ switch (selected)
+ {
+ case IDM_OPTIONS:
+ SendMessage(hWnd, WM_COMMAND, IDM_CONFIG, 0);
+ break;
+ case IDM_FONTS:
+ OpenOptions("Customize", "Fonts & Colors");
+ break;
+ case IDM_ICONS:
+ OpenOptions("Customize", "Icons");
+ break;
+ case IDM_HOTKEYS:
+ OpenOptions("Customize", "Hotkeys");
+ break;
+ case IDM_SAVEPOS:
+ SavePos(false);
+ break;
+ case IDM_SAVEPOSALL:
+ SavePos(true);
+ break;
+ case IDM_EXPORTRHTML:
+ {
+ ExportManager exp(hWnd, hContact, GetFilterNr());
+ exp.Export(IExport::RichHtml);
+ }
+
+ break;
+ case IDM_EXPORTPHTML:
+ {
+ ExportManager exp(hWnd, hContact, GetFilterNr());
+ exp.Export(IExport::PlainHtml);
+ }
+
+ break;
+ case IDM_EXPORTTXT:
+ {
+ ExportManager exp(hWnd, hContact, GetFilterNr());
+ exp.Export(IExport::Txt);
+ }
+
+ break;
+ case IDM_EXPORTBINARY:
+ {
+ ExportManager exp(hWnd, hContact, GetFilterNr());
+ exp.Export(IExport::Binary);
+ }
+
+ break;
+ case IDM_EXPORTDAT:
+ {
+ ExportManager exp(hWnd, hContact, GetFilterNr());
+ exp.Export(IExport::Dat);
+ }
+
+ break;
+ case IDM_IMPORTBINARY:
+ {
+ DoImport(IImport::Binary);
+ }
+
+ break;
+ case IDM_IMPORTDAT:
+ {
+ DoImport(IImport::Dat);
+ }
+
+ break;
+ }
+
+ DestroyMenu(hExportMenu);
+ DestroyMenu(hImportMenu);
+ DestroyMenu(hPopupMenu);
+ }
+}
+
+void HistoryWindow::DoImport(IImport::ImportType type)
+{
+ ExportManager exp(hWnd, hContact, GetFilterNr());
+ std::vector<IImport::ExternalMessage> messages;
+ std::wstring err;
+ if(exp.Import(type, messages, &err))
+ {
+ int act = MessageBox(hWnd, TranslateT("Do you want save imported messages to local profile?"), TranslateT("Import"), MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON2);
+ if(act == IDYES)
+ {
+ MargeMessages(messages);
+ HistoryWindow::RebuildEvents(hContact);
+ }
+ else if(act == IDNO)
+ {
+ EventList::AddImporter(hContact, type, exp.GetFileName());
+ HistoryWindow::RebuildEvents(hContact);
+ }
+ }
+ else if(!err.empty())
+ {
+ MessageBox(hWnd, err.c_str(), TranslateT("Error"), MB_ICONERROR);
+ }
+}
+
+void HistoryWindow::DeleteToolbarClicked(LPNMTOOLBAR lpnmTB)
+{
+ RECT rc;
+ SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT, (WPARAM)lpnmTB->iItem, (LPARAM)&rc);
+ MapWindowPoints(lpnmTB->hdr.hwndFrom, HWND_DESKTOP, (LPPOINT)&rc, 2);
+ HMENU hPopupMenu = CreatePopupMenu();
+ if(hPopupMenu != NULL)
+ {
+ AppendMenu(hPopupMenu, MF_STRING, IDM_DELETE, TranslateT("Delete"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEGROUP, TranslateT("Delete Group"));
+ AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEUSER, TranslateT("Delete All User History"));
+ SetMenuDefaultItem(hPopupMenu, IDM_DELETE, FALSE);
+
+ int selected = TrackPopupMenu(hPopupMenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, hWnd, 0);
+ switch (selected)
+ {
+ case IDM_DELETE:
+ Delete(0);
+ break;
+ case IDM_DELETEGROUP:
+ Delete(1);
+ break;
+ case IDM_DELETEUSER:
+ Delete(2);
+ break;
+ }
+
+ DestroyMenu(hPopupMenu);
+ }
+}
+
+void HistoryWindow::Delete(int what)
+{
+ int toDelete = 1;
+ size_t start = 0;
+ size_t end = 0;
+ if(selected < 0 || selected >= (int)eventList.size() || what > 2 || what < 0)
+ return;
+ if(what == 0)
+ {
+ CHARRANGE chrg;
+ SendMessage(editWindow,EM_EXGETSEL,0,(LPARAM)&chrg);
+ if(chrg.cpMin == 0 && chrg.cpMax == -1)
+ {
+ toDelete = (int)currentGroup.size();
+ }
+ else
+ {
+ while(start < currentGroup.size() && chrg.cpMin >= currentGroup[start].endPos) ++start;
+ end = start;
+ while(end < currentGroup.size() && chrg.cpMax > currentGroup[end].endPos) ++end;
+ if(start >= currentGroup.size())
+ return;
+ if(end < currentGroup.size())
+ ++end;
+ toDelete = (int)(end - start);
+ }
+ }
+ else if(what == 1)
+ {
+ end = currentGroup.size();
+ toDelete = (int)end;
+ }
+ else
+ {
+ if(eventList.size() == 0)
+ return;
+ toDelete = 1;
+ }
+
+ if(toDelete == 0)
+ return;
+ TCHAR message[256];
+ if(what == 2)
+ _tcscpy_s(message, TranslateT("This operation will PERMANENTLY REMOVE all history for this contact.\nAre you sure you want to do this?"));
+ else
+ _stprintf_s(message, TranslateT("Number of history items to delete: %d.\nAre you sure you want to do this?"), toDelete);
+ if(MessageBox(hWnd, message, TranslateT("Are You sure?"), MB_OKCANCEL | MB_ICONERROR) != IDOK)
+ return;
+
+ bool areImpMessages = false;
+ bool rebuild = false;
+ if(what == 2)
+ {
+ for(size_t j = 0; j < eventList.size(); ++j)
+ {
+ for(size_t i = 0; i < eventList[j].size(); ++i)
+ {
+ DeleteEvent(eventList[j][i]);
+ }
+ }
+
+ areImpMessages = EventList::IsImportedHistory(hContact);
+ rebuild = true;
+ }
+ else
+ {
+ for(size_t i = start; i < end; ++i)
+ {
+ EventIndex& ev = eventList[selected][i];
+ DeleteEvent(ev);
+ areImpMessages |= ev.isExternal;
+ }
+
+ if(start == 0 && end == currentGroup.size())
+ {
+ rebuild = true;
+ }
+ else
+ {
+ rebuild = false;
+ }
+ }
+
+ if(areImpMessages)
+ {
+ TCHAR *message = TranslateT("Do you want delete all imported messages for this contact?\nNote that next scheduler task import this messages again.");
+ if(MessageBox(hWnd, message, TranslateT("Are You sure?"), MB_YESNO | MB_ICONERROR) == IDYES)
+ {
+ EventList::DeleteImporter(hContact);
+ rebuild = true;
+ }
+ }
+
+ if(rebuild)
+ {
+ RebuildEvents(hContact);
+ }
+ else
+ {
+ RebuildGroup(selected);
+ SelectEventGroup(selected);
+ }
+}
+
+bool HistoryWindow::ContactChanged(bool sync)
+{
+ if(!isLoading)
+ {
+ HANDLE hItem = (HANDLE)SendDlgItemMessage(hWnd, IDC_LIST_CONTACTS, CLM_GETSELECTION, 0, 0);
+ if(hItem != NULL)
+ {
+ int typeOf = SendDlgItemMessage(hWnd, IDC_LIST_CONTACTS, CLM_GETITEMTYPE,(WPARAM)hItem,0);
+ if(typeOf == CLCIT_CONTACT)
+ {
+ if(hContact != hItem)
+ {
+ ChangeToFreeWindow(this);
+ isLoading = true;
+ hContact = hItem;
+ ReloadContacts();
+ if(sync)
+ FillHistoryThread(this);
+ else
+ mir_forkthread(HistoryWindow::FillHistoryThread, this);
+ return true;
+ }
+ }
+ else if(typeOf == CLCIT_INFO && hSystem == hItem)
+ {
+ if(hContact != NULL)
+ {
+ ChangeToFreeWindow(this);
+ isLoading = true;
+ hContact = NULL;
+ ReloadContacts();
+ if(sync)
+ FillHistoryThread(this);
+ else
+ mir_forkthread(HistoryWindow::FillHistoryThread, this);
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void HistoryWindow::GroupImagesChanged()
+{
+ if(isGroupImages != Options::instance->groupShowEvents)
+ {
+ isGroupImages = Options::instance->groupShowEvents;
+ if(isGroupImages)
+ {
+ ListView_SetImageList(listWindow, himlSmall, LVSIL_SMALL);
+ }
+ else
+ {
+ ListView_SetImageList(listWindow, himlNone, LVSIL_SMALL);
+ }
+ }
+}
+
+void HistoryWindow::FormatQuote(std::wstring& quote, const MessageData& md, const std::wstring& msg)
+{
+ if(md.isMe)
+ quote += myName;
+ else
+ quote += contactName;
+ TCHAR str[32];
+ tmi.printTimeStamp(NULL, md.timestamp, _T("d t"), str , 32, 0);
+ quote += _T(", ");
+ quote += str;
+ quote += _T("\n");
+ int f = 0;
+ do
+ {
+ int nf = (int)msg.find_first_of(_T("\r\n"), f);
+ if(nf >= 0 && nf < (int)msg.length())
+ {
+ if(nf - f >= 0 )
+ {
+ quote += _T(">");
+ quote += msg.substr(f, nf - f);
+ quote += _T("\n");
+ }
+
+ f = nf + 1;
+ if(msg[nf] == _T('\r') && f < (int)msg.length() && msg[f] == _T('\n'))
+ ++f;
+ }
+ else if(msg.length() - f > 0)
+ {
+ quote += _T(">");
+ quote += msg.substr(f, msg.length() - f);
+ quote += _T("\n");
+ f = -1;
+ }
+ else
+ f = -1;
+ }
+ while(f > 0 && f < (int)msg.length());
+}
+
+HANDLE HistoryWindow::GetNextContact(HANDLE hContact, int adder)
+{
+ HWND contactList = GetDlgItem(hWnd,IDC_LIST_CONTACTS);
+ bool find = false;
+ HANDLE _hContact;
+ if(adder > 0)
+ {
+ if(hContact != NULL)
+ {
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ while(_hContact)
+ {
+ HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, (WPARAM)_hContact, 0);
+ if(hItem != NULL)
+ {
+ find = true;
+ break;
+ }
+
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)_hContact, 0);
+ }
+
+ if(!find && EventList::GetContactMessageNumber(NULL))
+ {
+ _hContact = NULL;
+ find = true;
+ }
+ }
+
+ if(!find)
+ {
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while(_hContact && _hContact != hContact)
+ {
+ HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, (WPARAM)_hContact, 0);
+ if(hItem != NULL)
+ {
+ find = true;
+ break;
+ }
+
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)_hContact, 0);
+ }
+ }
+ }
+ else
+ {
+ HANDLE lastContact = NULL;
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while(_hContact && _hContact != hContact)
+ {
+ HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, (WPARAM)_hContact, 0);
+ if(hItem != NULL)
+ {
+ lastContact = _hContact;
+ }
+
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)_hContact, 0);
+ }
+
+ if(hContact != NULL)
+ {
+ if(lastContact == NULL && !EventList::GetContactMessageNumber(NULL))
+ {
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ while(_hContact)
+ {
+ HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, (WPARAM)_hContact, 0);
+ if(hItem != NULL)
+ {
+ lastContact = _hContact;
+ }
+
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)_hContact, 0);
+ }
+ }
+
+ if(lastContact != NULL || EventList::GetContactMessageNumber(NULL))
+ {
+ _hContact = lastContact;
+ find = true;
+ }
+ }
+ else if(lastContact != NULL)
+ {
+ _hContact = lastContact;
+ find = true;
+ }
+ }
+
+ if(find)
+ {
+ return _hContact;
+ }
+ else
+ {
+ return hContact;
+ }
+}
+
+void HistoryWindow::SelectContact(HANDLE _hContact)
+{
+ if(hContact != _hContact)
+ {
+ HWND contactList = GetDlgItem(hWnd,IDC_LIST_CONTACTS);
+ if(_hContact != NULL)
+ {
+ HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, (WPARAM)_hContact, 0);
+ if(hItem != NULL)
+ {
+ SendMessage(contactList, CLM_ENSUREVISIBLE, (WPARAM)hItem, 0);
+ SendMessage(contactList, CLM_SELECTITEM, (WPARAM)hItem, 0);
+ }
+ }
+ else
+ {
+ SendMessage(contactList, CLM_ENSUREVISIBLE, (WPARAM)hSystem, 0);
+ SendMessage(contactList, CLM_SELECTITEM, (WPARAM)hSystem, 0);
+ }
+
+ while(isLoading)
+ Sleep(100);
+ ContactChanged(true);
+ }
+}
+
+void ResetCList(HWND hWnd)
+{
+ COLORREF bkCLColor = Options::instance->GetColor(Options::ContactListBackground);
+ SendDlgItemMessage(hWnd, IDC_LIST_CONTACTS, CLM_SETBKBITMAP, 0, (LPARAM)(HBITMAP) NULL);
+ SendDlgItemMessage(hWnd, IDC_LIST_CONTACTS, CLM_SETBKCOLOR, bkCLColor, 0);
+ SendDlgItemMessage(hWnd, IDC_LIST_CONTACTS, CLM_SETUSEGROUPS, Options::instance->showContactGroups, 0);
+}
diff --git a/plugins/BasicHistory/src/HistoryWindow.h b/plugins/BasicHistory/src/HistoryWindow.h
new file mode 100644
index 0000000000..8a31d9194a
--- /dev/null
+++ b/plugins/BasicHistory/src/HistoryWindow.h
@@ -0,0 +1,95 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+#include "Searcher.h"
+
+class HistoryWindow : public SearchContext
+{
+private:
+ HistoryWindow(HANDLE _hContact);
+ void Initialise();
+ void Destroy();
+ void SplitterMoved(HWND splitter, LONG pos, bool screenPos);
+ static INT_PTR CALLBACK DlgProcHistory(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+ static LRESULT CALLBACK SplitterSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ static int HistoryDlgResizer(HWND, LPARAM, UTILRESIZECONTROL *urc);
+ static void FillHistoryThread(void* param);
+ static void Close(HistoryWindow* historyWindow);
+ static void ChangeToFreeWindow(HistoryWindow* historyWindow);
+ void ReplaceIcons(HWND hwndDlg, int selStart, BOOL isSent);
+ void EnableWindows(BOOL enable);
+ void ReloadContacts();
+ bool DoHotkey(UINT msg, LPARAM lParam, WPARAM wParam, int window);
+ void RestorePos();
+ void SavePos(bool all);
+ void FindToolbarClicked(LPNMTOOLBAR lpnmTB);
+ void ConfigToolbarClicked(LPNMTOOLBAR lpnmTB);
+ void DeleteToolbarClicked(LPNMTOOLBAR lpnmTB);
+ void Delete(int what);
+ bool ContactChanged(bool sync = false);
+ void GroupImagesChanged();
+ void FormatQuote(std::wstring& quote, const MessageData& md, const std::wstring& msg);
+ void FontsChanged();
+ void ReloadMainOptions();
+ void DoImport(IImport::ImportType type);
+
+ static std::map<HANDLE, HistoryWindow*> windows;
+ static std::vector<HistoryWindow*> freeWindows;
+ bool isDestroyed;
+ LONG splitterY;
+ LONG splitterOrgY;
+ LONG splitterX;
+ LONG splitterOrgX;
+ HICON *eventIcoms;
+ int allIconNumber;
+ HICON plusIco, minusIco, findNextIco, findPrevIco, configIco, deleteIco;
+ WNDPROC OldSplitterProc;
+ bool isContactList;
+ LONG listOryginalPos;
+ bool isLoading;
+ Searcher searcher;
+ bool isGroupImages;
+ HIMAGELIST himlSmall, himlNone;
+ HBRUSH bkBrush;
+ HBRUSH bkFindBrush;
+ HANDLE hSystem;
+ HWND splitterXhWnd, splitterYhWnd;
+protected:
+ virtual void AddGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText, int ico);
+public:
+ ~HistoryWindow();
+ static void Deinit();
+ static void Open(HANDLE hContact);
+ static void Close(HANDLE hContact);
+ static void RebuildEvents(HANDLE hContact);
+ static bool IsInList(HWND hWnd);
+ static int FontsChanged(WPARAM wParam, LPARAM lParam);
+ static INT_PTR DeleteAllUserHistory(WPARAM wParam, LPARAM lParam);
+ static void OptionsMainChanged();
+ static void OptionsGroupChanged();
+ static void OptionsSearchingChanged();
+ void Show();
+ void Focus();
+
+ // SearchContext interface
+ virtual void SelectEventGroup(int sel);
+ virtual HANDLE GetNextContact(HANDLE hContact, int adder);
+ virtual void SelectContact(HANDLE _hContact);
+};
+
diff --git a/plugins/BasicHistory/src/HotkeyHelper.cpp b/plugins/BasicHistory/src/HotkeyHelper.cpp
new file mode 100644
index 0000000000..eeb537456a
--- /dev/null
+++ b/plugins/BasicHistory/src/HotkeyHelper.cpp
@@ -0,0 +1,105 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "HotkeyHelper.h"
+
+LRESULT CALLBACK HotkeySubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+void RegisterHotkeyControl(HWND control)
+{
+ WNDPROC oldProc = (WNDPROC)SetWindowLongPtr(control, GWLP_WNDPROC, (LONG_PTR) HotkeySubclassProc);
+ SetWindowLongPtr(control, GWLP_USERDATA, (LONG_PTR) oldProc);
+}
+
+void UnregisterHotkeyControl(HWND control)
+{
+ WNDPROC oldProc = (WNDPROC)GetWindowLongPtr(control, GWLP_USERDATA);
+ if(oldProc != NULL)
+ {
+ SetWindowLongPtr(control, GWLP_WNDPROC, (LONG_PTR) oldProc);
+ SetWindowLongPtr(control, GWLP_USERDATA, NULL);
+ }
+}
+
+static bool isPresed = false;
+
+LRESULT CALLBACK HotkeySubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WNDPROC oldProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_UNICHAR:
+ case WM_DEADCHAR:
+ case WM_SYSDEADCHAR:
+ if(isPresed)
+ return 0;
+ break;
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ if(isPresed)
+ {
+ isPresed = false;
+ return 0;
+ }
+ break;
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ {
+ isPresed = false;
+ HWND hwndParent = GetParent(hwnd);
+ MSGFILTER filter;
+ filter.msg = msg;
+ filter.lParam = lParam;
+ filter.wParam = wParam;
+ filter.nmhdr.hwndFrom = hwnd;
+ filter.nmhdr.code = EN_MSGFILTER;
+ filter.nmhdr.idFrom = GetDlgCtrlID(hwnd);
+ if(SendMessage(hwndParent, WM_NOTIFY, NULL, (LPARAM)&filter))
+ {
+ isPresed = true;
+ return 0;
+ }
+
+ LRESULT res = CallWindowProc(oldProc, hwnd, msg, wParam, lParam);
+ filter.nmhdr.code = CLN_MYSELCHANGED;
+ SendMessage(hwndParent, WM_NOTIFY, NULL, (LPARAM)&filter);
+ return res;
+ }
+ break;
+ case WM_LBUTTONDOWN:
+ {
+ HWND hwndParent = GetParent(hwnd);
+ MSGFILTER filter;
+ filter.msg = msg;
+ filter.lParam = lParam;
+ filter.wParam = wParam;
+ filter.nmhdr.hwndFrom = hwnd;
+ filter.nmhdr.code = CLN_MYSELCHANGED;
+ filter.nmhdr.idFrom = GetDlgCtrlID(hwnd);
+
+ LRESULT res = CallWindowProc(oldProc, hwnd, msg, wParam, lParam);
+ SendMessage(hwndParent, WM_NOTIFY, NULL, (LPARAM)&filter);
+ return res;
+ }
+ break;
+ }
+ return CallWindowProc(oldProc, hwnd, msg, wParam, lParam);
+} \ No newline at end of file
diff --git a/plugins/BasicHistory/src/HotkeyHelper.h b/plugins/BasicHistory/src/HotkeyHelper.h
new file mode 100644
index 0000000000..f31b1c6666
--- /dev/null
+++ b/plugins/BasicHistory/src/HotkeyHelper.h
@@ -0,0 +1,24 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; 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
+
+#define CLN_MYSELCHANGED (CLN_FIRST-50)
+
+void RegisterHotkeyControl(HWND control);
+void UnregisterHotkeyControl(HWND control);
diff --git a/plugins/BasicHistory/src/IExport.h b/plugins/BasicHistory/src/IExport.h
new file mode 100644
index 0000000000..d024a084e4
--- /dev/null
+++ b/plugins/BasicHistory/src/IExport.h
@@ -0,0 +1,49 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; 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
+class IExport
+{
+protected:
+ std::wostream* stream;
+public:
+ enum ExportType
+ {
+ RichHtml,
+ PlainHtml,
+ Txt,
+ Binary,
+ Dat
+ };
+
+ void SetStream(std::wostream *str)
+ {
+ stream = str;
+ }
+
+ virtual const TCHAR* GetExt() = 0;
+ virtual void WriteHeader(const std::wstring &fileName, const std::wstring &filterName, const std::wstring &myName, const std::wstring &myId, const std::wstring &name1, const std::wstring &proto1, const std::wstring &id1, const std::string& baseProto1, const std::wstring& encoding) = 0;
+ virtual void WriteFooter() = 0;
+ virtual void WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText) = 0;
+ virtual void WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei) = 0;
+
+ virtual ~IExport()
+ {
+ }
+};
+
diff --git a/plugins/BasicHistory/src/IImport.h b/plugins/BasicHistory/src/IImport.h
new file mode 100644
index 0000000000..aef3b35179
--- /dev/null
+++ b/plugins/BasicHistory/src/IImport.h
@@ -0,0 +1,53 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; 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
+
+class IImport
+{
+protected:
+ std::istream* stream;
+public:
+ enum ImportType
+ {
+ Binary,
+ Dat
+ };
+
+ struct ExternalMessage
+ {
+ DWORD timestamp;
+ WORD eventType;
+ WORD flags;
+ std::wstring message;
+ };
+
+ void SetStream(std::istream *str)
+ {
+ stream = str;
+ }
+
+ virtual const TCHAR* GetExt() = 0;
+ virtual int IsContactInFile(const std::vector<HANDLE>& contacts) = 0;
+ virtual bool GetEventList(std::vector<ExternalMessage>& eventList) = 0;
+
+ virtual ~IImport()
+ {
+ }
+};
+
diff --git a/plugins/BasicHistory/src/ImageDataObject.cpp b/plugins/BasicHistory/src/ImageDataObject.cpp
new file mode 100644
index 0000000000..15cb67f6ce
--- /dev/null
+++ b/plugins/BasicHistory/src/ImageDataObject.cpp
@@ -0,0 +1,171 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; 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/>.
+*/
+
+//code taken partly from public example on the internet, source unknown.
+
+#include "StdAfx.h"
+#include "ImageDataObject.h"
+
+HBITMAP CacheIconToBMP(HICON hIcon, COLORREF backgroundColor, int sizeX, int sizeY)
+{
+ RECT rc;
+ HBRUSH hBkgBrush = CreateSolidBrush(backgroundColor);
+ rc.top = rc.left = 0;
+ rc.right = sizeY;
+ rc.bottom = sizeX;
+ HDC hdc = GetDC(0);
+ HBITMAP hBmp = CreateCompatibleBitmap(hdc, sizeY, sizeX);
+ HDC hdcMem = CreateCompatibleDC(hdc);
+ HBITMAP hoBmp = (HBITMAP)SelectObject(hdcMem, hBmp);
+ FillRect(hdcMem, &rc, hBkgBrush);
+ DrawIconEx(hdcMem, 0, 0, hIcon, sizeY, sizeX, 0, NULL, DI_NORMAL);
+ SelectObject(hdcMem, hoBmp);
+ DeleteDC(hdcMem);
+ ReleaseDC(NULL, hdc);
+ DeleteObject(hBkgBrush);
+ return hBmp;
+}
+
+// returns true on success, false on failure
+bool ImageDataObject::InsertIcon(IRichEditOle* pRichEditOle, HICON hIcon,
+ COLORREF backgroundColor, int sizeX, int sizeY)
+{
+ bool result;
+ HBITMAP hBmp = CacheIconToBMP(hIcon, backgroundColor, sizeX, sizeY);
+ result = InsertBitmap(pRichEditOle, hBmp);
+ DeleteObject(hBmp);
+ return result;
+}
+
+// returns true on success, false on failure
+bool ImageDataObject::InsertBitmap(IRichEditOle* pRichEditOle, HBITMAP hBitmap)
+{
+ SCODE sc;
+ BITMAP bminfo;
+
+ // Get the image data object
+ //
+ ImageDataObject *pods = new ImageDataObject;
+
+ GetObject(hBitmap, sizeof(bminfo), &bminfo);
+ pods->SetBitmap(hBitmap);
+
+ // Get the RichEdit container site
+ //
+ IOleClientSite *pOleClientSite;
+ pRichEditOle->GetClientSite(&pOleClientSite);
+
+ // Initialize a Storage Object
+ //
+ IStorage *pStorage;
+
+ LPLOCKBYTES lpLockBytes = NULL;
+ sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
+ if (sc != S_OK) {
+ pOleClientSite->Release();
+ return false;
+ }
+ sc = ::StgCreateDocfileOnILockBytes(lpLockBytes,
+ STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_READWRITE, 0, &pStorage);
+ if (sc != S_OK) {
+ lpLockBytes = NULL;
+ pOleClientSite->Release();
+ return false;
+ }
+ // The final ole object which will be inserted in the richedit control
+ //
+ IOleObject *pOleObject;
+ pOleObject = pods->GetOleObject(pOleClientSite, pStorage);
+ if (pOleObject == NULL) {
+ pStorage->Release();
+ pOleClientSite->Release();
+ return false;
+ }
+
+ // all items are "contained" -- this makes our reference to this object
+ // weak -- which is needed for links to embedding silent update.
+ OleSetContainedObject(pOleObject, TRUE);
+
+ // Now Add the object to the RichEdit
+ //
+ REOBJECT reobject;
+ ZeroMemory(&reobject, sizeof(REOBJECT));
+ reobject.cbStruct = sizeof(REOBJECT);
+
+ CLSID clsid;
+ sc = pOleObject->GetUserClassID(&clsid);
+ if (sc != S_OK) {
+ pOleObject->Release();
+ pStorage->Release();
+ pOleClientSite->Release();
+ return false;
+ }
+
+ reobject.clsid = clsid;
+ reobject.cp = REO_CP_SELECTION ;
+ reobject.dvaspect = DVASPECT_CONTENT;
+ reobject.poleobj = pOleObject;
+ reobject.polesite = pOleClientSite;
+ reobject.pstg = pStorage;
+ reobject.dwFlags = bminfo.bmHeight <= 12 ? 0 : REO_BELOWBASELINE;
+
+ // Insert the bitmap at the current location in the richedit control
+ //
+ sc = pRichEditOle->InsertObject(&reobject);
+
+ // Release all unnecessary interfaces
+ //
+ pOleObject->Release();
+ pOleClientSite->Release();
+ lpLockBytes->Release();
+ pStorage->Release();
+ if (sc != S_OK)
+ return false;
+ else
+ return true;
+}
+
+
+void ImageDataObject::SetBitmap(HBITMAP hBitmap)
+{
+ STGMEDIUM stgm;
+ stgm.tymed = TYMED_GDI; // Storage medium = HBITMAP handle
+ stgm.hBitmap = hBitmap;
+ stgm.pUnkForRelease = NULL; // Use ReleaseStgMedium
+
+ FORMATETC fm;
+ fm.cfFormat = CF_BITMAP; // Clipboard format = CF_BITMAP
+ fm.ptd = NULL; // Target Device = Screen
+ fm.dwAspect = DVASPECT_CONTENT; // Level of detail = Full content
+ fm.lindex = -1; // Index = Not applicaple
+ fm.tymed = TYMED_GDI; // Storage medium = HBITMAP handle
+
+ this->SetData(&fm, &stgm, TRUE);
+}
+
+
+IOleObject *ImageDataObject::GetOleObject(IOleClientSite *pOleClientSite, IStorage *pStorage)
+{
+ SCODE sc;
+ IOleObject *pOleObject;
+ sc = ::OleCreateStaticFromData(this, IID_IOleObject, OLERENDER_FORMAT,
+ &m_format, pOleClientSite, pStorage, (void **) & pOleObject);
+ if (sc != S_OK)
+ pOleObject = NULL;
+ return pOleObject;
+}
diff --git a/plugins/BasicHistory/src/ImageDataObject.h b/plugins/BasicHistory/src/ImageDataObject.h
new file mode 100644
index 0000000000..309c220e36
--- /dev/null
+++ b/plugins/BasicHistory/src/ImageDataObject.h
@@ -0,0 +1,133 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; 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/>.
+*/
+
+//code taken partly from public example on the internet, source unknown.
+
+#pragma once
+
+class ImageDataObject : IDataObject
+{
+public:
+ // returns true on success, false on failure
+ static bool InsertBitmap(IRichEditOle* pRichEditOle, HBITMAP hBitmap);
+ // returns true on success, false on failure
+ static bool InsertIcon(IRichEditOle* pRichEditOle, HICON hIcon,
+ COLORREF backgroundColor, int sizeX = 0, int sizeY = 0);
+
+private:
+ ULONG m_ulRefCnt;
+ BOOL m_bRelease;
+
+ STGMEDIUM m_stgmed;
+ FORMATETC m_format;
+
+ // ULONG m_nIndex; // current enumerator index
+
+public:
+ ImageDataObject() : m_ulRefCnt(0)
+ {
+ m_bRelease = FALSE;
+ }
+
+ ~ImageDataObject()
+ {
+ if (m_bRelease)
+ ::ReleaseStgMedium(&m_stgmed);
+ }
+
+ // IUnknown interface
+ STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
+ {
+ if (iid == IID_IUnknown || iid == IID_IDataObject)
+ {
+ *ppvObject = this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ return E_NOINTERFACE;
+ }
+
+ STDMETHOD_(ULONG, AddRef)(void)
+ {
+ m_ulRefCnt++;
+ return m_ulRefCnt;
+ }
+
+ STDMETHOD_(ULONG, Release)(void)
+ {
+ m_ulRefCnt--;
+ if (m_ulRefCnt == 0)
+ delete this;
+ return m_ulRefCnt;
+ }
+
+ // IDataObject Interface
+ STDMETHOD(GetData)(FORMATETC *pformatetcIn, STGMEDIUM *pmedium) {
+ HANDLE hDst;
+ hDst = ::OleDuplicateData(m_stgmed.hBitmap, CF_BITMAP, 0);
+ if (hDst == NULL)
+ return E_HANDLE;
+
+ pmedium->tymed = TYMED_GDI;
+ pmedium->hBitmap = (HBITMAP)hDst;
+ pmedium->pUnkForRelease = NULL;
+ return S_OK;
+ }
+
+ STDMETHOD(GetDataHere)(FORMATETC* pformatetc, STGMEDIUM* pmedium ) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(QueryGetData)(FORMATETC* pformatetc ) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(GetCanonicalFormatEtc)(FORMATETC* pformatectIn ,FORMATETC* pformatetcOut ) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(SetData)(FORMATETC* pformatetc , STGMEDIUM* pmedium , BOOL fRelease ) {
+ m_format = *pformatetc;
+ m_stgmed = *pmedium;
+
+ return S_OK;
+ }
+
+ STDMETHOD(EnumFormatEtc)(DWORD dwDirection , IEnumFORMATETC** ppenumFormatEtc ) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(DAdvise)(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink,
+ DWORD *pdwConnection) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(DUnadvise)(DWORD dwConnection) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise) {
+ return E_NOTIMPL;
+ }
+
+ // Other
+ void SetBitmap(HBITMAP hBitmap);
+ IOleObject *GetOleObject(IOleClientSite *pOleClientSite, IStorage *pStorage);
+};
+
diff --git a/plugins/BasicHistory/src/Options.cpp b/plugins/BasicHistory/src/Options.cpp
new file mode 100644
index 0000000000..218e721f03
--- /dev/null
+++ b/plugins/BasicHistory/src/Options.cpp
@@ -0,0 +1,2170 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "Options.h"
+#include "resource.h"
+
+extern HINSTANCE hInst;
+extern bool g_SmileyAddAvail;
+extern bool bPopupsEnabled;
+
+#define MODULE "BasicHistory"
+
+Options *Options::instance;
+
+struct EventNamesType
+{
+ int id;
+ TCHAR* name;
+} EventNames[] =
+{
+ EVENTTYPE_MESSAGE, LPGENT("Message"),
+ EVENTTYPE_FILE, LPGENT("File transfer"),
+ EVENTTYPE_URL, LPGENT("Link"),
+ EVENTTYPE_STATUSCHANGE, LPGENT("Status change"),
+ EVENTTYPE_AUTHREQUEST, LPGENT("Authorization request"),
+ EVENTTYPE_ADDED, LPGENT("You were added"),
+ EVENTTYPE_CONTACTS, LPGENT("Contacts recieved"),
+ EVENTTYPE_SMTPSIMPLE, LPGENT("SMTP Simple Email"),
+ ICQEVENTTYPE_SMS, LPGENT("SMS message")
+};
+
+struct TCpTable {
+ UINT cpId;
+ TCHAR *cpName;
+}
+cpTable[] = {
+ { CP_UTF8, _T("UTF-8") },
+ { 1250, _T("windows-1250") },
+ { 1251, _T("windows-1251") },
+ { 1252, _T("windows-1252") },
+ { 1253, _T("windows-1253") },
+ { 1254, _T("windows-1254") },
+ { 1255, _T("windows-1255") },
+ { 1256, _T("windows-1256") },
+ { 1257, _T("windows-1257") },
+ { 1258, _T("windows-1258") },
+ { 28591, _T("iso-8859-1") },
+ { 28592, _T("iso-8859-2") },
+ { 28593, _T("iso-8859-3") },
+ { 28594, _T("iso-8859-4") },
+ { 28595, _T("iso-8859-5") },
+ { 28596, _T("iso-8859-6") },
+ { 28597, _T("iso-8859-7") },
+ { 28598, _T("iso-8859-8") },
+ { 28599, _T("iso-8859-9") },
+ { 28603, _T("iso-8859-13") },
+ { 28605, _T("iso-8859-15") },
+};
+
+Options::Options()
+{
+ showContacts = false;
+ showContactGroups = true;
+ groupNewOnTop = true;
+ groupShowEvents = true;
+ groupShowTime = true;
+ groupShowName = false;
+ groupShowMessage = true;
+ groupMessageLen = 43;
+ groupTime = 5;
+ groupMessagesNumber = 100;
+ messagesNewOnTop = false;
+ messagesShowDate = false;
+ messagesShowSec = false;
+ messagesShowName = true;
+ messagesShowEvents = false;
+ messagesUseSmileys = true;
+ searchForInList = true;
+ searchForInMess = true;
+ searchMatchCase = false;
+ searchMatchWhole = false;
+ searchOnlyIn = false;
+ searchOnlyOut = false;
+ searchOnlyGroup = false;
+ searchAllContacts = false;
+ schedulerAlerts = true;
+ schedulerHistoryAlerts = true;
+ defFilter = 0;
+ codepageTxt = CP_UTF8;
+ codepageHtml1 = CP_UTF8;
+ codepageHtml2 = CP_UTF8;
+ encodingTxt = _T("UTF-8");
+ encodingHtml1 = _T("UTF-8");
+ encodingHtml2 = _T("UTF-8");
+ exportHtml1ShowDate = true;
+ exportHtml2ShowDate = false;
+ exportHtml2UseSmileys = true;
+}
+
+Options::~Options()
+{
+}
+
+int Options::InitOptions(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+
+ odp.cbSize = sizeof(odp);
+ odp.position = 100000000;
+ odp.hInstance = hInst;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.ptszTitle = LPGENT("History");
+
+ odp.ptszTab = LPGENT("Group list");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GROUPLIST);
+ odp.pfnDlgProc = Options::DlgProcOptsGroupList;
+ Options_AddPage(wParam, &odp);
+
+ odp.ptszTab = LPGENT("Messages");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_MESSAGES);
+ odp.pfnDlgProc = Options::DlgProcOptsMessages;
+ Options_AddPage(wParam, &odp);
+
+ odp.ptszTab = LPGENT("Searching");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SEARCHING);
+ odp.pfnDlgProc = Options::DlgProcOptsSearching;
+ Options_AddPage(wParam, &odp);
+
+ odp.ptszTab = LPGENT("Export");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_EXPORT);
+ odp.pfnDlgProc = Options::DlgProcOptsExport;
+ Options_AddPage(wParam, &odp);
+
+ odp.ptszTab = LPGENT("Scheduler");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SCHEDULER);
+ odp.pfnDlgProc = Options::DlgProcOptsScheduler;
+ Options_AddPage(wParam, &odp);
+
+ odp.ptszTab = LPGENT("Advanced");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_MAIN);
+ odp.pfnDlgProc = Options::DlgProcOptsMain;
+ Options_AddPage(wParam, &odp);
+
+ return 0;
+}
+
+struct FontOptionsList {
+ TCHAR* szDescr;
+ COLORREF defColour;
+ TCHAR* szDefFace;
+ BYTE defStyle;
+ char defSize;
+ TCHAR* szBackgroundName;
+ DWORD flags;
+};
+
+struct ColorOptionsList {
+ TCHAR* tszName;
+ COLORREF def;
+};
+
+struct HotkeyOptionsList {
+ const char *pszName;
+ const TCHAR *ptszDescription;
+ const TCHAR *ptszSection;
+ const char *pszService;
+ WORD DefHotKey;
+ LPARAM lParam;
+};
+
+static FontOptionsList g_FontOptionsList[] = {
+ {LPGENT(">> Outgoing timestamp"), RGB(0, 0, 0), _T("MS Shell Dlg 2"), DBFONTF_BOLD, -11, LPGENT("Outgoing background"), FIDF_ALLOWEFFECTS},
+ {LPGENT("<< Incoming timestamp"), RGB(0, 0, 0), _T("MS Shell Dlg 2"), DBFONTF_BOLD, -11, LPGENT("Incoming background"), FIDF_ALLOWEFFECTS},
+ {LPGENT(">> Outgoing name"), RGB(100,100,100), _T("MS Shell Dlg 2"), DBFONTF_BOLD, -11, LPGENT("Outgoing background"), FIDF_ALLOWEFFECTS},
+ {LPGENT("<< Incoming name"), RGB(90,160,90), _T("MS Shell Dlg 2"), DBFONTF_BOLD, -11, LPGENT("Incoming background"), FIDF_ALLOWEFFECTS},
+ {LPGENT(">> Outgoing messages"), RGB(0, 0, 0), _T("MS Shell Dlg 2"), 0, -11, LPGENT("Outgoing background"), FIDF_ALLOWEFFECTS},
+ {LPGENT("<< Incoming messages"), RGB(0, 0, 0), _T("MS Shell Dlg 2"), 0, -11, LPGENT("Incoming background"), FIDF_ALLOWEFFECTS},
+ {LPGENT("Group list"), RGB(0, 0, 0), _T("MS Shell Dlg 2"), 0, -11, LPGENT("Group list background"), FIDF_DISABLESTYLES},
+ {LPGENT("Find window"), RGB(0, 0, 0), _T("MS Shell Dlg 2"), 0, -11, LPGENT("Find window background"), FIDF_DISABLESTYLES},
+};
+
+static ColorOptionsList g_ColorOptionsList[] = {
+ LPGENT("Outgoing background"), RGB(245,245,255),
+ LPGENT("Incoming background"), RGB(245,255,245),
+ LPGENT("Group list background"), GetSysColor(COLOR_3DFACE),
+ LPGENT("Window background"), GetSysColor(COLOR_3DFACE),
+ LPGENT("Contact list background"), GetSysColor(COLOR_3DFACE),
+ LPGENT("Find window background"), GetSysColor(COLOR_WINDOW),
+};
+
+static HotkeyOptionsList g_HotkeyOptionsList[] = {
+ { "basichistory_hot_showall", LPGENT("Open global history"), LPGENT("History"), MS_HISTORY_SHOWCONTACTHISTORY, HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, 'H') | HKF_MIRANDA_LOCAL, 0 },
+ { "basichistory_hot_find", LPGENT("Find"), LPGENT("History"), 0, HOTKEYCODE(HOTKEYF_CONTROL, 'F'), HISTORY_HK_FIND },
+ { "basichistory_hot_findnext", LPGENT("Find Next"), LPGENT("History"), 0, VK_F3, HISTORY_HK_FINDNEXT },
+ { "basichistory_hot_findprev", LPGENT("Find Previous"), LPGENT("History"), 0, VK_F2, HISTORY_HK_FINDPREV },
+ { "basichistory_hot_matchcase", LPGENT("Switch Match Case"), LPGENT("History"), 0, 0, HISTORY_HK_MATCHCASE },
+ { "basichistory_hot_matchwhole", LPGENT("Switch Match Whole Word"), LPGENT("History"), 0, 0, HISTORY_HK_MATCHWHOLE },
+ { "basichistory_hot_showcontacts", LPGENT("Show/Hide Contacts"), LPGENT("History"), 0, 0, HISTORY_HK_SHOWCONTACTS },
+ { "basichistory_hot_onlyin", LPGENT("Switch Only Incomming Messages"), LPGENT("History"), 0, 0, HISTORY_HK_ONLYIN },
+ { "basichistory_hot_onlyout", LPGENT("Switch Only Outgoing Messages"), LPGENT("History"), 0, 0, HISTORY_HK_ONLYOUT },
+ { "basichistory_hot_onlygroup", LPGENT("Switch Only Selected Group"), LPGENT("History"), 0, 0, HISTORY_HK_ONLYGROUP },
+ { "basichistory_hot_allcontacts", LPGENT("Switch All Contacts"), LPGENT("History"), 0, 0, HISTORY_HK_ALLCONTACTS },
+ { "basichistory_hot_delete", LPGENT("Delete"), LPGENT("History"), 0, VK_DELETE, HISTORY_HK_DELETE },
+ { "basichistory_hot_exrhtml", LPGENT("Export To Rich Html"), LPGENT("History"), 0, 0, HISTORY_HK_EXRHTML },
+ { "basichistory_hot_exphtml", LPGENT("Export To Plain Html"), LPGENT("History"), 0, 0, HISTORY_HK_EXPHTML },
+ { "basichistory_hot_extxt", LPGENT("Export To Txt"), LPGENT("History"), 0, 0, HISTORY_HK_EXTXT },
+ { "basichistory_hot_exbin", LPGENT("Export To Binary"), LPGENT("History"), 0, 0, HISTORY_HK_EXBIN },
+ { "basichistory_hot_impbin", LPGENT("Import From Binary"), LPGENT("History"), 0, 0, HISTORY_HK_IMPBIN },
+ { "basichistory_hot_exdat", LPGENT("Export To Dat (mContacts)"), LPGENT("History"), 0, 0, HISTORY_HK_EXDAT },
+ { "basichistory_hot_impdat", LPGENT("Import From Dat (mContacts)"), LPGENT("History"), 0, 0, HISTORY_HK_IMPDAT },
+};
+
+const int g_fontsSize = SIZEOF(g_FontOptionsList);
+
+const int g_colorsSize = SIZEOF(g_ColorOptionsList);
+
+const int g_hotkeysSize = SIZEOF(g_HotkeyOptionsList);
+
+void Options::Unload()
+{
+ DeleteCriticalSection(&criticalSection);
+}
+
+void Options::Load(void)
+{
+ InitializeCriticalSection(&criticalSection);
+ FontIDT fid = {0};
+ ColourIDT cid = {0};
+ HOTKEYDESC hid = {0};
+ fid.cbSize = sizeof(FontIDT);
+ cid.cbSize = sizeof(ColourIDT);
+ hid.cbSize = sizeof(HOTKEYDESC);
+ strncpy_s(fid.dbSettingsGroup, "BasicHistory_Fonts", SIZEOF(fid.dbSettingsGroup));
+ _tcsncpy_s(fid.backgroundGroup, _T("History"), SIZEOF(fid.backgroundGroup));
+ _tcsncpy_s(fid.group, LPGENT("History"), SIZEOF(fid.group));
+ for(int i = 0; i < g_fontsSize; ++i)
+ {
+ fid.order = i;
+ _tcsncpy_s(fid.deffontsettings.szFace, g_FontOptionsList[i].szDefFace, LF_FACESIZE);
+ fid.deffontsettings.size = g_FontOptionsList[i].defSize;
+ fid.deffontsettings.colour = g_FontOptionsList[i].defColour;
+ fid.deffontsettings.style = g_FontOptionsList[i].defStyle;
+ fid.deffontsettings.charset = DEFAULT_CHARSET;
+ sprintf_s(fid.prefix, SIZEOF(fid.prefix), "Font%d", i);
+ _tcsncpy_s(fid.name, g_FontOptionsList[i].szDescr, SIZEOF(fid.name));
+ _tcsncpy_s(fid.backgroundName, g_FontOptionsList[i].szBackgroundName, SIZEOF(fid.name));
+ fid.flags = FIDF_DEFAULTVALID | FIDF_CLASSGENERAL | g_FontOptionsList[i].flags;
+ FontRegisterT(&fid);
+ }
+
+ strncpy_s(cid.dbSettingsGroup, "BasicHistory_Fonts", SIZEOF(fid.dbSettingsGroup));
+ _tcsncpy_s(cid.group, LPGENT("History"), SIZEOF(fid.group));
+ for(int i = 0; i < g_colorsSize; ++i)
+ {
+ _tcsncpy_s(cid.name, g_ColorOptionsList[i].tszName, SIZEOF(cid.name));
+ sprintf_s(cid.setting, SIZEOF(cid.setting), "Color%d", i);
+ cid.order = i;
+ cid.defcolour = g_ColorOptionsList[i].def;
+ ColourRegisterT(&cid);
+ }
+
+ hid.dwFlags = HKD_TCHAR;
+ for(int i = 0; i < g_hotkeysSize; ++i)
+ {
+ hid.pszName = g_HotkeyOptionsList[i].pszName;
+ hid.ptszDescription = g_HotkeyOptionsList[i].ptszDescription;
+ hid.ptszSection = g_HotkeyOptionsList[i].ptszSection;
+ hid.pszService = g_HotkeyOptionsList[i].pszService;
+ hid.DefHotKey = g_HotkeyOptionsList[i].DefHotKey;
+ hid.lParam = g_HotkeyOptionsList[i].lParam;
+ Hotkey_Register(&hid);
+ }
+
+ showContacts = DBGetContactSettingByte(0, MODULE, "showContacts", 0) ? true : false;
+ showContactGroups = DBGetContactSettingByte(0, MODULE, "showContactGroups", 1) ? true : false;
+ groupNewOnTop = DBGetContactSettingByte(0, MODULE, "groupNewOnTop", 1) ? true : false;
+ groupShowEvents = DBGetContactSettingByte(0, MODULE, "groupShowEvents", 1) ? true : false;
+ groupShowTime = DBGetContactSettingByte(0, MODULE, "groupShowTime", 1) ? true : false;
+ groupShowName = DBGetContactSettingByte(0, MODULE, "groupShowName", 0) ? true : false;
+ groupShowMessage = DBGetContactSettingByte(0, MODULE, "groupShowMessage", 1) ? true : false;
+ groupMessageLen = DBGetContactSettingDword(0, MODULE, "groupMessageLen", 43);
+ if(groupMessageLen < 5) groupMessageLen = 5;
+ groupTime = DBGetContactSettingDword(0, MODULE, "groupTime", 5);
+ if(groupTime < 1) groupTime = 1;
+ groupMessagesNumber = DBGetContactSettingDword(0, MODULE, "groupMessagesNumber", 100);
+ if(groupMessagesNumber < 1) groupMessagesNumber = 1;
+ messagesNewOnTop = DBGetContactSettingByte(0, MODULE, "messagesNewOnTop", 0) ? true : false;
+ messagesShowDate = DBGetContactSettingByte(0, MODULE, "messagesShowDate", 0) ? true : false;
+ messagesShowSec = DBGetContactSettingByte(0, MODULE, "messagesShowSec", 0) ? true : false;
+ messagesShowName = DBGetContactSettingByte(0, MODULE, "messagesShowName", 1) ? true : false;
+ messagesShowEvents = DBGetContactSettingByte(0, MODULE, "messagesShowEvents", 0) ? true : false;
+ messagesUseSmileys = DBGetContactSettingByte(0, MODULE, "messagesUseSmileys", 1) ? true : false;
+ searchForInList = DBGetContactSettingByte(0, MODULE, "searchForInList", 1) ? true : false;
+ searchForInMess = DBGetContactSettingByte(0, MODULE, "searchForInMess", 1) ? true : false;
+ searchMatchCase = DBGetContactSettingByte(0, MODULE, "searchMatchCase", 0) ? true : false;
+ searchMatchWhole = DBGetContactSettingByte(0, MODULE, "searchMatchWhole", 0) ? true : false;
+ searchOnlyIn = DBGetContactSettingByte(0, MODULE, "searchOnlyIn", 0) ? true : false;
+ searchOnlyOut = DBGetContactSettingByte(0, MODULE, "searchOnlyOut", 0) ? true : false;
+ searchOnlyGroup = DBGetContactSettingByte(0, MODULE, "searchOnlyGroup", 0) ? true : false;
+ searchAllContacts = DBGetContactSettingByte(0, MODULE, "searchAllContacts", 0) ? true : false;
+ schedulerAlerts = DBGetContactSettingByte(0, MODULE, "schedulerAlerts", 1) ? true : false;
+ schedulerHistoryAlerts = DBGetContactSettingByte(0, MODULE, "schedulerHistoryAlerts", 1) ? true : false;
+
+ defFilter = DBGetContactSettingByte(0, MODULE, "defFilter", defFilter);
+ int filtersCount = DBGetContactSettingDword(0, MODULE, "customFiltersCount", 0);
+ for(int i = 0; i < filtersCount; ++i)
+ {
+ char buf[256];
+ FilterOptions fo;
+ sprintf_s(buf, "filterName_%d", i);
+ DBVARIANT nameV;
+ if(!DBGetContactSettingWString(0, MODULE, buf, &nameV))
+ {
+ fo.name = nameV.pwszVal;
+ DBFreeVariant(&nameV);
+ }
+ else break;
+ sprintf_s(buf, "filterInOut_%d", i);
+ int inOut = DBGetContactSettingByte(0, MODULE, buf, 0);
+ if(inOut == 1)
+ fo.onlyIncomming = true;
+ else if(inOut == 2)
+ fo.onlyOutgoing = true;
+ sprintf_s(buf, "filterEvents_%d", i);
+ DBVARIANT eventsV;
+ if(!DBGetContactSettingString(0, MODULE, buf, &eventsV))
+ {
+ int k = 0;
+ char* id = eventsV.pszVal;
+ while(eventsV.pszVal[k])
+ {
+ if(eventsV.pszVal[k] == ';')
+ {
+ eventsV.pszVal[k] = 0;
+ fo.events.push_back(strtol(id, NULL, 16));
+ id = eventsV.pszVal + k + 1;
+ }
+
+ ++k;
+ }
+ DBFreeVariant(&eventsV);
+ }
+ else break;
+
+ customFilters.insert(customFilters.end(), fo);
+ }
+
+ if(defFilter > 1)
+ {
+ defFilter = 0;
+
+ DBVARIANT defFilterStrV;
+ if(!DBGetContactSettingWString(0, MODULE, "defFilterStr", &defFilterStrV))
+ {
+ std::wstring filterName = defFilterStrV.pwszVal;
+ for(int i = 0; i < (int)customFilters.size(); ++i)
+ {
+ if(filterName == customFilters[i].name)
+ {
+ defFilter = i + 2;
+ break;
+ }
+ }
+ DBFreeVariant(&defFilterStrV);
+ }
+ }
+
+ codepageTxt = DBGetContactSettingDword(0, MODULE, "codepageTxt", CP_UTF8);
+ codepageHtml1 = DBGetContactSettingDword(0, MODULE, "codepageHtml1", CP_UTF8);
+ codepageHtml2 = DBGetContactSettingDword(0, MODULE, "codepageHtml2", CP_UTF8);
+ DBVARIANT encodingV;
+ if(!DBGetContactSettingWString(0, MODULE, "encodingTxt", &encodingV))
+ {
+ encodingTxt = encodingV.pwszVal;
+ DBFreeVariant(&encodingV);
+ }
+ else
+ {
+ encodingTxt = _T("UTF-8");
+ }
+ if(!DBGetContactSettingWString(0, MODULE, "encodingHtml1", &encodingV))
+ {
+ encodingHtml1 = encodingV.pwszVal;
+ DBFreeVariant(&encodingV);
+ }
+ else
+ {
+ encodingHtml1 = _T("UTF-8");
+ }
+ if(!DBGetContactSettingWString(0, MODULE, "encodingHtml2", &encodingV))
+ {
+ encodingHtml2 = encodingV.pwszVal;
+ DBFreeVariant(&encodingV);
+ }
+ else
+ {
+ encodingHtml2 = _T("UTF-8");
+ }
+
+ exportHtml1ShowDate = DBGetContactSettingByte(0, MODULE, "exportHtml1ShowDate", 1) ? true : false;
+ exportHtml2ShowDate = DBGetContactSettingByte(0, MODULE, "exportHtml2ShowDate", 0) ? true : false;
+ exportHtml2UseSmileys = DBGetContactSettingByte(0, MODULE, "exportHtml2UseSmileys", 1) ? true : false;
+ if(!DBGetContactSettingWString(0, MODULE, "extCssHtml2", &encodingV))
+ {
+ extCssHtml2 = encodingV.pwszVal;
+ DBFreeVariant(&encodingV);
+ }
+ else
+ {
+ extCssHtml2 = _T("");
+ }
+
+ if(!DBGetContactSettingWString(0, MODULE, "ftpLogPath", &encodingV))
+ {
+ ftpLogPath = encodingV.pwszVal;
+ DBFreeVariant(&encodingV);
+ }
+
+ if(!DBGetContactSettingWString(0, MODULE, "ftpExePath", &encodingV))
+ {
+ ftpExePath = encodingV.pwszVal;
+ DBFreeVariant(&encodingV);
+ }
+ else
+ {
+ ftpExePath = ftpExePathDef;
+ }
+
+ LoadTasks();
+}
+
+COLORREF Options::GetFont(Fonts fontId, PLOGFONT font)
+{
+ FontIDT fid = {0};
+ fid.cbSize = sizeof(FontIDT);
+ _tcsncpy_s(fid.group, LPGENT("History"), SIZEOF(fid.group));
+ _tcsncpy_s(fid.name, g_FontOptionsList[fontId].szDescr, SIZEOF(fid.name));
+ return (COLORREF)CallService(MS_FONT_GETT, (WPARAM)&fid, (LPARAM)font);
+}
+
+COLORREF Options::GetColor(Colors colorId)
+{
+ ColourIDT cid = {0};
+ cid.cbSize = sizeof(ColourIDT);
+ _tcsncpy_s(cid.group, LPGENT("History"), SIZEOF(cid.group));
+ _tcsncpy_s(cid.name, g_ColorOptionsList[colorId].tszName, SIZEOF(cid.name));
+ return (COLORREF)CallService(MS_COLOUR_GETT, (WPARAM)&cid, NULL);
+}
+
+void Options::Save()
+{
+ DBWriteContactSettingByte(0, MODULE, "showContacts", showContacts ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "showContactGroups", showContactGroups ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "groupNewOnTop", groupNewOnTop ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "groupShowEvents", groupShowEvents ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "groupShowTime", groupShowTime ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "groupShowName", groupShowName ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "groupShowMessage", groupShowMessage ? 1 : 0);
+ if(groupMessageLen < 5) groupMessageLen = 5;
+ DBWriteContactSettingDword(0, MODULE, "groupMessageLen", groupMessageLen);
+ if(groupTime < 1) groupTime = 1;
+ DBWriteContactSettingDword(0, MODULE, "groupTime", groupTime);
+ if(groupMessagesNumber < 1) groupMessagesNumber = 1;
+ DBWriteContactSettingDword(0, MODULE, "groupMessagesNumber", groupMessagesNumber);
+ DBWriteContactSettingByte(0, MODULE, "messagesNewOnTop", messagesNewOnTop ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "messagesShowDate", messagesShowDate ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "messagesShowSec", messagesShowSec ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "messagesShowName", messagesShowName ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "messagesShowEvents", messagesShowEvents ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "messagesUseSmileys", messagesUseSmileys ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "searchForInList", searchForInList ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "searchForInMess", searchForInMess ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "searchMatchCase", searchMatchCase ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "searchMatchWhole", searchMatchWhole ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "searchOnlyIn", searchOnlyIn ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "searchOnlyOut", searchOnlyOut ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "searchOnlyGroup", searchOnlyGroup ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "searchAllContacts", searchAllContacts ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "schedulerAlerts", schedulerAlerts ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "schedulerHistoryAlerts", schedulerHistoryAlerts ? 1 : 0);
+ if(defFilter < 0 || defFilter - 2 >= (int)customFilters.size()) defFilter = 0;
+ DBWriteContactSettingByte(0, MODULE, "defFilter", defFilter < 2 ? defFilter : 2);
+ if(defFilter >= 2)
+ DBWriteContactSettingWString(0, MODULE, "defFilterStr", customFilters[defFilter - 2].name.c_str());
+ DBWriteContactSettingDword(0, MODULE, "customFiltersCount", (DWORD)customFilters.size());
+ for(int i = 0 ; i < (int)customFilters.size(); ++i)
+ {
+ char buf[256];
+ sprintf_s(buf, "filterName_%d", i);
+ DBWriteContactSettingWString(0, MODULE, buf, customFilters[i].name.c_str());
+ sprintf_s(buf, "filterInOut_%d", i);
+ DBWriteContactSettingByte(0, MODULE, buf, customFilters[i].onlyIncomming ? 1 : (customFilters[i].onlyOutgoing ? 2 : 0));
+ std::string events;
+ for(std::vector<int>::iterator it = customFilters[i].events.begin(); it != customFilters[i].events.end(); ++it)
+ {
+ _itoa_s(*it, buf, 16);
+ events += buf;
+ events += ";";
+ }
+
+ sprintf_s(buf, "filterEvents_%d", i);
+ DBWriteContactSettingString(0, MODULE, buf, events.c_str());
+ }
+
+ DBWriteContactSettingDword(0, MODULE, "codepageTxt", codepageTxt);
+ DBWriteContactSettingDword(0, MODULE, "codepageHtml1", codepageHtml1);
+ DBWriteContactSettingDword(0, MODULE, "codepageHtml2", codepageHtml2);
+ DBWriteContactSettingWString(0, MODULE, "encodingTxt", encodingTxt.c_str());
+ DBWriteContactSettingWString(0, MODULE, "encodingHtml1", encodingHtml1.c_str());
+ DBWriteContactSettingWString(0, MODULE, "encodingHtml2", encodingHtml2.c_str());
+ DBWriteContactSettingByte(0, MODULE, "exportHtml1ShowDate", exportHtml1ShowDate ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "exportHtml2ShowDate", exportHtml2ShowDate ? 1 : 0);
+ DBWriteContactSettingByte(0, MODULE, "exportHtml2UseSmileys", exportHtml2UseSmileys ? 1 : 0);
+ DBWriteContactSettingWString(0, MODULE, "extCssHtml2", extCssHtml2.c_str());
+ DBWriteContactSettingWString(0, MODULE, "ftpLogPath", ftpLogPath.c_str());
+ if(ftpExePath != ftpExePathDef)
+ {
+ DBWriteContactSettingWString(0, MODULE, "ftpExePath", ftpExePath.c_str());
+ }
+ else
+ {
+ DBDeleteContactSetting(0, MODULE, "ftpExePath");
+ }
+}
+
+void Options::SaveTasks(std::list<TaskOptions>* tasks)
+{
+ EnterCriticalSection(&criticalSection);
+ int oldTaskNr = (int)taskOptions.size();
+ taskOptions.clear();
+ int i = 0;
+ char buf[256];
+ for(std::list<TaskOptions>::iterator it = tasks->begin(); it != tasks->end(); ++it)
+ {
+ sprintf_s(buf, "Task_compress_%d", i);
+ DBWriteContactSettingByte(0, MODULE, buf, it->compress);
+ sprintf_s(buf, "Task_useFtp_%d", i);
+ DBWriteContactSettingByte(0, MODULE, buf, it->useFtp);
+ sprintf_s(buf, "Task_isSystem_%d", i);
+ DBWriteContactSettingByte(0, MODULE, buf, it->isSystem);
+ sprintf_s(buf, "Task_active_%d", i);
+ DBWriteContactSettingByte(0, MODULE, buf, it->active);
+ sprintf_s(buf, "Task_exportImported_%d", i);
+ DBWriteContactSettingByte(0, MODULE, buf, it->exportImported);
+ sprintf_s(buf, "Task_type_%d", i);
+ DBWriteContactSettingByte(0, MODULE, buf, it->type);
+ sprintf_s(buf, "Task_eventUnit_%d", i);
+ DBWriteContactSettingByte(0, MODULE, buf, it->eventUnit);
+ sprintf_s(buf, "Task_trigerType_%d", i);
+ DBWriteContactSettingByte(0, MODULE, buf, it->trigerType);
+ sprintf_s(buf, "Task_exportType_%d", i);
+ DBWriteContactSettingByte(0, MODULE, buf, it->exportType);
+ sprintf_s(buf, "Task_importType_%d", i);
+ DBWriteContactSettingByte(0, MODULE, buf, it->importType);
+ sprintf_s(buf, "Task_eventDeltaTime_%d", i);
+ DBWriteContactSettingDword(0, MODULE, buf, it->eventDeltaTime);
+ sprintf_s(buf, "Task_filterId_%d", i);
+ DBWriteContactSettingDword(0, MODULE, buf, it->filterId);
+ sprintf_s(buf, "Task_dayTime_%d", i);
+ DBWriteContactSettingDword(0, MODULE, buf, it->dayTime);
+ sprintf_s(buf, "Task_dayOfWeek_%d", i);
+ DBWriteContactSettingDword(0, MODULE, buf, it->dayOfWeek);
+ sprintf_s(buf, "Task_dayOfMonth_%d", i);
+ DBWriteContactSettingDword(0, MODULE, buf, it->dayOfMonth);
+ sprintf_s(buf, "Task_deltaTime_%d", i);
+ DBWriteContactSettingDword(0, MODULE, buf, it->deltaTime);
+ sprintf_s(buf, "Task_lastExport_low_%d", i);
+ DBWriteContactSettingDword(0, MODULE, buf, (int)it->lastExport);
+ sprintf_s(buf, "Task_lastExport_hi_%d", i);
+ DBWriteContactSettingDword(0, MODULE, buf, ((unsigned long long int)it->lastExport) >> 32);
+ sprintf_s(buf, "Task_ftpName_%d", i);
+ DBWriteContactSettingWString(0, MODULE, buf, it->ftpName.c_str());
+ sprintf_s(buf, "Task_filterName_%d", i);
+ DBWriteContactSettingWString(0, MODULE, buf, it->filterName.c_str());
+ sprintf_s(buf, "Task_filePath_%d", i);
+ DBWriteContactSettingWString(0, MODULE, buf, it->filePath.c_str());
+ sprintf_s(buf, "Task_taskName_%d", i);
+ DBWriteContactSettingWString(0, MODULE, buf, it->taskName.c_str());
+ sprintf_s(buf, "Task_zipPassword_%d", i);
+ DBWriteContactSettingString(0, MODULE, buf, it->zipPassword.c_str());
+
+ HANDLE _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ sprintf_s(buf, "IsInTask_%d", i);
+ while(_hContact)
+ {
+ DBDeleteContactSetting(_hContact, MODULE, buf);
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)_hContact, 0);
+ }
+
+ for(size_t j = 0; j < it->contacts.size(); ++j)
+ {
+ DBWriteContactSettingByte(it->contacts[j], MODULE, buf, 1);
+ }
+
+ it->orderNr = i++;
+ taskOptions.push_back(*it);
+ }
+
+ DBWriteContactSettingDword(0, MODULE, "Task_count", i);
+
+ for(i = (int)tasks->size(); i < oldTaskNr; ++i)
+ {
+ sprintf_s(buf, "Task_compress_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_useFtp_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_isSystem_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_active_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_type_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_eventUnit_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_trigerType_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_exportType_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_eventDeltaTime_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_filterId_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_dayTime_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_dayOfWeek_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_dayOfMonth_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_deltaTime_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_lastExport_low_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_lastExport_hi_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_ftpName_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_filterName_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_filePath_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+ sprintf_s(buf, "Task_taskName_%d", i);
+ DBDeleteContactSetting(NULL, MODULE, buf);
+
+ HANDLE _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ sprintf_s(buf, "IsInTask_%d", i);
+ while(_hContact)
+ {
+ DBDeleteContactSetting(_hContact, MODULE, buf);
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)_hContact, 0);
+ }
+ }
+
+ LeaveCriticalSection(&criticalSection);
+}
+
+void Options::SaveTaskTime(TaskOptions& to)
+{
+ int i = to.orderNr;
+ char buf[256];
+ sprintf_s(buf, "Task_lastExport_low_%d", i);
+ DBWriteContactSettingDword(0, MODULE, buf, (int)to.lastExport);
+ sprintf_s(buf, "Task_lastExport_hi_%d", i);
+ DBWriteContactSettingDword(0, MODULE, buf, ((unsigned long long int)to.lastExport) >> 32);
+}
+
+void Options::LoadTasks()
+{
+ int taskCount = DBGetContactSettingDword(0, MODULE, "Task_count", 0);
+ char buf[256];
+ for(int i = 0; i < taskCount; ++i)
+ {
+ TaskOptions to;
+ sprintf_s(buf, "Task_compress_%d", i);
+ to.compress = DBGetContactSettingByte(0, MODULE, buf, to.compress) != 0;
+ sprintf_s(buf, "Task_useFtp_%d", i);
+ to.useFtp = DBGetContactSettingByte(0, MODULE, buf, to.useFtp) != 0;
+ sprintf_s(buf, "Task_isSystem_%d", i);
+ to.isSystem = DBGetContactSettingByte(0, MODULE, buf, to.isSystem) != 0;
+ sprintf_s(buf, "Task_active_%d", i);
+ to.active = DBGetContactSettingByte(0, MODULE, buf, to.active) != 0;
+ sprintf_s(buf, "Task_exportImported_%d", i);
+ to.exportImported = DBGetContactSettingByte(0, MODULE, buf, to.exportImported) != 0;
+ sprintf_s(buf, "Task_type_%d", i);
+ to.type = (TaskOptions::TaskType)DBGetContactSettingByte(0, MODULE, buf, to.type);
+ sprintf_s(buf, "Task_eventUnit_%d", i);
+ to.eventUnit = (TaskOptions::EventUnit)DBGetContactSettingByte(0, MODULE, buf, to.eventUnit);
+ sprintf_s(buf, "Task_trigerType_%d", i);
+ to.trigerType = (TaskOptions::TrigerType)DBGetContactSettingByte(0, MODULE, buf, to.trigerType);
+ sprintf_s(buf, "Task_exportType_%d", i);
+ to.exportType = (IExport::ExportType)DBGetContactSettingByte(0, MODULE, buf, to.exportType);
+ sprintf_s(buf, "Task_importType_%d", i);
+ to.importType = (IImport::ImportType)DBGetContactSettingByte(0, MODULE, buf, to.importType);
+ sprintf_s(buf, "Task_eventDeltaTime_%d", i);
+ to.eventDeltaTime = DBGetContactSettingDword(0, MODULE, buf, to.eventDeltaTime);
+ sprintf_s(buf, "Task_filterId_%d", i);
+ to.filterId = DBGetContactSettingDword(0, MODULE, buf, to.filterId);
+ sprintf_s(buf, "Task_dayTime_%d", i);
+ to.dayTime = DBGetContactSettingDword(0, MODULE, buf, to.dayTime);
+ sprintf_s(buf, "Task_dayOfWeek_%d", i);
+ to.dayOfWeek = DBGetContactSettingDword(0, MODULE, buf, to.dayOfWeek);
+ sprintf_s(buf, "Task_dayOfMonth_%d", i);
+ to.dayOfMonth = DBGetContactSettingDword(0, MODULE, buf, to.dayOfMonth);
+ sprintf_s(buf, "Task_deltaTime_%d", i);
+ to.deltaTime = DBGetContactSettingDword(0, MODULE, buf, to.deltaTime);
+ unsigned long long int le = to.lastExport;
+ sprintf_s(buf, "Task_lastExport_low_%d", i);
+ to.lastExport = DBGetContactSettingDword(0, MODULE, buf, (int)le) & 0xffffffff;
+ sprintf_s(buf, "Task_lastExport_hi_%d", i);
+ to.lastExport |= ((unsigned long long int)DBGetContactSettingDword(0, MODULE, buf, le >> 32)) << 32;
+ sprintf_s(buf, "Task_ftpName_%d", i);
+ DBVARIANT var;
+ if(!DBGetContactSettingWString(0, MODULE, buf, &var))
+ {
+ to.ftpName = var.ptszVal;
+ DBFreeVariant(&var);
+ }
+ sprintf_s(buf, "Task_filterName_%d", i);
+ if(!DBGetContactSettingWString(0, MODULE, buf, &var))
+ {
+ to.filterName = var.ptszVal;
+ DBFreeVariant(&var);
+ }
+ sprintf_s(buf, "Task_filePath_%d", i);
+ if(!DBGetContactSettingWString(0, MODULE, buf, &var))
+ {
+ to.filePath = var.ptszVal;
+ DBFreeVariant(&var);
+ }
+ sprintf_s(buf, "Task_taskName_%d", i);
+ if(!DBGetContactSettingWString(0, MODULE, buf, &var))
+ {
+ to.taskName = var.ptszVal;
+ DBFreeVariant(&var);
+ }
+ sprintf_s(buf, "Task_zipPassword_%d", i);
+ if(!DBGetContactSettingString(0, MODULE, buf, &var))
+ {
+ to.zipPassword = var.pszVal;
+ DBFreeVariant(&var);
+ }
+
+ HANDLE _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ sprintf_s(buf, "IsInTask_%d", i);
+ while(_hContact)
+ {
+ if(DBGetContactSettingByte(_hContact, MODULE, buf, 0) == 1)
+ {
+ to.contacts.push_back(_hContact);
+ }
+
+ _hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)_hContact, 0);
+ }
+
+ to.orderNr = i;
+ taskOptions.push_back(to);
+ }
+}
+
+void OptionsMainChanged();
+void OptionsGroupChanged();
+void OptionsMessageChanged();
+void OptionsSearchingChanged();
+void OptionsSchedulerChanged();
+void InitTaskMenuItems();
+
+void SetEventCB(HWND hwndCB, int eventId)
+{
+ int cpCount = SIZEOF(EventNames);
+ int selCpIdx = -1;
+ for(int i = 0; i < cpCount; ++i)
+ {
+ if(EventNames[i].id == eventId)
+ selCpIdx = i;
+ }
+
+ if(selCpIdx == -1)
+ {
+ TCHAR buf[24];
+ _stprintf_s(buf, 24, _T("%d"), eventId);
+ ComboBox_SetText(hwndCB, buf);
+ }
+ else
+ {
+ ComboBox_SetCurSel(hwndCB, selCpIdx);
+ }
+}
+
+int GetEventCB(HWND hwndCB, bool errorReport, int &eventId)
+{
+ int selCpIdx = ComboBox_GetCurSel(hwndCB);
+ if(selCpIdx < 0)
+ {
+ TCHAR text[24];
+ ComboBox_GetText(hwndCB, text, 24);
+ TCHAR * stopOn = NULL;
+ long cp = _tcstol(text, &stopOn, 10);
+ if(errorReport && (stopOn == text || *stopOn != '\0' || cp < 0))
+ {
+ MessageBox(GetParent(hwndCB), TranslateT("Invalid event number"), TranslateT("Error"), MB_OK | MB_ICONERROR);
+ SetFocus(hwndCB);
+ return -1;
+ }
+
+ eventId = cp;
+ }
+ else if(selCpIdx > 1)
+ eventId = EventNames[selCpIdx - 2].id;
+ else
+ return selCpIdx + 1;
+
+ return 0;
+}
+
+void ClearLB(HWND hwndLB)
+{
+ while(ListBox_GetCount(hwndLB) > 0)
+ ListBox_DeleteString(hwndLB, 0);
+}
+
+void ReloadEventLB(HWND hwndLB, const FilterOptions &sel)
+{
+ while(ListBox_GetCount(hwndLB) > 0)
+ ListBox_DeleteString(hwndLB, 0);
+ if(sel.onlyIncomming && !sel.onlyOutgoing)
+ {
+ ListBox_AddString(hwndLB, TranslateT("Incoming events"));
+ }
+ else if(sel.onlyOutgoing && !sel.onlyIncomming)
+ {
+ ListBox_AddString(hwndLB, TranslateT("Outgoing events"));
+ }
+
+ for(std::vector<int>::const_iterator it = sel.events.begin(); it != sel.events.end(); ++it)
+ {
+ int cpCount = SIZEOF(EventNames);
+ int selCpIdx = -1;
+ for(int i = 0; i < cpCount; ++i)
+ {
+ if(EventNames[i].id == *it)
+ selCpIdx = i;
+ }
+ if(selCpIdx == -1)
+ {
+ TCHAR buf[24];
+ _stprintf_s(buf, 24, _T("%d"), *it);
+ ListBox_AddString(hwndLB, buf);
+ }
+ else
+ {
+ ListBox_AddString(hwndLB, TranslateTS(EventNames[selCpIdx].name));
+ }
+ }
+}
+
+bool CheckFile(HWND hwndEdit)
+{
+ TCHAR buf[MAX_PATH];
+ Edit_GetText(hwndEdit, buf, MAX_PATH);
+ DWORD atr = GetFileAttributes(buf);
+ if(atr == INVALID_FILE_ATTRIBUTES || atr & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ MessageBox(GetParent(hwndEdit), TranslateT("File do not exist. Enter correct file path."), TranslateT("Invalid file"), MB_OK | MB_ICONERROR);
+ SetFocus(hwndEdit);
+ return false;
+ }
+
+ return true;
+}
+
+bool OpenFileDlg(HWND hwndDlg, HWND hwndEdit, const TCHAR* defName, const TCHAR* ext, const TCHAR* title, bool open)
+{
+ TCHAR filter[1024];
+ std::locale loc;
+ TCHAR extUpper[32];
+ _tcscpy_s(extUpper, ext);
+ extUpper[0] = std::toupper(ext[0], loc);
+ _stprintf_s(filter, TranslateT("%s Files (*.%s)"), extUpper, ext);
+ size_t len = _tcslen(filter) + 1;
+ _stprintf_s(filter + len, 1024 - len, _T("*.%s"), ext);
+ len += _tcslen(filter + len) + 1;
+ _tcscpy_s(filter + len, 1024 - len, TranslateT("All Files (*.*)"));
+ len += _tcslen(filter + len) + 1;
+ _tcscpy_s(filter + len, 1024 - len, _T("*.*"));
+ len += _tcslen(filter + len) + 1;
+ filter[len] = 0;
+ TCHAR stzFilePath[1024];
+
+ Edit_GetText(hwndEdit, stzFilePath, 1023);
+ if(stzFilePath[0] == 0)
+ {
+ _tcscpy_s(stzFilePath, defName);
+ len = _tcslen(stzFilePath) + 1;
+ stzFilePath[len] = 0;
+ }
+ else
+ {
+ len = _tcslen(stzFilePath) + 1;
+ stzFilePath[len] = 0;
+ }
+
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = hwndDlg;
+ ofn.lpstrFilter = filter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = stzFilePath;
+ ofn.lpstrTitle = title;
+ ofn.nMaxFile = 1024;
+ ofn.lpstrDefExt = ext;
+ if(open)
+ {
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+ if(GetOpenFileName(&ofn))
+ {
+ Edit_SetText(hwndEdit, stzFilePath);
+ return true;
+ }
+ }
+ else
+ {
+ ofn.Flags = OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR;
+ if(GetSaveFileName(&ofn))
+ {
+ Edit_SetText(hwndEdit, stzFilePath);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+INT_PTR CALLBACK Options::DlgProcOptsMain(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)FALSE);
+ CheckDlgButton(hwndDlg, IDC_SHOWCONTACTS, instance->showContacts ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_SHOWCONTACTGROUPS, instance->showContactGroups ? 1 : 0);
+ HWND events = GetDlgItem(hwndDlg, IDC_EVENT);
+ HWND defFilter = GetDlgItem(hwndDlg, IDC_DEFFILTER);
+ HWND listFilter = GetDlgItem(hwndDlg, IDC_LIST_FILTERS);
+ HWND ftp = GetDlgItem(hwndDlg, IDC_WINSCP);
+ HWND ftpLog = GetDlgItem(hwndDlg, IDC_WINSCPLOG);
+ ComboBox_AddString(events, TranslateT("Incoming events"));
+ ComboBox_AddString(events, TranslateT("Outgoing events"));
+ for(int i = 0 ; i < SIZEOF(EventNames); ++i)
+ {
+ ComboBox_AddString(events, TranslateTS(EventNames[i].name));
+ }
+
+ ComboBox_AddString(defFilter, TranslateT("Default history events"));
+ ComboBox_AddString(defFilter, TranslateT("All events"));
+ Edit_LimitText(GetDlgItem(hwndDlg, IDC_FILTER_NAME), 20);
+ ComboBox_LimitText(events, 20);
+
+ instance->customFiltersTemp.clear();
+ instance->customFiltersTemp.insert(instance->customFiltersTemp.begin(), instance->customFilters.begin(), instance->customFilters.end());
+ for(std::vector<FilterOptions>::iterator it = instance->customFiltersTemp.begin(); it != instance->customFiltersTemp.end(); ++it)
+ {
+ ComboBox_AddString(defFilter, it->name.c_str());
+ ListBox_AddString(listFilter, it->name.c_str());
+ }
+ ComboBox_SetCurSel(defFilter, instance->defFilter);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_FILTER), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EVENT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD_EVENT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_EVENT), FALSE);
+
+ Edit_LimitText(ftp, MAX_PATH);
+ Edit_LimitText(ftpLog, MAX_PATH);
+ Edit_SetText(ftp, instance->ftpExePath.c_str());
+ Edit_SetText(ftpLog, instance->ftpLogPath.c_str());
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)TRUE);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ {
+ BOOL init = (BOOL)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ HWND listFilter = GetDlgItem(hwndDlg, IDC_LIST_FILTERS);
+ HWND listEvents = GetDlgItem(hwndDlg, IDC_LIST_EVENTS);
+ HWND nameFilter = GetDlgItem(hwndDlg, IDC_FILTER_NAME);
+ HWND defFilter = GetDlgItem(hwndDlg, IDC_DEFFILTER);
+ HWND eventCB = GetDlgItem(hwndDlg, IDC_EVENT);
+ switch(LOWORD(wParam))
+ {
+ case IDC_ADD_FILTER:
+ {
+ TCHAR name[24];
+ Edit_GetText(nameFilter, name, 24);
+ if(name[0] == 0)
+ {
+ MessageBox(hwndDlg, TranslateT("Enter filter name"), TranslateT("Error"), MB_ICONERROR);
+ return TRUE;
+ }
+
+ FilterOptions fo(name);
+ for(std::vector<FilterOptions>::iterator it = instance->customFiltersTemp.begin(); it != instance->customFiltersTemp.end(); ++it)
+ {
+ if(it->name == fo.name)
+ {
+ MessageBox(hwndDlg, TranslateT("Filter name exists"), TranslateT("Error"), MB_ICONERROR);
+ return TRUE;
+ }
+ }
+
+ instance->customFiltersTemp.insert(instance->customFiltersTemp.end(), fo);
+ int i = ListBox_AddString(listFilter, name);
+ ListBox_SetCurSel(listFilter, i);
+ ComboBox_AddString(defFilter, name);
+ name[0] = 0;
+ Edit_SetText(nameFilter, name);
+ ReloadEventLB(listEvents, instance->customFiltersTemp[i]);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_FILTER), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EVENT), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD_EVENT), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_EVENT), FALSE);
+ }
+ break;
+ case IDC_DELETE_FILTER:
+ {
+ int sel = ListBox_GetCurSel(listFilter);
+ if(sel < 0)
+ return TRUE;
+ for(size_t i = sel; i < instance->customFiltersTemp.size() - 1; ++i)
+ {
+ instance->customFiltersTemp[i] = instance->customFiltersTemp[i + 1];
+ }
+
+ instance->customFiltersTemp.resize(instance->customFiltersTemp.size() - 1);
+ ListBox_DeleteString(listFilter, sel);
+ ComboBox_DeleteString(defFilter, sel + 2);
+ if(ComboBox_GetCurSel(defFilter) < 0)
+ {
+ ComboBox_SetCurSel(defFilter, 0);
+ }
+
+ ClearLB(listEvents);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_FILTER), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EVENT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD_EVENT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_EVENT), FALSE);
+ }
+ break;
+ case IDC_ADD_EVENT:
+ {
+ int sel = ListBox_GetCurSel(listFilter);
+ if(sel < 0)
+ return TRUE;
+ int eventId;
+ int selCB = GetEventCB(eventCB, true, eventId);
+ if(selCB < 0)
+ return TRUE;
+ if(selCB == 1)
+ {
+ if(instance->customFiltersTemp[sel].onlyIncomming)
+ {
+ MessageBox(hwndDlg, TranslateT("Event already exists"), TranslateT("Error"), MB_ICONERROR);
+ return TRUE;
+ }
+
+ if(instance->customFiltersTemp[sel].onlyOutgoing)
+ instance->customFiltersTemp[sel].onlyOutgoing = false;
+ else
+ instance->customFiltersTemp[sel].onlyIncomming = true;
+ }
+ else if(selCB == 2)
+ {
+ if(instance->customFiltersTemp[sel].onlyOutgoing)
+ {
+ MessageBox(hwndDlg, TranslateT("Event already exists"), TranslateT("Error"), MB_ICONERROR);
+ return TRUE;
+ }
+
+ if(instance->customFiltersTemp[sel].onlyIncomming)
+ instance->customFiltersTemp[sel].onlyIncomming = false;
+ else
+ instance->customFiltersTemp[sel].onlyOutgoing = true;
+ }
+ else
+ {
+ if(std::find(instance->customFiltersTemp[sel].events.begin(), instance->customFiltersTemp[sel].events.end(), eventId) != instance->customFiltersTemp[sel].events.end())
+ {
+ MessageBox(hwndDlg, TranslateT("Event already exists"), TranslateT("Error"), MB_ICONERROR);
+ return TRUE;
+ }
+
+ instance->customFiltersTemp[sel].events.push_back(eventId);
+ }
+
+ ReloadEventLB(listEvents, instance->customFiltersTemp[sel]);
+ ComboBox_SetCurSel(eventCB, -1);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_EVENT), FALSE);
+ }
+ break;
+ case IDC_DELETE_EVENT:
+ {
+ int sel = ListBox_GetCurSel(listFilter);
+ if(sel < 0)
+ return TRUE;
+ int eventSel = ListBox_GetCurSel(listEvents);
+ if(eventSel < 0)
+ return TRUE;
+ int stId = 0;
+ if(instance->customFiltersTemp[sel].onlyIncomming || instance->customFiltersTemp[sel].onlyOutgoing)
+ ++stId;
+ if(eventSel >= stId)
+ {
+ --eventSel;
+ for(int i = eventSel; i < (int)instance->customFiltersTemp[sel].events.size() - 1; ++i)
+ {
+ instance->customFiltersTemp[sel].events[i] = instance->customFiltersTemp[sel].events[i + 1];
+ }
+
+ instance->customFiltersTemp[sel].events.resize(instance->customFiltersTemp[sel].events.size() - 1);
+ }
+ else
+ {
+ instance->customFiltersTemp[sel].onlyIncomming = false;
+ instance->customFiltersTemp[sel].onlyOutgoing = false;
+ }
+
+ ReloadEventLB(listEvents, instance->customFiltersTemp[sel]);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_EVENT), FALSE);
+ }
+ break;
+ case IDC_WINSCP_BROWSE:
+ if(!OpenFileDlg(hwndDlg, GetDlgItem(hwndDlg, IDC_WINSCP), _T("WinSCP.exe"), _T("exe"), TranslateT("Browse WinSCP file"), true))
+ {
+ return TRUE;
+ }
+
+ break;
+ case IDC_WINSCPLOG_BROWSE:
+ if(!OpenFileDlg(hwndDlg, GetDlgItem(hwndDlg, IDC_WINSCPLOG), _T("ftplog.txt"), _T("txt"), TranslateT("Save WinSCP log file"), false))
+ {
+ return TRUE;
+ }
+
+ break;
+ }
+ }
+ else if(HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_LIST_FILTERS)
+ {
+ HWND listFilter = GetDlgItem(hwndDlg, IDC_LIST_FILTERS);
+ HWND listEvents = GetDlgItem(hwndDlg, IDC_LIST_EVENTS);
+ int sel = ListBox_GetCurSel(listFilter);
+ if(sel < 0)
+ ClearLB(listEvents);
+ else
+ ReloadEventLB(listEvents, instance->customFiltersTemp[sel]);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_FILTER), sel >= 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EVENT), sel >= 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ADD_EVENT), sel >= 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_EVENT), FALSE);
+ }
+ else if(HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_LIST_EVENTS)
+ {
+ HWND listEvents = GetDlgItem(hwndDlg, IDC_LIST_EVENTS);
+ int sel = ListBox_GetCurSel(listEvents);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_EVENT), sel >= 0);
+ }
+
+ if (init && (HIWORD(wParam) == BN_CLICKED || (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_DEFFILTER) || (HIWORD(wParam) == EN_CHANGE && LOWORD(wParam) != IDC_FILTER_NAME)))
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ if(((LPNMHDR)lParam)->code == PSN_APPLY)
+ {
+ HWND ftp = GetDlgItem(hwndDlg, IDC_WINSCP);
+ TCHAR buf[MAX_PATH];
+ Edit_GetText(ftp, buf, MAX_PATH);
+ if(buf[0] != 0 && !CheckFile(ftp))
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
+ return TRUE;
+ }
+
+ instance->ftpExePath = buf;
+ Edit_GetText(GetDlgItem(hwndDlg, IDC_WINSCPLOG), buf, MAX_PATH);
+ instance->ftpLogPath = buf;
+
+ instance->showContacts = IsDlgButtonChecked(hwndDlg, IDC_SHOWCONTACTS) ? true : false;
+ instance->showContactGroups = IsDlgButtonChecked(hwndDlg, IDC_SHOWCONTACTGROUPS) ? true : false;
+ instance->defFilter = ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_DEFFILTER));
+ instance->customFilters.clear();
+ instance->customFilters.insert(instance->customFilters.begin(), instance->customFiltersTemp.begin(), instance->customFiltersTemp.end());
+ Options::instance->Save();
+ OptionsMainChanged();
+ }
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+class OptsData
+{
+public:
+ OptsData()
+ {
+ init = false;
+ }
+
+ bool init;
+};
+
+INT_PTR CALLBACK Options::DlgProcOptsGroupList(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ OptsData* optsData = new OptsData();
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)optsData);
+
+ CheckDlgButton(hwndDlg, IDC_NEWONTOP, instance->groupNewOnTop ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_SHOWEVENTS, instance->groupShowEvents ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_SHOWTIME, instance->groupShowTime ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_SHOWNAME, instance->groupShowName ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_SHOWMESSAGE, instance->groupShowMessage ? 1 : 0);
+ Edit_LimitText(GetDlgItem(hwndDlg, IDC_MESSAGELEN), 4);
+ SetDlgItemInt(hwndDlg, IDC_MESSAGELEN, instance->groupMessageLen, FALSE);
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_SHOWMESSAGE, BN_CLICKED), NULL);
+ Edit_LimitText(GetDlgItem(hwndDlg, IDC_GROUPTIME), 2);
+ Edit_LimitText(GetDlgItem(hwndDlg, IDC_LIMITMESSAGES), 3);
+ SetDlgItemInt(hwndDlg, IDC_GROUPTIME, instance->groupTime, FALSE);
+ SetDlgItemInt(hwndDlg, IDC_LIMITMESSAGES, instance->groupMessagesNumber, FALSE);
+
+ optsData->init = true;
+ return TRUE;
+ }
+ case WM_COMMAND:
+ {
+ OptsData* optsData = (OptsData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_SHOWMESSAGE)
+ {
+ if(IsDlgButtonChecked(hwndDlg, IDC_SHOWMESSAGE))
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGELEN), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGELEN_DESC), TRUE);
+ }
+ else
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGELEN), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGELEN_DESC), FALSE);
+ }
+ }
+ if (optsData->init && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == EN_CHANGE))
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ if(((LPNMHDR)lParam)->code == PSN_APPLY)
+ {
+ instance->groupNewOnTop = IsDlgButtonChecked(hwndDlg, IDC_NEWONTOP) ? true : false;
+ instance->groupShowEvents = IsDlgButtonChecked(hwndDlg, IDC_SHOWEVENTS) ? true : false;
+ instance->groupShowTime = IsDlgButtonChecked(hwndDlg, IDC_SHOWTIME) ? true : false;
+ instance->groupShowName = IsDlgButtonChecked(hwndDlg, IDC_SHOWNAME) ? true : false;
+ instance->groupShowMessage = IsDlgButtonChecked(hwndDlg, IDC_SHOWMESSAGE) ? true : false;
+ BOOL success;
+ instance->groupMessageLen = GetDlgItemInt(hwndDlg, IDC_MESSAGELEN, &success, FALSE);
+ instance->groupTime = GetDlgItemInt(hwndDlg, IDC_GROUPTIME, &success, FALSE);
+ instance->groupMessagesNumber = GetDlgItemInt(hwndDlg, IDC_LIMITMESSAGES, &success, FALSE);
+
+ Options::instance->Save();
+ OptionsGroupChanged();
+ }
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+INT_PTR CALLBACK Options::DlgProcOptsMessages(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ CheckDlgButton(hwndDlg, IDC_NEWONTOP, instance->messagesNewOnTop ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_SHOWDATE, instance->messagesShowDate ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_SHOWSECOND, instance->messagesShowSec ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_SHOWNAME, instance->messagesShowName ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_SHOWEVENTS, instance->messagesShowEvents ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_SHOWSMILEYS, instance->messagesUseSmileys ? 1 : 0);
+ if(!g_SmileyAddAvail)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWSMILEYS), FALSE);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ if(((LPNMHDR)lParam)->code == PSN_APPLY)
+ {
+ instance->messagesNewOnTop = IsDlgButtonChecked(hwndDlg, IDC_NEWONTOP) ? true : false;
+ instance->messagesShowDate = IsDlgButtonChecked(hwndDlg, IDC_SHOWDATE) ? true : false;
+ instance->messagesShowSec = IsDlgButtonChecked(hwndDlg, IDC_SHOWSECOND) ? true : false;
+ instance->messagesShowName = IsDlgButtonChecked(hwndDlg, IDC_SHOWNAME) ? true : false;
+ instance->messagesShowEvents = IsDlgButtonChecked(hwndDlg, IDC_SHOWEVENTS) ? true : false;
+ instance->messagesUseSmileys = IsDlgButtonChecked(hwndDlg, IDC_SHOWSMILEYS) ? true : false;
+
+ Options::instance->Save();
+ OptionsMessageChanged();
+ }
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+INT_PTR CALLBACK Options::DlgProcOptsSearching(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ CheckDlgButton(hwndDlg, IDC_FORLIST, instance->searchForInList ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_FORMES, instance->searchForInMess ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_MATCHCASE, instance->searchMatchCase ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_MATCHWHOLE, instance->searchMatchWhole ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_ONLYIN, instance->searchOnlyIn ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_ONLYOUT, instance->searchOnlyOut ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_ONLYGROUP, instance->searchOnlyGroup ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_ALLCONTACTS, instance->searchAllContacts ? 1 : 0);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ {
+ if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ONLYIN)
+ {
+ if(IsDlgButtonChecked(hwndDlg, IDC_ONLYIN) && IsDlgButtonChecked(hwndDlg, IDC_ONLYOUT))
+ {
+ CheckDlgButton(hwndDlg, IDC_ONLYOUT, 0);
+ }
+ }
+ else if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ONLYOUT)
+ {
+ if(IsDlgButtonChecked(hwndDlg, IDC_ONLYOUT) && IsDlgButtonChecked(hwndDlg, IDC_ONLYIN))
+ {
+ CheckDlgButton(hwndDlg, IDC_ONLYIN, 0);
+ }
+ }
+
+ if (HIWORD(wParam) == BN_CLICKED)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ if(((LPNMHDR)lParam)->code == PSN_APPLY)
+ {
+ instance->searchForInList = IsDlgButtonChecked(hwndDlg, IDC_FORLIST) ? true : false;
+ instance->searchForInMess = IsDlgButtonChecked(hwndDlg, IDC_FORMES) ? true : false;
+ instance->searchMatchCase = IsDlgButtonChecked(hwndDlg, IDC_MATCHCASE) ? true : false;
+ instance->searchMatchWhole = IsDlgButtonChecked(hwndDlg, IDC_MATCHWHOLE) ? true : false;
+ instance->searchOnlyIn = IsDlgButtonChecked(hwndDlg, IDC_ONLYIN) ? true : false;
+ instance->searchOnlyOut = IsDlgButtonChecked(hwndDlg, IDC_ONLYOUT) ? true : false;
+ instance->searchOnlyGroup = IsDlgButtonChecked(hwndDlg, IDC_ONLYGROUP) ? true : false;
+ instance->searchAllContacts = IsDlgButtonChecked(hwndDlg, IDC_ALLCONTACTS) ? true : false;
+
+ Options::instance->Save();
+ OptionsSearchingChanged();
+ }
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void InitCodepageCB(HWND hwndCB, unsigned int codepage, const std::wstring& name)
+{
+ int cpCount = sizeof(cpTable) / sizeof(cpTable[0]);
+ int selCpIdx = -1;
+ ComboBox_LimitText(hwndCB, 256);
+ for(int i = 0; i < cpCount; ++i)
+ {
+ ComboBox_AddString(hwndCB, TranslateTS(cpTable[i].cpName));
+ if(cpTable[i].cpId == codepage && name == cpTable[i].cpName)
+ selCpIdx = i;
+ }
+
+ if(selCpIdx == -1)
+ {
+ TCHAR buf[300];
+ _stprintf_s(buf, 300, _T("%d;%s"), codepage, name.c_str());
+ ComboBox_SetText(hwndCB, buf);
+ }
+ else
+ {
+ ComboBox_SetCurSel(hwndCB, selCpIdx);
+ }
+
+ ComboBox_LimitText(hwndCB, 127);
+}
+
+unsigned int GetCodepageCB(HWND hwndCB, bool errorReport, unsigned int defCp, const std::wstring& defName, std::wstring& name)
+{
+ int selCpIdx = ComboBox_GetCurSel(hwndCB);
+ if(selCpIdx < 0)
+ {
+ TCHAR text[128];
+ ComboBox_GetText(hwndCB, text, 128);
+ std::wstring str = text;
+ name = _T("");
+ size_t pos = str.find_first_of(_T(';'));
+ if(pos < str.length())
+ {
+ text[pos] = 0;
+ name = str.substr(pos + 1);
+ }
+
+ TCHAR * stopOn = NULL;
+ long cp = _tcstol(text, &stopOn, 10);
+ if((pos >= str.length() || name.empty() || stopOn == text || *stopOn != '\0' || cp < 0 || cp > 0xffff))
+ {
+ if(errorReport)
+ {
+ MessageBox(GetParent(hwndCB), TranslateT("You've entered invalid codepage. Select codepage from combo box or enter correct number."), TranslateT("Invalid codepage"), MB_OK | MB_ICONERROR);
+ SetFocus(hwndCB);
+ }
+
+ name = defName;
+ return -1;
+ }
+
+ return cp;
+ }
+ else
+ {
+ name = cpTable[selCpIdx].cpName;
+ return cpTable[selCpIdx].cpId;
+ }
+}
+
+INT_PTR CALLBACK Options::DlgProcOptsExport(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)FALSE);
+ InitCodepageCB(GetDlgItem(hwndDlg, IDC_TXTENC), instance->codepageTxt, instance->encodingTxt);
+ InitCodepageCB(GetDlgItem(hwndDlg, IDC_HTML1ENC), instance->codepageHtml1, instance->encodingHtml1);
+ InitCodepageCB(GetDlgItem(hwndDlg, IDC_HTML2ENC), instance->codepageHtml2, instance->encodingHtml2);
+ CheckDlgButton(hwndDlg, IDC_HTML1DATE, instance->exportHtml1ShowDate ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_HTML2DATE, instance->exportHtml2ShowDate ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_HTML2SHOWSMILEYS, instance->exportHtml2UseSmileys ? 1 : 0);
+ Edit_LimitText(GetDlgItem(hwndDlg, IDC_HTML2EXTCSSFILE), MAX_PATH);
+ if(instance->extCssHtml2.empty())
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HTML2EXTCSSFILE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CSS_BROWSE), FALSE);
+ }
+ else
+ {
+ CheckDlgButton(hwndDlg, IDC_HTML2EXTCSS, TRUE);
+ Edit_SetText(GetDlgItem(hwndDlg, IDC_HTML2EXTCSSFILE), instance->extCssHtml2.c_str());
+ }
+
+ if(!g_SmileyAddAvail)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HTML2SHOWSMILEYS), FALSE);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)TRUE);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ {
+ BOOL init = (BOOL)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_HTML2EXTCSS)
+ {
+ BOOL en = (BOOL)IsDlgButtonChecked(hwndDlg, IDC_HTML2EXTCSS);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HTML2EXTCSSFILE), en);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CSS_BROWSE), en);
+ }
+ else if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_CSS_BROWSE)
+ {
+ if(!OpenFileDlg(hwndDlg, GetDlgItem(hwndDlg, IDC_HTML2EXTCSSFILE), _T(""), _T("css"), TranslateT("Browse CSS file"), true))
+ {
+ return TRUE;
+ }
+ }
+
+ if (init && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam)==CBN_SELCHANGE || HIWORD(wParam)==CBN_EDITCHANGE || HIWORD(wParam) == EN_CHANGE))
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ if(((LPNMHDR)lParam)->code == PSN_APPLY)
+ {
+ std::wstring newName1, newName2, newName3;
+ unsigned int cp1 = GetCodepageCB(GetDlgItem(hwndDlg, IDC_TXTENC), true, instance->codepageTxt, instance->encodingTxt, newName1);
+ if(cp1 == -1)
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
+ return TRUE;
+ }
+ unsigned int cp2 = GetCodepageCB(GetDlgItem(hwndDlg, IDC_HTML1ENC), true, instance->codepageHtml1, instance->encodingHtml1, newName2);
+ if(cp2 == -1)
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
+ return TRUE;
+ }
+ unsigned int cp3 = GetCodepageCB(GetDlgItem(hwndDlg, IDC_HTML2ENC), true, instance->codepageHtml2, instance->encodingHtml2, newName3);
+ if(cp3 == -1)
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
+ return TRUE;
+ }
+ if(IsDlgButtonChecked(hwndDlg, IDC_HTML2EXTCSS))
+ {
+ if(!CheckFile(GetDlgItem(hwndDlg, IDC_HTML2EXTCSSFILE)))
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
+ return TRUE;
+ }
+
+ TCHAR buf[MAX_PATH];
+ Edit_GetText(GetDlgItem(hwndDlg, IDC_HTML2EXTCSSFILE), buf, MAX_PATH);
+ instance->extCssHtml2 = buf;
+ }
+ else
+ {
+ instance->extCssHtml2 = _T("");
+ }
+
+ instance->codepageTxt = cp1;
+ instance->encodingTxt = newName1;
+ instance->codepageHtml1 = cp2;
+ instance->encodingHtml1 = newName2;
+ instance->codepageHtml2 = cp3;
+ instance->encodingHtml2 = newName3;
+ instance->exportHtml1ShowDate = IsDlgButtonChecked(hwndDlg, IDC_HTML1DATE) ? true : false;
+ instance->exportHtml2ShowDate = IsDlgButtonChecked(hwndDlg, IDC_HTML2DATE) ? true : false;
+ instance->exportHtml2UseSmileys = IsDlgButtonChecked(hwndDlg, IDC_HTML2SHOWSMILEYS) ? true : false;
+
+ Options::instance->Save();
+ }
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+struct DlgTaskOpt
+{
+ std::list<TaskOptions>* tasks;
+ TaskOptions* to;
+};
+
+INT_PTR CALLBACK Options::DlgProcOptsScheduler(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ std::list<TaskOptions>* tasks = new std::list<TaskOptions>(Options::instance->taskOptions.begin(), Options::instance->taskOptions.end());
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)tasks);
+ HWND listTasks = GetDlgItem(hwndDlg, IDC_LIST_TASKS);
+ for(std::list<TaskOptions>::iterator it = tasks->begin(); it != tasks->end(); ++it)
+ {
+ ListBox_AddString(listTasks, it->taskName.c_str());
+ }
+
+ if(!bPopupsEnabled)
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SCHEDULER_ALERTS), FALSE);
+ }
+
+ CheckDlgButton(hwndDlg, IDC_SCHEDULER_ALERTS, instance->schedulerAlerts ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_SCHEDULER_HISTORY_ALERTS, instance->schedulerHistoryAlerts ? 1 : 0);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_TASK), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_TASK), FALSE);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ {
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ std::list<TaskOptions>* tasks = (std::list<TaskOptions>*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ HWND listTasks = GetDlgItem(hwndDlg, IDC_LIST_TASKS);
+ int sel = ListBox_GetCurSel(listTasks);
+ TaskOptions toAdd;
+ TaskOptions* to = &toAdd;
+ switch(LOWORD(wParam))
+ {
+ case IDC_EDIT_TASK:
+ if(sel >= 0)
+ {
+ std::list<TaskOptions>::iterator it = tasks->begin();
+ while(sel-- > 0 && it != tasks->end())
+ ++it;
+ if(it == tasks->end())
+ break;
+ to = &(*it);
+ }
+ else
+ break;
+ case IDC_ADD_TASK:
+ {
+ DlgTaskOpt top;
+ top.tasks = tasks;
+ top.to = to;
+ if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_DLG_TASK), hwndDlg, DlgProcOptsTask, (LPARAM)&top) == IDOK)
+ {
+ if(LOWORD(wParam) == IDC_ADD_TASK)
+ {
+ tasks->push_back(*to);
+ ListBox_AddString(listTasks, to->taskName.c_str());
+ ListBox_SetCurSel(listTasks, tasks->size() - 1);
+ }
+ else
+ {
+ sel = ListBox_GetCurSel(listTasks);
+ ListBox_DeleteString(listTasks, sel);
+ ListBox_InsertString(listTasks, sel, to->taskName.c_str());
+ ListBox_SetCurSel(listTasks, sel);
+ }
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_TASK), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_TASK), TRUE);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+
+ break;
+ case IDC_DELETE_TASK:
+ if(sel >= 0)
+ {
+ ListBox_DeleteString(listTasks, sel);
+ std::list<TaskOptions>::iterator it = tasks->begin();
+ while(sel-- > 0 && it != tasks->end())
+ ++it;
+ if(it != tasks->end())
+ tasks->erase(it);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_TASK), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_TASK), FALSE);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+
+ break;
+ default:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ else if(HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_LIST_TASKS)
+ {
+ HWND listTasks = GetDlgItem(hwndDlg, IDC_LIST_TASKS);
+ int sel = ListBox_GetCurSel(listTasks);
+ if(sel < 0)
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_TASK), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_TASK), FALSE);
+ }
+ else
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_TASK), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE_TASK), TRUE);
+ }
+ }
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ if(((LPNMHDR)lParam)->code == PSN_APPLY)
+ {
+ std::list<TaskOptions>* tasks = (std::list<TaskOptions>*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ instance->schedulerAlerts = IsDlgButtonChecked(hwndDlg, IDC_SCHEDULER_ALERTS) ? true : false;
+ instance->schedulerHistoryAlerts = IsDlgButtonChecked(hwndDlg, IDC_SCHEDULER_HISTORY_ALERTS) ? true : false;
+ Options::instance->SaveTasks(tasks);
+ OptionsSchedulerChanged();
+ InitTaskMenuItems();
+ }
+ return TRUE;
+ }
+ case WM_DESTROY:
+ {
+ std::list<TaskOptions>* tasks = (std::list<TaskOptions>*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ delete tasks;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, NULL);
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+void ResetListOptions(HWND hwnd)
+{
+ SendMessage(hwnd, CLM_SETGREYOUTFLAGS, 0, 0);
+ SendMessage(hwnd, CLM_SETLEFTMARGIN, 2, 0);
+ SendMessage(hwnd, CLM_SETBKBITMAP, 0, (LPARAM)(HBITMAP) NULL);
+ SendMessage(hwnd, CLM_SETBKCOLOR, GetSysColor(COLOR_WINDOW), 0);
+ SendMessage(hwnd, CLM_SETINDENT, 10, 0);
+ for (int i = 0; i <= FONTID_MAX; i++)
+ SendMessage(hwnd, CLM_SETTEXTCOLOR, i, GetSysColor(COLOR_WINDOWTEXT));
+}
+
+void RebuildList(HWND hwnd, HANDLE hSystem, TaskOptions* to)
+{
+ HANDLE hItem;
+ if(to->isSystem && hSystem)
+ {
+ SendMessage(hwnd, CLM_SETCHECKMARK, (WPARAM) hSystem, 1);
+ }
+
+ for(size_t i = 0; i < to->contacts.size(); ++i)
+ {
+ hItem = (HANDLE) SendMessage(hwnd, CLM_FINDCONTACT, (WPARAM) to->contacts[i], 0);
+ if (hItem)
+ SendMessage(hwnd, CLM_SETCHECKMARK, (WPARAM) hItem, 1);
+ }
+}
+
+void SaveList(HWND hwnd, HANDLE hSystem, TaskOptions* to)
+{
+ HANDLE hContact, hItem;
+
+ to->contacts.clear();
+ if (hSystem)
+ to->isSystem = SendMessage(hwnd, CLM_GETCHECKMARK, (WPARAM) hSystem, 0) != 0;
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ do
+ {
+ hItem = (HANDLE) SendMessage(hwnd, CLM_FINDCONTACT, (WPARAM) hContact, 0);
+ if (hItem && SendMessage(hwnd, CLM_GETCHECKMARK, (WPARAM) hItem, 0))
+ to->contacts.push_back(hContact);
+ }
+ while (hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0));
+}
+
+bool IsValidTask(TaskOptions& to, std::list<TaskOptions>* top = NULL, std::wstring* err = NULL, std::wstring* errDescr = NULL);
+
+#ifndef LOCALE_SSHORTTIME
+#define LOCALE_SSHORTTIME 0x00000079
+#endif
+
+INT_PTR CALLBACK Options::DlgProcOptsTask(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static HANDLE hSystem;
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ DlgTaskOpt* top = (DlgTaskOpt*)lParam;
+ TaskOptions* to = top->to;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+ HWND comboType = GetDlgItem(hwndDlg, IDC_TASK_TYPE);
+ HWND filter = GetDlgItem(hwndDlg, IDC_TASK_FILTER);
+ HWND eventUnit = GetDlgItem(hwndDlg, IDC_EVENT_UNIT);
+ HWND trigerType = GetDlgItem(hwndDlg, IDC_TRIGER_TYPE);
+ HWND exportType = GetDlgItem(hwndDlg, IDC_EXPORT_TYPE);
+ HWND importType = GetDlgItem(hwndDlg, IDC_IMPORT_TYPE);
+ HWND compress = GetDlgItem(hwndDlg, IDC_COMPRESS);
+ HWND exportPath = GetDlgItem(hwndDlg, IDC_EXPORT_PATH);
+ HWND ftpFile = GetDlgItem(hwndDlg, IDC_FTP);
+ HWND ftpFileButton = GetDlgItem(hwndDlg, IDC_UPLOAD);
+ HWND contactList = GetDlgItem(hwndDlg, IDC_LIST_CONTACTSEX);
+ HWND weekList = GetDlgItem(hwndDlg, IDC_TRIGER_WEEK);
+ HWND day = GetDlgItem(hwndDlg, IDC_TRIGER_DAY);
+ HWND deltaTime = GetDlgItem(hwndDlg, IDC_TRIGER_DELTA_TIME);
+ HWND time = GetDlgItem(hwndDlg, IDC_TRIGER_TIME);
+ HWND name = GetDlgItem(hwndDlg, IDC_TASK_NAME);
+ HWND active = GetDlgItem(hwndDlg, IDC_TASK_ACTIVE);
+ HWND star = GetDlgItem(hwndDlg, IDC_TASK_STAR);
+ HWND password = GetDlgItem(hwndDlg, IDC_PASSWORD);
+ HWND expImp = GetDlgItem(hwndDlg, IDC_EXPIMP);
+
+ Edit_LimitText(name, 16);
+ Edit_SetText(name, to->taskName.c_str());
+
+ Button_SetCheck(active, to->active);
+
+ Button_SetCheck(expImp, to->exportImported);
+
+ ComboBox_AddString(comboType, TranslateT("Export"));
+ ComboBox_AddString(comboType, TranslateT("Delete"));
+ ComboBox_AddString(comboType, TranslateT("Export and Delete"));
+ ComboBox_AddString(comboType, TranslateT("Import"));
+ ComboBox_AddString(comboType, TranslateT("Import and Marge"));
+ ComboBox_SetCurSel(comboType, to->type);
+
+ Edit_LimitText(GetDlgItem(hwndDlg, IDC_EVENT_TIME), 6);
+ SetDlgItemInt(hwndDlg, IDC_EVENT_TIME, to->eventDeltaTime, TRUE);
+ ComboBox_AddString(eventUnit, TranslateT("Minute"));
+ ComboBox_AddString(eventUnit, TranslateT("Hour"));
+ ComboBox_AddString(eventUnit, TranslateT("Day"));
+ ComboBox_SetCurSel(eventUnit, to->eventUnit);
+
+ ComboBox_AddString(filter, TranslateT("Default history events"));
+ ComboBox_AddString(filter, TranslateT("All events"));
+ int selFilter = to->filterId;
+ if(selFilter > 1)
+ selFilter = 0;
+ int i = 1;
+ for(std::vector<FilterOptions>::iterator it = instance->customFilters.begin(); it != instance->customFilters.end(); ++it)
+ {
+ ++i;
+ ComboBox_AddString(filter, it->name.c_str());
+ if(to->filterId > 1 && it->name == to->filterName)
+ {
+ selFilter = i;
+ }
+ }
+ ComboBox_SetCurSel(filter, selFilter);
+
+ ComboBox_AddString(trigerType, TranslateT("At Start"));
+ ComboBox_AddString(trigerType, TranslateT("At Finish"));
+ ComboBox_AddString(trigerType, TranslateT("Daily"));
+ ComboBox_AddString(trigerType, TranslateT("Weekly"));
+ ComboBox_AddString(trigerType, TranslateT("Monthly"));
+ ComboBox_AddString(trigerType, TranslateT("Delta time (minutes)"));
+ ComboBox_AddString(trigerType, TranslateT("Delta time (hours)"));
+ ComboBox_SetCurSel(trigerType, to->trigerType);
+
+ ComboBox_AddString(exportType, TranslateT("Rich Html"));
+ ComboBox_AddString(exportType, TranslateT("Plain Html"));
+ ComboBox_AddString(exportType, TranslateT("Txt"));
+ ComboBox_AddString(exportType, TranslateT("Binary"));
+ ComboBox_AddString(exportType, TranslateT("Dat (mContacts)"));
+ ComboBox_SetCurSel(exportType, to->exportType);
+
+ ComboBox_AddString(importType, TranslateT("Binary"));
+ ComboBox_AddString(importType, TranslateT("Dat (mContacts)"));
+ ComboBox_SetCurSel(importType, to->importType);
+
+ Button_SetCheck(compress, to->compress);
+ Edit_LimitText(password, 99);
+ SetWindowTextA(password, to->zipPassword.c_str());
+
+ Edit_LimitText(exportPath, MAX_PATH);
+ Edit_SetText(exportPath, to->filePath.c_str());
+
+ if(!FTPAvail())
+ {
+ EnableWindow(ftpFile, FALSE);
+ EnableWindow(ftpFileButton, FALSE);
+ to->useFtp = false;
+ }
+
+ Button_SetCheck(ftpFileButton, to->useFtp);
+ Edit_SetText(ftpFile, to->ftpName.c_str());
+
+ ComboBox_AddString(weekList, TranslateT("Monday"));
+ ComboBox_AddString(weekList, TranslateT("Tuesday"));
+ ComboBox_AddString(weekList, TranslateT("Wednesday"));
+ ComboBox_AddString(weekList, TranslateT("Thursday"));
+ ComboBox_AddString(weekList, TranslateT("Friday"));
+ ComboBox_AddString(weekList, TranslateT("Saturday"));
+ ComboBox_AddString(weekList, TranslateT("Sunday"));
+ ComboBox_SetCurSel(weekList, to->dayOfWeek);
+
+ Edit_LimitText(day, 2);
+ SetDlgItemInt(hwndDlg, IDC_TRIGER_DAY, to->dayOfMonth, FALSE);
+
+ Edit_LimitText(deltaTime, 4);
+ SetDlgItemInt(hwndDlg, IDC_TRIGER_DELTA_TIME, to->deltaTime, FALSE);
+
+ TCHAR timeFormat[10];
+ if(GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTTIME, timeFormat, 10) == 0)
+ {
+ TCHAR sep = _T(':');
+ if(GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STIME, timeFormat, 10) > 0)
+ sep = timeFormat[0];
+ _stprintf_s(timeFormat, _T("HH%cmm"), sep);
+ }
+
+ SYSTEMTIME st;
+ GetSystemTime (&st);
+ st.wHour = to->dayTime/60;
+ st.wMinute = to->dayTime%60;
+ st.wSecond = 0;
+ st.wMilliseconds = 0;
+ DateTime_SetFormat(time, timeFormat);
+ DateTime_SetSystemtime(time, GDT_VALID, &st);
+
+ CLCINFOITEM cii = { 0 };
+ cii.cbSize = sizeof(cii);
+ cii.flags = CLCIIF_GROUPFONT | CLCIIF_CHECKBOX | CLCIIF_BELOWCONTACTS;
+ cii.pszText = TranslateT("System");
+ hSystem = (HANDLE) SendMessage(contactList, CLM_ADDINFOITEM, 0, (LPARAM) & cii);
+ SendMessage(contactList, CLM_AUTOREBUILD, 0, 0);
+ ResetListOptions(contactList);
+ RebuildList(contactList, hSystem, to);
+
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_TASK_TYPE, CBN_SELCHANGE), NULL);
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_TRIGER_TYPE, CBN_SELCHANGE), NULL);
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_COMPRESS, BN_CLICKED), NULL);
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_UPLOAD, BN_CLICKED), NULL);
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_COMPRESS, BN_CLICKED), NULL);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ if (LOWORD(wParam) == IDOK)
+ {
+ DlgTaskOpt* top = (DlgTaskOpt*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ TaskOptions* to = top->to;
+ TaskOptions toCp(*to);
+ toCp.taskName.resize(17);
+ int nameLen = Edit_GetText(GetDlgItem(hwndDlg, IDC_TASK_NAME), (wchar_t*)toCp.taskName.c_str(), 17);
+ toCp.taskName.resize(nameLen);
+ toCp.active = Button_GetCheck(GetDlgItem(hwndDlg, IDC_TASK_ACTIVE)) != 0;
+ toCp.exportImported = Button_GetCheck(GetDlgItem(hwndDlg, IDC_EXPIMP)) != 0;
+ toCp.type = (enum TaskOptions::TaskType)ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_TASK_TYPE));
+ toCp.filterId = ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_TASK_FILTER));
+ if(toCp.filterId > 1)
+ toCp.filterName = instance->customFilters[toCp.filterId - 2].name;
+ BOOL isOK = FALSE;
+ toCp.eventDeltaTime = GetDlgItemInt(hwndDlg, IDC_EVENT_TIME, &isOK, TRUE);
+ if(!isOK)
+ {
+ TCHAR msg[256];
+ _stprintf_s(msg, TranslateT("Invalid '%s' value."), TranslateT("Events older than"));
+ MessageBox(hwndDlg, msg, TranslateT("Error"), MB_ICONERROR);
+ break;
+ }
+ toCp.eventUnit = (enum TaskOptions::EventUnit)ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_EVENT_UNIT));
+ toCp.trigerType = (enum TaskOptions::TrigerType)ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_TRIGER_TYPE));
+ toCp.exportType = (enum IExport::ExportType)ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_EXPORT_TYPE));
+ toCp.importType = (enum IImport::ImportType)ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_IMPORT_TYPE));
+ toCp.compress = Button_GetCheck(GetDlgItem(hwndDlg, IDC_COMPRESS)) != 0;
+ char bufC[100];
+ GetWindowTextA(GetDlgItem(hwndDlg, IDC_PASSWORD), bufC, 100);
+ toCp.zipPassword = bufC;
+ HWND exportPath = GetDlgItem(hwndDlg, IDC_EXPORT_PATH);
+ int exLen = Edit_GetTextLength(exportPath);
+ toCp.filePath.resize(exLen + 1);
+ Edit_GetText(exportPath, (wchar_t*)toCp.filePath.c_str(), exLen + 1);
+ toCp.filePath.resize(exLen);
+ toCp.useFtp = Button_GetCheck(GetDlgItem(hwndDlg, IDC_UPLOAD)) != 0;
+ HWND ftpFile = GetDlgItem(hwndDlg, IDC_FTP);
+ exLen = Edit_GetTextLength(ftpFile);
+ toCp.ftpName.resize(exLen + 1);
+ Edit_GetText(ftpFile, (wchar_t*)toCp.ftpName.c_str(), exLen + 1);
+ toCp.ftpName.resize(exLen);
+ SYSTEMTIME st;
+ DateTime_GetSystemtime(GetDlgItem(hwndDlg, IDC_TRIGER_TIME), &st);
+ toCp.dayTime = st.wHour * 60 + st.wMinute;
+ toCp.dayOfWeek = ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_TRIGER_WEEK));
+ toCp.dayOfMonth = GetDlgItemInt(hwndDlg, IDC_TRIGER_DAY, &isOK, FALSE);
+ if(!isOK)
+ {
+ if(toCp.trigerType == TaskOptions::Monthly)
+ {
+ TCHAR msg[256];
+ _stprintf_s(msg, TranslateT("Invalid '%s' value."), TranslateT("Day"));
+ MessageBox(hwndDlg, msg, TranslateT("Error"), MB_ICONERROR);
+ break;
+ }
+ else
+ toCp.dayOfMonth = to->dayOfMonth;
+ }
+ toCp.deltaTime = GetDlgItemInt(hwndDlg, IDC_TRIGER_DELTA_TIME, &isOK, FALSE);
+ if(!isOK)
+ {
+ if(toCp.trigerType == TaskOptions::DeltaMin || toCp.trigerType == TaskOptions::DeltaHour)
+ {
+ TCHAR msg[256];
+ _stprintf_s(msg, TranslateT("Invalid '%s' value."), TranslateT("Delta time"));
+ MessageBox(hwndDlg, msg, TranslateT("Error"), MB_ICONERROR);
+ break;
+ }
+ else
+ toCp.deltaTime = to->deltaTime;
+ }
+ SaveList(GetDlgItem(hwndDlg, IDC_LIST_CONTACTSEX), hSystem, &toCp);
+ std::wstring err;
+ std::wstring errDescr;
+ std::wstring lastName = to->taskName;
+ to->taskName = L"";
+ if(!IsValidTask(toCp, top->tasks, &err, &errDescr))
+ {
+ to->taskName = lastName;
+ TCHAR msg[256];
+ if(err.empty())
+ _tcscpy_s(msg, TranslateT("Some value is invalid"));
+ else if(errDescr.empty())
+ {
+ _stprintf_s(msg, TranslateT("Invalid '%s' value."), err.c_str());
+ }
+ else
+ {
+ _stprintf_s(msg, TranslateT("Invalid '%s' value.\n%s"), err.c_str(), errDescr.c_str());
+ }
+
+ MessageBox(hwndDlg, msg, TranslateT("Error"), MB_ICONERROR);
+ break;
+ }
+
+ toCp.lastExport = time(NULL);
+
+ *to = toCp;
+ EndDialog(hwndDlg, IDOK);
+ }
+ else if (LOWORD(wParam) == IDCANCEL)
+ {
+ EndDialog(hwndDlg, IDCANCEL);
+ }
+ else if (LOWORD(wParam) == IDC_UPLOAD)
+ {
+ if(Button_GetCheck(GetDlgItem(hwndDlg, IDC_UPLOAD)) == 0)
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FTP), FALSE);
+ }
+ else
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FTP), TRUE);
+ }
+ }
+ else if(LOWORD(wParam) == IDC_COMPRESS)
+ {
+ if(Button_GetCheck(GetDlgItem(hwndDlg, IDC_COMPRESS)) == 0)
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD_LABEL), FALSE);
+ }
+ else
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD_LABEL), TRUE);
+ }
+ }
+ }
+ else if (HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ if(LOWORD(wParam) == IDC_TASK_TYPE)
+ {
+ TaskOptions::TaskType sel = (enum TaskOptions::TaskType)ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_TASK_TYPE));
+ int show = sel == TaskOptions::Delete ? SW_HIDE : SW_SHOW;
+ int showFilter = (sel == TaskOptions::Import || sel == TaskOptions::ImportAndMarge) ? SW_HIDE : SW_SHOW;
+ int showImport = (sel == TaskOptions::Import || sel == TaskOptions::ImportAndMarge) ? SW_SHOW : SW_HIDE;
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EXPORT_TYPE), show);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EXPORT_TYPE_LABEL), show);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_COMPRESS), show);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), show);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_PASSWORD_LABEL), show);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EXPORT_PATH), show);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EXPORT_PATH_LABEL), show);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_FTP), show);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_UPLOAD), show);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_FTP_LABEL), show);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EXPIMP), show);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TASK_FILTER), showFilter);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TASK_FILTER_LABEL), showFilter);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EVENT_TIME), showFilter);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EVENT_UNIT), showFilter);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EVENT_LABEL), showFilter);
+ if(show != showFilter)
+ {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EXPORT_TYPE), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_EXPIMP), SW_HIDE);
+ }
+
+ ShowWindow(GetDlgItem(hwndDlg, IDC_IMPORT_TYPE), showImport);
+ std::wstring str;
+ TCHAR* compressText = TranslateT("Compress output files");
+ TCHAR* uploadText = TranslateT("Upload to FTP (WinSCP requred)");
+ TCHAR* typeText = TranslateT("Export to");
+ if(showFilter == SW_HIDE)
+ {
+ str = TranslateT("** Use <ext> to insert extension, <contact> to insert contact name");
+ compressText = TranslateT("Input files are compressed");
+ uploadText = TranslateT("Download from FTP (WinSCP requred)");
+ typeText = TranslateT("Import from");
+ }
+ else if(show == SW_HIDE)
+ {
+ str = TranslateT("* Use negative values to filter younger events");
+ }
+ else
+ {
+ str = TranslateT("* Use negative values to filter younger events");
+ str += _T("\n");
+ str += TranslateT("** Use <date> to insert date, <ext> to insert extension, <contact> to insert contact name");
+ }
+
+ Static_SetText(GetDlgItem(hwndDlg, IDC_TASK_STAR), str.c_str());
+ Button_SetText(GetDlgItem(hwndDlg, IDC_COMPRESS), compressText);
+ Button_SetText(GetDlgItem(hwndDlg, IDC_UPLOAD), uploadText);
+ Static_SetText(GetDlgItem(hwndDlg, IDC_EXPORT_TYPE_LABEL), typeText);
+ }
+ else if(LOWORD(wParam) == IDC_TRIGER_TYPE)
+ {
+ TaskOptions::TrigerType sel = (enum TaskOptions::TrigerType)ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_TRIGER_TYPE));
+ int showT = (sel == TaskOptions::Daily || sel == TaskOptions::Weekly || sel == TaskOptions::Monthly) ? SW_SHOW : SW_HIDE;
+ int showW = sel == TaskOptions::Weekly ? SW_SHOW : SW_HIDE;
+ int showM = sel == TaskOptions::Monthly ? SW_SHOW : SW_HIDE;
+ int showDT = (sel == TaskOptions::DeltaMin || sel == TaskOptions::DeltaHour) ? SW_SHOW : SW_HIDE;
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TRIGER_TIME), showT);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TRIGER_TIME_LABEL), showT);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TRIGER_WEEK), showW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TRIGER_WEEK_LABEL), showW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TRIGER_DAY), showM);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TRIGER_DAY_LABEL), showM);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TRIGER_DELTA_TIME), showDT);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_TRIGER_DELTA_TIME_LABEL), showDT);
+ }
+ }
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ NMHDR* nmhdr = (NMHDR *) lParam;
+ if (nmhdr->idFrom == IDC_LIST_CONTACTSEX && nmhdr->code == CLN_OPTIONSCHANGED)
+ {
+ ResetListOptions(hwndDlg);
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+bool Options::FTPAvail()
+{
+ DWORD atr = GetFileAttributes(instance->ftpExePath.c_str());
+ return !(atr == INVALID_FILE_ATTRIBUTES || atr & FILE_ATTRIBUTE_DIRECTORY);
+}
diff --git a/plugins/BasicHistory/src/Options.h b/plugins/BasicHistory/src/Options.h
new file mode 100644
index 0000000000..4a5d17229c
--- /dev/null
+++ b/plugins/BasicHistory/src/Options.h
@@ -0,0 +1,199 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+#include "IImport.h"
+#include "IExport.h"
+
+struct FilterOptions
+{
+ FilterOptions()
+ :onlyIncomming(false),
+ onlyOutgoing(false)
+ {
+ }
+
+ FilterOptions(const std::wstring& _name)
+ :name(_name),
+ onlyIncomming(false),
+ onlyOutgoing(false)
+ {
+ }
+
+ std::wstring name;
+ std::vector<int> events;
+ bool onlyIncomming;
+ bool onlyOutgoing;
+};
+
+struct TaskOptions
+{
+ bool compress;
+ bool useFtp;
+ bool isSystem;
+ bool active;
+ bool forceExecute;
+ bool showMBAfterExecute;
+ bool exportImported;
+ enum TaskType
+ {
+ Export,
+ Delete,
+ ExportAndDelete,
+ Import,
+ ImportAndMarge
+ } type;
+
+ enum EventUnit
+ {
+ Minute,
+ Hour,
+ Day
+ } eventUnit;
+
+ enum TrigerType
+ {
+ AtStart,
+ AtEnd,
+ Daily,
+ Weekly,
+ Monthly,
+ DeltaMin,
+ DeltaHour
+ } trigerType;
+
+ IExport::ExportType exportType;
+ IImport::ImportType importType;
+ int eventDeltaTime;
+ int filterId;
+ int dayTime;
+ int dayOfWeek;
+ int dayOfMonth;
+ int deltaTime;
+ int orderNr;
+ time_t lastExport;
+ std::wstring ftpName;
+ std::wstring filterName;
+ std::wstring filePath;
+ std::wstring taskName;
+ std::string zipPassword; // char* because zip file using ANSI password
+ std::vector<HANDLE> contacts;
+ TaskOptions()
+ {
+ forceExecute = false;
+ showMBAfterExecute = false;
+ exportImported = true;
+ type = Export;
+ eventUnit = Hour;
+ trigerType = AtStart;
+ exportType = IExport::RichHtml;
+ importType = IImport::Binary;
+ eventDeltaTime = 0;
+ filterId = 0;
+ compress = true;
+ useFtp = false;
+ isSystem = false;
+ active = true;
+ dayTime = 20 * 60;
+ dayOfWeek = 0;
+ dayOfMonth = 1;
+ deltaTime = 24;
+ orderNr = 0;
+ TCHAR buf[MAX_PATH];
+ if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, buf)))
+ {
+ filePath = buf;
+ filePath += _T("\\");
+ }
+
+ filePath += TranslateT("History");
+ filePath += _T("_<contact>_<date>.<ext>");
+ lastExport = time(NULL);
+ }
+};
+
+class Options
+{
+private:
+ std::vector<FilterOptions> customFiltersTemp;
+public:
+ Options();
+ ~Options();
+ static int InitOptions(WPARAM wParam, LPARAM lParam);
+
+ static INT_PTR CALLBACK DlgProcOptsMain(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+ static INT_PTR CALLBACK DlgProcOptsGroupList(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+ static INT_PTR CALLBACK DlgProcOptsMessages(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+ static INT_PTR CALLBACK DlgProcOptsSearching(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+ static INT_PTR CALLBACK DlgProcOptsExport(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+ static INT_PTR CALLBACK DlgProcOptsScheduler(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+ static INT_PTR CALLBACK DlgProcOptsTask(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+ static bool FTPAvail();
+
+ static Options *instance;
+ void Save();
+ void SaveTasks(std::list<TaskOptions>* tasks);
+ void SaveTaskTime(TaskOptions& to);
+ void Load(void);
+ void LoadTasks();
+ void Unload();
+
+ bool showContacts, showContactGroups, groupNewOnTop, groupShowEvents, groupShowTime, groupShowName, groupShowMessage;
+ bool messagesNewOnTop, messagesShowDate, messagesShowSec, messagesShowName, messagesShowEvents, messagesUseSmileys;
+ bool searchForInList, searchForInMess, searchMatchCase, searchMatchWhole, searchOnlyIn, searchOnlyOut, searchOnlyGroup, searchAllContacts;
+ bool schedulerAlerts, schedulerHistoryAlerts;
+ int groupMessageLen, groupTime, groupMessagesNumber;
+
+ std::vector<FilterOptions> customFilters;
+ int defFilter;
+ unsigned int codepageTxt, codepageHtml1, codepageHtml2;
+ std::wstring encodingTxt, encodingHtml1, encodingHtml2;
+ bool exportHtml1ShowDate, exportHtml2ShowDate, exportHtml2UseSmileys;
+ std::wstring extCssHtml2;
+ std::vector<TaskOptions> taskOptions;
+ std::wstring ftpLogPath;
+ std::wstring ftpExePathDef;
+ std::wstring ftpExePath;
+ CRITICAL_SECTION criticalSection;
+
+ enum Fonts
+ {
+ OutTimestamp = 0,
+ InTimestamp,
+ OutName,
+ InName,
+ OutMessages,
+ InMessages,
+ GroupList,
+ Find,
+ };
+
+ enum Colors
+ {
+ OutBackground = 0,
+ InBackground,
+ GroupListBackground,
+ WindowBackground,
+ ContactListBackground,
+ FindBackground,
+ };
+
+ COLORREF GetFont(Fonts fontId, PLOGFONT font);
+ COLORREF GetColor(Colors colorId);
+};
+
diff --git a/plugins/BasicHistory/src/PlainHtmlExport.cpp b/plugins/BasicHistory/src/PlainHtmlExport.cpp
new file mode 100644
index 0000000000..050b4e457e
--- /dev/null
+++ b/plugins/BasicHistory/src/PlainHtmlExport.cpp
@@ -0,0 +1,106 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "PlainHtmlExport.h"
+#include "Options.h"
+#define EXP_FILE (*stream)
+
+PlainHtmlExport::~PlainHtmlExport()
+{
+}
+
+extern std::wstring MakeTextHtmled(const std::wstring& message, std::queue<std::pair<size_t, size_t> >* positionMap = NULL);
+extern std::wstring UrlHighlightHtml(const std::wstring& message, bool& isUrl);
+
+void PlainHtmlExport::WriteHeader(const std::wstring &fileName, const std::wstring &filterName, const std::wstring &myName, const std::wstring &myId, const std::wstring &name1, const std::wstring &proto1, const std::wstring &id1, const std::string& baseProto1, const std::wstring& encoding)
+{
+ EXP_FILE << _T("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
+ EXP_FILE << _T("<html><head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=") << encoding << _T("\">\n");
+ EXP_FILE << _T("<title>") << TranslateT("History Log") << _T(" [") << MakeTextHtmled(myName) << _T("] - [") << MakeTextHtmled(name1) << _T("]</title>\n");
+ EXP_FILE << _T("<style type=\"text/css\"><!--\n");
+ EXP_FILE << _T("h3 { color: #666666; text-align: center; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 16pt; }\n");
+ EXP_FILE << _T("h4 { text-align: center; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 14pt; }\n");
+ EXP_FILE << _T("h6 { font-weight: normal; color: #000000; text-align: center; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 8pt; }\n");
+ EXP_FILE << _T(".mes { border-top-width: 1px; border-right-width: 0px; border-bottom-width: 0px;border-left-width: 0px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #666666; border-bottom-color: #666666; padding: 4px; color: #000000; font: normal normal normal 8pt normal Tahoma, Tahoma, Verdana, Arial, sans-serif; text-decoration: none; }\n");
+ EXP_FILE << _T(".text { clear: both; }\n");
+ EXP_FILE << _T(".nick { float: left; font: normal normal bold 8pt normal Tahoma, Tahoma, Verdana, Arial, sans-serif; text-decoration: none; }\n");
+ EXP_FILE << _T(".date { float: right; clear: right; font: normal normal bold 8pt normal Tahoma, Tahoma, Verdana, Arial, sans-serif; text-decoration: none; }\n");
+ EXP_FILE << _T(".url { color: #0000FF; }\n");
+ EXP_FILE << _T(".nick#inc { color: #C83F6B; }\n");
+ EXP_FILE << _T(".nick#out { color: #0860BD; }\n");
+ EXP_FILE << _T(".date#inc { color: #000000; }\n");
+ EXP_FILE << _T(".date#out { color: #000000; }\n");
+ EXP_FILE << _T(".mes#event0 { background-color: #DBDBDB; }\n");
+ EXP_FILE << _T(".mes#event1 { background-color: #EEEEEE; }\n");
+ EXP_FILE << _T(".mes#event2 { background-color: #CCD9F4; }\n");
+ EXP_FILE << _T(".mes#session { background-color: #FFFDD7; }\n");
+ EXP_FILE << _T("--></style>\n</head><body>\n<h4>") << TranslateT("History Log") << _T("</h4>\n<h3>");
+ EXP_FILE << MakeTextHtmled(myName);
+ if(proto1.length() || myId.length())
+ {
+ EXP_FILE << _T(" (") << MakeTextHtmled(proto1) << _T(": ") << MakeTextHtmled(myId) << _T(") - ");
+ }
+ else
+ {
+ EXP_FILE << _T(" - ");
+ }
+
+ EXP_FILE << MakeTextHtmled(name1);
+ if(proto1.length() || id1.length())
+ {
+ EXP_FILE << _T(" (") << MakeTextHtmled(proto1) << _T(": ") << MakeTextHtmled(id1) << _T(")</h3>\n");
+ }
+ else
+ {
+ EXP_FILE << _T("</h3>\n");
+ }
+
+ EXP_FILE << _T("<h6>") << TranslateT("Filter:") << _T(" ") << MakeTextHtmled(filterName) << _T("</h6>\n");
+}
+
+void PlainHtmlExport::WriteFooter()
+{
+ EXP_FILE << _T("<div class=mes></div>\n</body></html>\n");
+}
+
+void PlainHtmlExport::WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText)
+{
+ TCHAR buf[256];
+ EXP_FILE << _T("<div class=mes id=session>\n");
+ _stprintf_s(buf, TranslateT("Conversation started at %s"), time.c_str());
+ EXP_FILE << _T("<div class=text>") << buf << _T("</div>\n");
+ EXP_FILE << _T("</div>\n");
+}
+
+void PlainHtmlExport::WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei)
+{
+ TCHAR *id = isMe ? _T("out") : _T("inc");
+ TCHAR* ev = (isMe ? _T("1") : _T("0"));
+ bool isUrl = false;
+ std::wstring& mes = UrlHighlightHtml(MakeTextHtmled(message), isUrl);
+ if(isUrl)
+ ev = _T("2");
+ EXP_FILE << _T("<div class=mes id=event") << ev << _T(">\n");
+ EXP_FILE << _T("<div class=nick id=") << id << _T(">") << MakeTextHtmled(user) << _T(":</div>\n");
+ EXP_FILE << _T("<div class=date id=") << id << _T(">") << (Options::instance->exportHtml1ShowDate ? longDate : shortDate) << _T("</div>\n");
+ EXP_FILE << _T("<div class=text>\n");
+ EXP_FILE << mes;
+ EXP_FILE << _T("\n</div>\n");
+ EXP_FILE << _T("</div>\n");
+}
diff --git a/plugins/BasicHistory/src/PlainHtmlExport.h b/plugins/BasicHistory/src/PlainHtmlExport.h
new file mode 100644
index 0000000000..bb1c971ea3
--- /dev/null
+++ b/plugins/BasicHistory/src/PlainHtmlExport.h
@@ -0,0 +1,37 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+#include "IExport.h"
+class PlainHtmlExport :
+ public IExport
+{
+public:
+ virtual const TCHAR* GetExt()
+ {
+ return _T("html");
+ }
+
+ virtual void WriteHeader(const std::wstring &fileName, const std::wstring &filterName, const std::wstring &myName, const std::wstring &myId, const std::wstring &name1, const std::wstring &proto1, const std::wstring &id1, const std::string& baseProto1, const std::wstring& encoding);
+ virtual void WriteFooter();
+ virtual void WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText);
+ virtual void WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei);
+
+ virtual ~PlainHtmlExport();
+};
+
diff --git a/plugins/BasicHistory/src/RichHtmlExport.cpp b/plugins/BasicHistory/src/RichHtmlExport.cpp
new file mode 100644
index 0000000000..5a772a4914
--- /dev/null
+++ b/plugins/BasicHistory/src/RichHtmlExport.cpp
@@ -0,0 +1,545 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "RichHtmlExport.h"
+#include "Options.h"
+#include "resource.h"
+#define EXP_FILE (*stream)
+
+RichHtmlExport::~RichHtmlExport()
+{
+}
+
+extern HINSTANCE hInst;
+extern HANDLE *hEventIcons;
+extern HANDLE hPlusExIcon, hMinusExIcon;
+extern bool g_SmileyAddAvail;
+
+std::wstring MakeTextHtmled(const std::wstring& message, std::queue<std::pair<size_t, size_t> >* positionMap = NULL)
+{
+ std::wstring ret;
+ std::wstring search = _T("&<>\t\r\n");
+ size_t start = 0;
+ size_t find;
+ size_t currentAdd = 0;
+ while((find = message.find_first_of(search, start)) < message.length())
+ {
+ ret += message.substr(start, find - start);
+ switch(message[find])
+ {
+ case _T('&'):
+ ret += _T("&amp;");
+ break;
+ case _T('<'):
+ ret += _T("&lt;");
+ break;
+ case _T('>'):
+ ret += _T("&gt;");
+ break;
+ case _T('\t'):
+ ret += _T(" ");
+ break;
+ case _T('\n'):
+ ret += _T("<br>");
+ break;
+ }
+
+ start = find + 1;
+ if(positionMap != NULL)
+ {
+ size_t len = ret.length() - start - currentAdd;
+ if(len != 0)
+ {
+ positionMap->push(std::pair<size_t, size_t>(start + currentAdd, len));
+ currentAdd += len;
+ }
+ }
+ }
+
+ ret += message.substr(start, message.length() - start);
+ return ret;
+}
+
+std::wstring UrlHighlightHtml(const std::wstring& message, bool& isUrl)
+{
+ std::wstring ret;
+ std::wstring htmlStop = _T("\'\" []<>\r\n");
+ std::wstring search = _T("://");
+ size_t start = 0;
+ size_t find;
+ while((find = message.find(search, start)) < message.length())
+ {
+ size_t urlStart = message.find_last_of(htmlStop, find);
+ size_t urlEnd = message.find_first_of(htmlStop, find + 3);
+ if(urlStart >= message.length())
+ urlStart = -1;
+ if(urlEnd >= message.length())
+ urlEnd = message.length();
+ if(((int)urlEnd -3 - (int)find > 0) && ((int)find - (int)urlStart -1 > 0))
+ {
+ ret += message.substr(start, (urlStart + 1) - start);
+ std::wstring url = message.substr(urlStart + 1, urlEnd - urlStart - 1);
+ start = urlEnd;
+ ret += _T("<a class=url target=_blank href=\"");
+ ret += url + _T("\">") + url + _T("</a>");
+ isUrl = true;
+ }
+ else
+ {
+ ret += message.substr(start, (find + 3) - start);
+ start = find + 3;
+ }
+ }
+
+ ret += message.substr(start, message.length() - start);
+ return ret;
+}
+
+std::wstring RemoveExt(const std::wstring &fileName)
+{
+ size_t find = fileName.find_last_of(L'.');
+ if(find < fileName.length())
+ {
+ return fileName.substr(0, find);
+ }
+
+ return fileName;
+}
+
+std::wstring GetName(const std::wstring &path)
+{
+ size_t find = path.find_last_of(L"\\/");
+ if(find < path.length())
+ {
+ return path.substr(find + 1);
+ }
+
+ return path;
+}
+
+void ExtractFile(short int res, const std::wstring &fileName)
+{
+ HRSRC rSrc = FindResource(hInst, MAKEINTRESOURCE(res), MAKEINTRESOURCE(CUSTOMRES));
+ if(rSrc != NULL)
+ {
+ HGLOBAL res = LoadResource(hInst, rSrc);
+ int size = SizeofResource(hInst, rSrc);
+ if(res != NULL)
+ {
+ char* resData = (char*)LockResource(res);
+ if(resData != NULL)
+ {
+ std::ofstream stream (fileName.c_str(), std::ios_base::binary);
+ if(stream.is_open())
+ {
+ stream.write(resData, size);
+ stream.close();
+ }
+ }
+
+ FreeResource(res);
+ }
+ }
+}
+
+#pragma pack(push, 2)
+typedef struct
+{
+ BYTE bWidth; // Width, in pixels, of the image
+ BYTE bHeight; // Height, in pixels, of the image
+ BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
+ BYTE bReserved; // Reserved ( must be 0)
+ WORD wPlanes; // Color Planes
+ WORD wBitCount; // Bits per pixel
+ DWORD dwBytesInRes; // How many bytes in this resource?
+ DWORD dwImageOffset; // Where in the file is this image?
+} ICONDIRENTRY, *LPICONDIRENTRY;
+
+typedef struct
+{
+ WORD idReserved; // Reserved (must be 0)
+ WORD idType; // Resource Type (1 for icons)
+ WORD idCount; // How many images?
+ //ICONDIRENTRY idEntries; // An entry for each image (idCount of 'em)
+} ICONDIR, *LPICONDIR;
+
+#pragma pack(pop)
+
+typedef struct tagMyBITMAPINFO {
+ BITMAPINFOHEADER bmiHeader;
+ RGBQUAD bmiColors[256];
+} MYBITMAPINFO;
+
+void IcoSave(const std::wstring &fileName, HICON hicon)
+{
+ std::ofstream store (fileName.c_str(), std::ios_base::binary);
+ if(!store.is_open())
+ return;
+ ICONINFO ii;
+ if(!GetIconInfo(hicon,&ii))
+ {
+ store.close();
+ return;
+ }
+
+ HBITMAP hbmMask = ii.hbmMask;
+ HBITMAP hbmColor = ii.hbmColor;
+ BITMAP bmiMask;
+ BITMAP bmiColor;
+ if(
+ GetObject(hbmColor,sizeof(bmiColor),&bmiColor) &&
+ GetObject(hbmMask,sizeof(bmiMask),&bmiMask) &&
+ (bmiColor.bmWidth==bmiMask.bmWidth) &&
+ (bmiColor.bmHeight==bmiMask.bmHeight) &&
+ (bmiMask.bmHeight) > 0 &&
+ (bmiMask.bmWidth) > 0
+ )
+ {
+ BITMAPINFOHEADER icobmi = {0};
+ MYBITMAPINFO info1 = {0};
+ MYBITMAPINFO info2 = {0};
+
+ HDC hDC = CreateCompatibleDC(NULL);
+ info1.bmiHeader.biSize = sizeof(info1.bmiHeader);
+ info1.bmiHeader.biWidth = bmiColor.bmWidth;
+ info1.bmiHeader.biHeight = bmiColor.bmHeight;
+ info1.bmiHeader.biPlanes = 1;
+ info1.bmiHeader.biBitCount = bmiColor.bmBitsPixel;
+ unsigned int size = GetDIBits(hDC,hbmColor,0,info1.bmiHeader.biHeight,NULL,(BITMAPINFO*)&info1,DIB_RGB_COLORS);
+ char* bits1 = new char[info1.bmiHeader.biSizeImage];
+ size = GetDIBits(hDC,hbmColor,0,info1.bmiHeader.biHeight,bits1,(BITMAPINFO*)&info1,DIB_RGB_COLORS);
+ info2.bmiHeader.biSize = sizeof(info2.bmiHeader);
+ info2.bmiHeader.biWidth = bmiMask.bmWidth;
+ info2.bmiHeader.biHeight = bmiMask.bmHeight;
+ info2.bmiHeader.biPlanes = 1;
+ info2.bmiHeader.biBitCount = bmiMask.bmBitsPixel;
+ size = GetDIBits(hDC,hbmColor,0,info1.bmiHeader.biHeight,NULL,(BITMAPINFO*)&info2,DIB_RGB_COLORS);
+ char* bits2 = new char[info2.bmiHeader.biSizeImage];
+ size = GetDIBits(hDC,hbmMask,0,info2.bmiHeader.biHeight,bits2,(BITMAPINFO*)&info2,DIB_RGB_COLORS);
+
+ ICONDIR icodir;
+ ICONDIRENTRY icoent;
+ icodir.idReserved = 0;
+ icodir.idType = 1;
+ icodir.idCount = 1;
+
+ icoent.bWidth = (unsigned char)bmiColor.bmWidth;
+ icoent.bHeight = (unsigned char)bmiColor.bmHeight;
+ icoent.bColorCount = 8<=bmiColor.bmBitsPixel?0:1<<bmiColor.bmBitsPixel;
+ icoent.bReserved = 0;
+ icoent.wPlanes = bmiColor.bmPlanes;
+ icoent.wBitCount = bmiColor.bmBitsPixel;
+ icoent.dwBytesInRes = sizeof(BITMAPINFOHEADER) + info1.bmiHeader.biSizeImage + info2.bmiHeader.biSizeImage;
+
+ icoent.dwImageOffset = sizeof(icodir) + sizeof(icoent);
+
+ store.write((char*)&icodir,sizeof(icodir));
+ store.write((char*)&icoent,sizeof(icoent));
+
+ icobmi.biSize = sizeof(icobmi);
+ icobmi.biWidth = bmiColor.bmWidth;
+ icobmi.biHeight = bmiColor.bmHeight + bmiMask.bmHeight;
+ icobmi.biPlanes = info1.bmiHeader.biPlanes;
+ icobmi.biBitCount = bmiColor.bmBitsPixel;
+ icobmi.biSizeImage = 0;
+
+ store.write((char*)&icobmi,sizeof(icobmi));
+
+ store.write(bits1, info1.bmiHeader.biSizeImage);
+ store.write(bits2, info2.bmiHeader.biSizeImage);
+ DeleteDC(hDC);
+ delete [] bits1;
+ delete [] bits2;
+ }
+
+ store.close();
+ if(ii.hbmColor) DeleteObject(ii.hbmColor);
+ if(ii.hbmMask ) DeleteObject(ii.hbmMask );
+}
+
+bool DeleteDirectory(LPCTSTR lpszDir, bool noRecycleBin = true)
+{
+ size_t len = _tcslen(lpszDir);
+ TCHAR *pszFrom = new TCHAR[len+2];
+ _tcscpy_s(pszFrom, len+2, lpszDir);
+ pszFrom[len] = 0;
+ pszFrom[len+1] = 0;
+
+ SHFILEOPSTRUCT fileop;
+ fileop.hwnd = NULL; // no status display
+ fileop.wFunc = FO_DELETE; // delete operation
+ fileop.pFrom = pszFrom; // source file name as double null terminated string
+ fileop.pTo = NULL; // no destination needed
+ fileop.fFlags = FOF_NOCONFIRMATION|FOF_SILENT; // do not prompt the user
+
+ if(!noRecycleBin)
+ fileop.fFlags |= FOF_ALLOWUNDO;
+
+ fileop.fAnyOperationsAborted = FALSE;
+ fileop.lpszProgressTitle = NULL;
+ fileop.hNameMappings = NULL;
+
+ int ret = SHFileOperation(&fileop);
+ delete [] pszFrom;
+ return (ret == 0);
+}
+
+void RichHtmlExport::WriteHeader(const std::wstring &fileName, const std::wstring &filterName, const std::wstring &myName, const std::wstring &myId, const std::wstring &name1, const std::wstring &proto1, const std::wstring &id1, const std::string& baseProto1, const std::wstring& encoding)
+{
+ baseProto = baseProto1;
+ folder = RemoveExt(fileName) + TranslateT("_files");
+ folderName = GetName(folder);
+ DeleteDirectory(folder.c_str());
+ CreateDirectory(folder.c_str(), NULL);
+ std::wstring css = folder + _T("\\history.css");
+ BOOL cssCopied = FALSE;
+ if(!Options::instance->extCssHtml2.empty())
+ {
+ cssCopied = CopyFile(Options::instance->extCssHtml2.c_str(), css.c_str(), FALSE);
+ }
+
+ if(!cssCopied)
+ ExtractFile(IDR_CSS, css);
+ ExtractFile(IDR_JS, folder + _T("\\history.js"));
+
+ HICON ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hPlusExIcon);
+ IcoSave(folder + _T("\\pnode.ico"), ico);
+ CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0);
+ ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hMinusExIcon);
+ IcoSave(folder + _T("\\mnode.ico"), ico);
+ CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0);
+ ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hEventIcons[0]);
+ IcoSave(folder + _T("\\event0.ico"), ico);
+ CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0);
+ ico = (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)hEventIcons[1]);
+ IcoSave(folder + _T("\\event1.ico"), ico);
+ CallService(MS_SKIN2_RELEASEICON, (LPARAM)ico, 0);
+
+ EXP_FILE << _T("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
+ EXP_FILE << _T("<html><head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=") << encoding << _T("\">\n");
+ EXP_FILE << _T("<title>") << TranslateT("History Log") << _T(" [") << MakeTextHtmled(myName) << _T("] - [") << MakeTextHtmled(name1) << _T("]</title>\n");
+ EXP_FILE << _T("<link rel=\"Stylesheet\" href=\"") << folderName << _T("\\history.css\" type=\"text/css\">\n");
+ EXP_FILE << _T("<script type=\"text/javascript\" src=\"") << folderName << _T("\\history.js\"></script>\n");
+ EXP_FILE << _T("</head><body>\n");
+
+ EXP_FILE << _T("<span id=\"menubar\">\n");
+ EXP_FILE << _T("<a class=mainmenu onmouseover='this.className=\"mainmenusel\";' href=\"javascript:void(0)\" onClick=\"ShowMenu(1)\" onMouseOut='HideMenu();this.className=\"mainmenu\";'>") << TranslateT("Menu") << _T("</a></span>\n");
+ EXP_FILE << _T("<span class=floatmenu id=L1 onmouseover=clearTimeout(timer) onmouseout=HideMenu()>\n");
+ EXP_FILE << _T("<table><tr>\n");
+ EXP_FILE << _T("<td class=menuitemunsel onmouseover='this.className=\"menuitemsel\"' onmouseout='this.className=\"menuitemunsel\"'>\n");
+ EXP_FILE << _T("<a class=menuitem onmouseover=ShowMenu(1) href=\"javascript:void(0)\" onclick=OpenAll(1)>") << TranslateT("Open all") << _T("</a>\n");
+ EXP_FILE << _T("</td></tr><tr>\n");
+ EXP_FILE << _T("<td class=menuitemunsel onmouseover='this.className=\"menuitemsel\"' onmouseout='this.className=\"menuitemunsel\"'>\n");
+ EXP_FILE << _T("<a class=menuitem onmouseover=ShowMenu(1) href=\"javascript:void(0)\" onclick=OpenAll(0)>") << TranslateT("Close all") << _T("</a>\n");
+ EXP_FILE << _T("</td></tr></table></span>\n");
+ EXP_FILE << _T("<script language=\"JavaScript\">\n");
+ EXP_FILE << _T("<!--\n");
+ EXP_FILE << _T("var menu = document.getElementById(\"menubar\");\n");
+ EXP_FILE << _T("if(menu != null)\n");
+ EXP_FILE << _T(" menu.style.visibility = \"visible\";\n");
+ EXP_FILE << _T("// -->\n");
+ EXP_FILE << _T("</script>\n");
+
+ EXP_FILE << _T("<h4>") << TranslateT("History Log") << _T("</h4>\n<h3>");
+ EXP_FILE << MakeTextHtmled(myName);
+ if(proto1.length() || myId.length())
+ {
+ EXP_FILE << _T(" (") << MakeTextHtmled(proto1) << _T(": ") << MakeTextHtmled(myId) << _T(") - ");
+ }
+ else
+ {
+ EXP_FILE << _T(" - ");
+ }
+
+ EXP_FILE << MakeTextHtmled(name1);
+ if(proto1.length() || id1.length())
+ {
+ EXP_FILE << _T(" (") << MakeTextHtmled(proto1) << _T(": ") << MakeTextHtmled(id1) << _T(")</h3>\n");
+ }
+ else
+ {
+ EXP_FILE << _T("</h3>\n");
+ }
+
+ EXP_FILE << _T("<h6>") << TranslateT("Filter:") << _T(" ") << MakeTextHtmled(filterName) << _T("</h6>\n");
+ groupId = 0;
+}
+
+void RichHtmlExport::WriteFooter()
+{
+ if(groupId > 0)
+ {
+ EXP_FILE << _T("</div>\n");
+ }
+
+ EXP_FILE << _T("<div class=mes id=bottom></div>\n</body></html>\n");
+}
+
+void RichHtmlExport::WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText)
+{
+ TCHAR *id = isMe ? _T("out") : _T("inc");
+ TCHAR* ev = (isMe ? _T("1") : _T("0"));
+ if(groupId > 0)
+ {
+ EXP_FILE << _T("</div>\n");
+ }
+
+ bool isUrl = false;
+ std::wstring& mes = ReplaceSmileys(isMe, eventText, isUrl);
+ EXP_FILE << _T("<div class=mes id=session>\n");
+ EXP_FILE << _T("<span class=eventimg id=") << id << _T("><img src=\"") << folderName << _T("\\pnode.ico\" class=sessionimage width=\"16\" height=\"16\" onclick=\"toggleFolder('group") << groupId << _T("', this)\"/>");
+ EXP_FILE << _T("<img src=\"") << folderName << _T("\\event") << ev << _T(".ico\" class=sessionimage width=\"16\" height=\"16\" onclick=\"toggleFolder('group") << groupId << _T("', this)\"/></span>\n");
+ EXP_FILE << _T("<span class=date id=") << id << _T(">") << time << _T("</span>\n<span class=text>\n") << mes;
+ EXP_FILE << _T("</span>\n</div>\n");
+ EXP_FILE << _T("<div class=group id=group") << groupId << _T(">\n");
+ ++groupId;
+}
+
+void RichHtmlExport::WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei)
+{
+ TCHAR *id = isMe ? _T("out") : _T("inc");
+ TCHAR* ev = (isMe ? _T("1") : _T("0"));
+ TCHAR* ev1 = ev;
+ bool isUrl = false;
+ std::wstring& mes = ReplaceSmileys(isMe, message, isUrl);
+ if(isUrl)
+ ev = _T("2");
+ EXP_FILE << _T("<div class=mes id=event") << ev << _T(">\n");
+ EXP_FILE << _T("<div class=eventimg id=") << id << _T(">") << _T("<img src=\"") << folderName << _T("\\event") << ev1 << _T(".ico\" class=sessionimage width=\"16\" height=\"16\"/></div>\n");
+ EXP_FILE << _T("<div class=date id=") << id << _T(">") << (Options::instance->exportHtml2ShowDate ? longDate : shortDate) << _T("</div>\n");
+ EXP_FILE << _T("<div class=nick id=") << id << _T(">") << MakeTextHtmled(user) << _T("</div>\n");
+ EXP_FILE << _T("<div class=text>\n");
+ EXP_FILE << mes;
+ EXP_FILE << _T("\n</div>\n");
+ EXP_FILE << _T("</div>\n");
+}
+
+std::wstring RichHtmlExport::ReplaceSmileys(bool isMe, const std::wstring &msg, bool &isUrl)
+{
+ if(Options::instance->exportHtml2UseSmileys && g_SmileyAddAvail)
+ {
+ TCHAR* msgbuf = new TCHAR[msg.length() + 1];
+ memcpy_s(msgbuf, (msg.length() + 1) * sizeof(TCHAR), msg.c_str(), (msg.length() + 1) * sizeof(TCHAR));
+ SMADD_BATCHPARSE2 sp = {0};
+ SMADD_BATCHPARSERES *spr;
+ sp.cbSize = sizeof(sp);
+ sp.Protocolname = baseProto.length() == 0 ? NULL : baseProto.c_str();
+ sp.str = msgbuf;
+ sp.flag = SAFL_TCHAR | SAFL_PATH | (isMe ? SAFL_OUTGOING : 0);
+ spr = (SMADD_BATCHPARSERES*)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&sp);
+ delete[] msgbuf;
+
+ if (spr == NULL || (INT_PTR)spr == CALLSERVICE_NOTFOUND)
+ {
+ // Did not find a simley
+ return UrlHighlightHtml(MakeTextHtmled(msg), isUrl);
+ }
+
+ std::queue<std::pair<size_t, size_t> > positionMap;
+ std::wstring newMsg = MakeTextHtmled(msg, &positionMap);
+ std::wstring smileyMsg;
+
+ size_t last_pos=0;
+ std::pair<size_t, size_t> pos(0, 0);
+ size_t currentAdd = 0;
+ if(!positionMap.empty())
+ {
+ pos = positionMap.front();
+ positionMap.pop();
+ }
+
+ for (unsigned i = 0; i < sp.numSmileys; ++i)
+ {
+ size_t startChar = spr[i].startChar + currentAdd;
+ while(startChar >= pos.first && pos.second)
+ {
+ startChar += pos.second;
+ currentAdd += pos.second;
+ if(!positionMap.empty())
+ {
+ pos = positionMap.front();
+ positionMap.pop();
+ }
+ else
+ {
+ pos = std::pair<size_t, size_t>(0, 0);
+ }
+ }
+
+ size_t endChar = spr[i].startChar + spr[i].size + currentAdd;
+ while(endChar >= pos.first && pos.second)
+ {
+ endChar += pos.second;
+ currentAdd += pos.second;
+ if(!positionMap.empty())
+ {
+ pos = positionMap.front();
+ positionMap.pop();
+ }
+ else
+ {
+ pos = std::pair<size_t, size_t>(0, 0);
+ }
+ }
+
+ size_t size = endChar - startChar;
+
+ if (spr[i].filepath != NULL) // For deffective smileypacks
+ {
+ // Add text
+ if (startChar - last_pos > 0)
+ {
+ smileyMsg += newMsg.substr(last_pos, startChar - last_pos);
+ }
+
+ std::wstring smileyName = GetName(spr[i].filepath);
+ if(smileys.find(smileyName) == smileys.end())
+ {
+ smileys.insert(smileyName);
+ CopyFile(spr[i].filepath, (folder + _T("\\") + smileyName).c_str(), FALSE);
+ }
+
+ std::wstring smileyText = newMsg.substr(startChar, size);
+ smileyMsg += _T("<img class=smiley src=\"");
+ smileyMsg += folderName;
+ smileyMsg += _T("\\");
+ smileyMsg += smileyName;
+ smileyMsg += _T("\" alt=\"");
+ smileyMsg += smileyText;
+ smileyMsg += _T("\"/>");
+ }
+
+ // Get next
+ last_pos = endChar;
+ }
+
+ // Add rest of text
+ if (last_pos < newMsg.length())
+ {
+ smileyMsg += newMsg.substr(last_pos);
+ }
+
+ CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)spr);
+ return UrlHighlightHtml(smileyMsg, isUrl);
+ }
+ else
+ {
+ return UrlHighlightHtml(MakeTextHtmled(msg), isUrl);
+ }
+}
diff --git a/plugins/BasicHistory/src/RichHtmlExport.h b/plugins/BasicHistory/src/RichHtmlExport.h
new file mode 100644
index 0000000000..f4f6686964
--- /dev/null
+++ b/plugins/BasicHistory/src/RichHtmlExport.h
@@ -0,0 +1,44 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+#include "IExport.h"
+class RichHtmlExport :
+ public IExport
+{
+private:
+ int groupId;
+ std::wstring folder;
+ std::wstring folderName;
+ std::string baseProto;
+ stdext::hash_set<std::wstring> smileys;
+ std::wstring ReplaceSmileys(bool isMe, const std::wstring &msg, bool &isUrl);
+public:
+ virtual const TCHAR* GetExt()
+ {
+ return _T("html");
+ }
+
+ virtual void WriteHeader(const std::wstring &fileName, const std::wstring &filterName, const std::wstring &myName, const std::wstring &myId, const std::wstring &name1, const std::wstring &proto1, const std::wstring &id1, const std::string& baseProto1, const std::wstring& encoding);
+ virtual void WriteFooter();
+ virtual void WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText);
+ virtual void WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei);
+
+ virtual ~RichHtmlExport();
+};
+
diff --git a/plugins/BasicHistory/src/Scheduler.cpp b/plugins/BasicHistory/src/Scheduler.cpp
new file mode 100644
index 0000000000..3b2ba18ab9
--- /dev/null
+++ b/plugins/BasicHistory/src/Scheduler.cpp
@@ -0,0 +1,1576 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "Options.h"
+#include "ExportManager.h"
+#include "HistoryWindow.h"
+#include "zip\zip.h"
+#include "zip\unzip.h"
+#include "zip\iowin32.h"
+
+// Sorry for plain C implementation
+#define MODULE "BasicHistory"
+extern HANDLE g_hMainThread;
+bool bPopupsEnabled;
+bool DoTask(TaskOptions& to);
+bool IsValidTask(TaskOptions& to, std::list<TaskOptions>* top = NULL, std::wstring* err = NULL, std::wstring* errDescr = NULL);
+std::wstring GetFileName(const std::wstring &baseName, std::wstring contactName, std::map<std::wstring, bool>& existingContacts, bool replaceContact);
+std::wstring GetDirectoryName(const std::wstring &path);
+std::wstring GetName(const std::wstring &path);
+bool DeleteDirectory(LPCTSTR lpszDir, bool noRecycleBin = true);
+void ListDirectory(const std::wstring &basePath, const std::wstring &path, std::list<std::wstring>& files);
+std::wstring ReplaceStr(const std::wstring& str, wchar_t oldCh, wchar_t newCh);
+time_t GetNextExportTime(TaskOptions& to);
+void SchedulerThreadFunc(void*);
+volatile bool finishThread = false;
+bool initTask = false;
+HANDLE thread = NULL;
+HANDLE threadEvent;
+time_t nextExportTime;
+void StartThread(bool init);
+void StopThread();
+bool GetNextExportTime(bool init, time_t now);
+bool ExecuteCurrentTask(time_t now);
+void GetZipFileTime(const TCHAR *file, uLong *dt);
+std::wstring ReplaceExt(const std::wstring& file, const TCHAR* ext);
+bool ZipFiles(const std::wstring& dir, std::wstring zipFilePath, const std::string& password);
+bool UnzipFiles(const std::wstring& dir, std::wstring& zipFilePath, const std::string& password);
+bool FtpFiles(const std::wstring& dir, const std::wstring& filePath, const std::wstring& ftpName);
+bool FtpGetFiles(const std::wstring& dir, const std::list<std::wstring>& files, const std::wstring& ftpName);
+void CreatePath(const TCHAR *szDir);
+void DoError(const TaskOptions& to, const std::wstring error);
+
+void OptionsSchedulerChanged()
+{
+ StartThread(false);
+}
+
+void InitScheduler()
+{
+ bPopupsEnabled = ServiceExists(MS_POPUP_ADDPOPUPT) || ServiceExists(MS_POPUP_ADDPOPUPCLASS);
+ if (ServiceExists(MS_POPUP_REGISTERCLASS))
+ {
+ //hPopupIcon = LoadIconEx(I_CHKUPD);
+ POPUPCLASS test = {0};
+ test.cbSize = sizeof(POPUPCLASS);
+ test.flags = PCF_TCHAR;
+ test.hIcon = LoadSkinnedIcon(SKINICON_OTHER_HISTORY);
+ test.iSeconds = 10;
+ test.ptszDescription = TranslateT("History task");
+ test.pszName = MODULE;
+ CallService(MS_POPUP_REGISTERCLASS, 0, (WPARAM)&test);
+ }
+
+ StartThread(true);
+}
+
+void DeinitScheduler()
+{
+ StopThread();
+}
+
+int DoLastTask(WPARAM, LPARAM)
+{
+ for(std::vector<TaskOptions>::iterator it = Options::instance->taskOptions.begin(); it != Options::instance->taskOptions.end(); ++it)
+ {
+ if(it->trigerType == TaskOptions::AtEnd && it->active)
+ {
+ DoTask(*it);
+ }
+ }
+
+ return 0;
+}
+
+bool IsValidTask(TaskOptions& to, std::list<TaskOptions>* top, std::wstring* err, std::wstring* errDescr)
+{
+ if(to.taskName.empty())
+ {
+ if(err != NULL)
+ *err = TranslateT("Name");
+ return false;
+ }
+ if(top != NULL)
+ {
+ for(std::list<TaskOptions>::iterator it = top->begin(); it != top->end(); ++it)
+ {
+ if(it->taskName == to.taskName)
+ {
+ if(err != NULL)
+ *err = TranslateT("Name");
+ return false;
+ }
+ }
+ }
+ if(!to.isSystem && to.contacts.size() == 0)
+ {
+ if(err != NULL)
+ *err = TranslateT("Contacts");
+ if(errDescr != NULL)
+ *errDescr = TranslateT("At least one contact should be selected.");
+ return false;
+ }
+
+ bool isImportTask = to.type == TaskOptions::Import || to.type == TaskOptions::ImportAndMarge;
+ if(!isImportTask)
+ {
+ if(to.filterId > 1)
+ {
+ int filter = 0;
+
+ for(int i = 0; i < (int)Options::instance->customFilters.size(); ++i)
+ {
+ if(to.filterName == Options::instance->customFilters[i].name)
+ {
+ filter = i + 2;
+ break;
+ }
+ }
+
+ if(filter < 2)
+ {
+ if(err != NULL)
+ *err = TranslateT("Filter");
+ return false;
+ }
+
+ to.filterId = filter;
+ }
+ else if(to.filterId < 0)
+ {
+ if(err != NULL)
+ *err = TranslateT("Filter");
+ return false;
+ }
+ }
+
+ if(to.type == TaskOptions::Delete)
+ {
+ return true;
+ }
+
+ if(!Options::FTPAvail() && to.useFtp)
+ {
+ if(err != NULL)
+ *err = TranslateT("Upload to FTP");
+ return false;
+ }
+ if(to.filePath.empty())
+ {
+ if(err != NULL)
+ *err = TranslateT("Path to output file");
+ return false;
+ }
+ if(to.useFtp && to.ftpName.empty())
+ {
+ if(err != NULL)
+ *err = TranslateT("Session name");
+ if(errDescr != NULL)
+ *errDescr = TranslateT("To create session open WinSCP, click New Session, enter data and save with specific name. Remember if FTP server using password you should save it in WinSCP.");
+ return false;
+ }
+ if(to.useFtp && (to.filePath.find(_T('\\')) < to.filePath.length() || to.filePath.find(_T(':')) < to.filePath.length() || to.filePath[0] != L'/'))
+ {
+ if(err != NULL)
+ *err = TranslateT("Path to file");
+ if(errDescr != NULL)
+ *errDescr = TranslateT("FTP path must contains '/' instead '\\' and starts from '/'.");
+ return false;
+ }
+ if(isImportTask && to.filePath.find(_T("<date>")) < to.filePath.length())
+ {
+ if(err != NULL)
+ *err = TranslateT("Path to file");
+ if(errDescr != NULL)
+ *errDescr = TranslateT("FTP path cannot contain <date> in import task.");
+ return false;
+ }
+ if(!isImportTask && (to.exportType < IExport::RichHtml || to.exportType > IExport::Dat))
+ {
+ if(err != NULL)
+ *err = TranslateT("Export to");
+ return false;
+ }
+ if(isImportTask && (to.importType < IImport::Binary || to.importType > IImport::Dat))
+ {
+ if(err != NULL)
+ *err = TranslateT("Import from");
+ return false;
+ }
+ if((to.trigerType == TaskOptions::Daily || to.trigerType == TaskOptions::Weekly || to.trigerType == TaskOptions::Monthly) && (to.dayTime < 0 || to.dayTime >= 24 * 60))
+ {
+ if(err != NULL)
+ *err = TranslateT("Time");
+ return false;
+ }
+ if(to.trigerType == TaskOptions::Weekly && (to.dayOfWeek < 0 || to.dayOfWeek >= 7))
+ {
+ if(err != NULL)
+ *err = TranslateT("Day of week");
+ return false;
+ }
+ if(to.trigerType == TaskOptions::Monthly && (to.dayOfMonth <= 0 || to.dayOfMonth >= 32))
+ {
+ if(err != NULL)
+ *err = TranslateT("Day");
+ return false;
+ }
+ if((to.trigerType == TaskOptions::DeltaMin || to.trigerType == TaskOptions::DeltaHour) && (to.deltaTime < 0 || to.deltaTime >= 10000))
+ {
+ if(err != NULL)
+ *err = TranslateT("Delta time");
+ return false;
+ }
+
+ return true;
+}
+
+static void CALLBACK DoRebuildEventsInMainAPCFunc(ULONG_PTR dwParam)
+{
+ HANDLE* contacts = (HANDLE*) dwParam;
+ size_t size = (size_t)contacts[0];
+ for(size_t i = 1; i <= size; ++i)
+ {
+ HistoryWindow::RebuildEvents(contacts[i]);
+ }
+
+ delete[] contacts;
+}
+
+bool DoTask(TaskOptions& to)
+{
+ std::wstring err;
+ std::wstring errDescr;
+ if(!IsValidTask(to, NULL, &err, &errDescr))
+ {
+ TCHAR msg[256];
+ if(err.empty())
+ _tcscpy_s(msg, TranslateT("Some value is invalid"));
+ else if(errDescr.empty())
+ {
+ _stprintf_s(msg, TranslateT("Invalid '%s' value."), err.c_str());
+ }
+ else
+ {
+ _stprintf_s(msg, TranslateT("Invalid '%s' value.\n%s"), err.c_str(), errDescr.c_str());
+ }
+ DoError(to, msg);
+ return true;
+ }
+
+ DWORD now = time(NULL);
+ long long int t = to.eventDeltaTime * 60;
+ if(to.eventUnit > TaskOptions::Minute)
+ t *= 60LL;
+ if(to.eventUnit > TaskOptions::Hour)
+ t *= 24LL;
+ if(t > 2147483647LL)
+ {
+ DoError(to, TranslateT("Unknown error"));
+ return true;
+ }
+
+ bool error = false;
+ std::wstring errorStr;
+ std::list<ExportManager*> managers;
+ if(to.type == TaskOptions::Delete)
+ {
+ if(to.isSystem)
+ {
+ ExportManager *exp = new ExportManager(NULL, NULL, to.filterId);
+ exp->SetDeleteWithoutExportEvents(t, now);
+ managers.push_back(exp);
+ }
+
+ for(size_t i = 0; i < to.contacts.size(); ++i)
+ {
+ ExportManager *exp = new ExportManager(NULL, to.contacts[i], to.filterId);
+ exp->SetDeleteWithoutExportEvents(t, now);
+ managers.push_back(exp);
+ }
+ }
+ else if(to.type == TaskOptions::Import || to.type == TaskOptions::ImportAndMarge)
+ {
+ std::map<std::wstring, bool> existingContacts1;
+ ExportManager mExp = ExportManager(NULL, NULL, 1);
+ std::wstring filePath = to.filePath;
+ std::wstring dir;
+ std::list<std::wstring> files;
+ std::vector<HANDLE> contacts;
+ if(to.useFtp || to.compress)
+ {
+ std::map<std::wstring, bool> existingContacts;
+ TCHAR temp[MAX_PATH];
+ temp[0] = 0;
+ GetTempPath(MAX_PATH, temp);
+ dir = temp;
+ dir += GetName(filePath);
+ dir = GetFileName(dir, L"", existingContacts, true);
+ dir = ReplaceExt(dir, L"");
+ size_t pos = dir.find_last_of(_T('.'));
+ if(pos < dir.length())
+ {
+ dir = dir.substr(0, pos);
+ }
+
+ DeleteDirectory(dir.c_str());
+ CreateDirectory(dir.c_str(), NULL);
+ }
+
+ const TCHAR* ext = ExportManager::GetExt(to.importType);
+ if(to.isSystem)
+ {
+ std::wstring n = GetFileName(filePath, mExp.GetContactName(), existingContacts1, true);
+ n = ReplaceExt(n, ext);
+ files.push_back(n);
+ contacts.push_back(NULL);
+ }
+
+ for(size_t i = 0; i < to.contacts.size(); ++i)
+ {
+ mExp.hContact = to.contacts[i];
+ std::wstring n = GetFileName(filePath, mExp.GetContactName(), existingContacts1, true);
+ n = ReplaceExt(n, ext);
+ files.push_back(n);
+ contacts.push_back(to.contacts[i]);
+ }
+
+ if(to.useFtp)
+ {
+ if(to.compress)
+ {
+ std::map<std::wstring, bool> existingContacts;
+ std::wstring n = GetFileName(filePath, L"", existingContacts, true);
+ n = ReplaceExt(n, L"zip");
+ files.clear();
+ files.push_back(n);
+ filePath = dir + L"\\" + GetName(filePath);
+ }
+
+ error = FtpGetFiles(dir, files, to.ftpName);
+ if(error)
+ {
+ if(!errorStr.empty())
+ {
+ errorStr += L"\n";
+ }
+
+ errorStr += TranslateT("Cannot get FTP file(s).");
+ }
+ }
+
+ if(!error && to.compress)
+ {
+ error = UnzipFiles(dir, filePath, to.zipPassword);
+ if(error)
+ {
+ if(!errorStr.empty())
+ {
+ errorStr += L"\n";
+ }
+
+ errorStr += TranslateT("Cannot unzip file(s).");
+ }
+
+ if(to.useFtp)
+ DeleteFile(filePath.c_str());
+ }
+
+ if(!error && (to.useFtp || to.compress))
+ {
+ files.clear();
+ std::list<std::wstring> files1;
+ ListDirectory(dir, L"\\", files1);
+ for(std::list<std::wstring>::iterator it = files1.begin(); it != files1.end(); ++it)
+ {
+ files.push_back(dir + *it);
+ }
+ }
+
+ if(!error)
+ {
+ std::list<HANDLE> contactList;
+ for(std::list<std::wstring>::iterator it = files.begin(); it != files.end(); ++it)
+ {
+ mExp.SetAutoImport(*it);
+ int ret = mExp.Import(to.importType, contacts);
+ if(ret == -3)
+ {
+ if(contacts.size() == 1)
+ {
+ ret = 0;
+ }
+ else
+ {
+ std::map<std::wstring, bool> existingContacts;
+ std::wstring name = GetName(*it);
+ for(ret = 0; ret < (int)contacts.size(); ++ret)
+ {
+ mExp.hContact = contacts[ret];
+ std::wstring n = GetFileName(to.filePath, mExp.GetContactName(), existingContacts, true);
+ n = ReplaceExt(n, ext);
+ n = GetName(n);
+ if(n == name)
+ break;
+ }
+
+ if(ret >= (int)contacts.size())
+ ret = -1;
+ }
+ }
+
+ if(ret >= 0)
+ {
+ mExp.hContact = contacts[ret];
+ if(to.type == TaskOptions::Import)
+ {
+ EventList::AddImporter(mExp.hContact, to.importType, *it);
+ contactList.push_back(mExp.hContact);
+ }
+ else
+ {
+ std::vector<IImport::ExternalMessage> messages;
+ if(mExp.Import(to.importType, messages, NULL))
+ {
+ mExp.MargeMessages(messages);
+ contactList.push_back(mExp.hContact);
+ }
+ }
+ }
+ else if(ret != -1)
+ {
+ if(!errorStr.empty())
+ {
+ errorStr += L"\n";
+ }
+
+ TCHAR msg[1024];
+
+ _stprintf_s(msg, TranslateT("Incorrect file format: %s."), GetName(*it).c_str());
+ errorStr += msg;
+ }
+ else
+ {
+ if(!errorStr.empty())
+ {
+ errorStr += L"\n";
+ }
+
+ TCHAR msg[1024];
+
+ _stprintf_s(msg, TranslateT("Unknown contact in file: %s."), GetName(*it).c_str());
+ errorStr += msg;
+ }
+ }
+
+ if(contactList.size() > 0)
+ {
+ HANDLE* contacts = new HANDLE[contactList.size() + 1];
+ contacts[0] = (HANDLE)contactList.size();
+ int i = 1;
+ for(std::list<HANDLE>::iterator it = contactList.begin(); it != contactList.end(); ++it)
+ {
+ contacts[i++] = *it;
+ }
+
+ QueueUserAPC(DoRebuildEventsInMainAPCFunc, g_hMainThread, (ULONG_PTR) contacts);
+ }
+ }
+
+ if(to.useFtp || to.compress)
+ {
+ DeleteDirectory(dir.c_str());
+ }
+ }
+ else
+ {
+ std::map<std::wstring, bool> existingContacts;
+ std::wstring filePath = to.filePath;
+ std::wstring dir;
+ if(!to.useFtp && !to.compress)
+ {
+ dir = GetDirectoryName(filePath);
+ if(!dir.empty())
+ {
+ CreateDirectory(dir.c_str(), NULL);
+ }
+ }
+ else
+ {
+ filePath = GetName(filePath);
+ TCHAR temp[MAX_PATH];
+ temp[0] = 0;
+ GetTempPath(MAX_PATH, temp);
+ dir = temp;
+ dir += filePath;
+ dir = GetFileName(dir, L"", existingContacts, true);
+ dir = ReplaceExt(dir, L"");
+ size_t pos = dir.find_last_of(_T('.'));
+ if(pos < dir.length())
+ {
+ dir = dir.substr(0, pos);
+ }
+
+ DeleteDirectory(dir.c_str());
+ CreateDirectory(dir.c_str(), NULL);
+ filePath = dir + L"\\" + filePath;
+ }
+ if(to.isSystem)
+ {
+ ExportManager *exp = new ExportManager(NULL, NULL, to.filterId);
+ exp->SetAutoExport(GetFileName(filePath, exp->GetContactName(), existingContacts, true), t, now);
+ exp->useImportedMessages = to.exportImported;
+ if(!exp->Export(to.exportType))
+ {
+ error = true;
+ if(!errorStr.empty())
+ {
+ errorStr += L"\n";
+ }
+
+ TCHAR msg[1024];
+
+ _stprintf_s(msg, TranslateT("Cannot export history for contact: %s."), exp->GetContactName().c_str());
+ errorStr += msg;
+ }
+
+ if(to.type == TaskOptions::Export)
+ {
+ delete exp;
+ }
+ else
+ {
+ managers.push_back(exp);
+ }
+ }
+
+ if(!error)
+ {
+ for(size_t i = 0; i < to.contacts.size(); ++i)
+ {
+ ExportManager *exp = new ExportManager(NULL, to.contacts[i], to.filterId);
+ exp->SetAutoExport(GetFileName(filePath, exp->GetContactName(), existingContacts, true), t, now);
+ exp->useImportedMessages = to.exportImported;
+ if(!exp->Export(to.exportType))
+ {
+ error = true;
+ if(!errorStr.empty())
+ {
+ errorStr += L"\n";
+ }
+
+ TCHAR msg[1024];
+
+ _stprintf_s(msg, TranslateT("Cannot export history for contact: %s."), exp->GetContactName().c_str());
+ errorStr += msg;
+ break;
+ }
+
+ if(to.type == TaskOptions::Export)
+ {
+ delete exp;
+ }
+ else
+ {
+ managers.push_back(exp);
+ }
+ }
+ }
+
+ if(error)
+ {
+ if(to.compress && !to.useFtp)
+ {
+ DeleteDirectory(dir.c_str());
+ }
+ }
+ else if(to.compress)
+ {
+ std::wstring zipFilePath = to.filePath;
+ std::wstring zipDir = dir;
+ if(!to.useFtp)
+ {
+ zipDir = GetDirectoryName(zipFilePath);
+ if(!zipDir.empty())
+ {
+ CreateDirectory(zipDir.c_str(), NULL);
+ }
+ }
+ else
+ {
+ zipFilePath = GetName(zipFilePath);
+ TCHAR temp[MAX_PATH];
+ temp[0] = 0;
+ GetTempPath(MAX_PATH, temp);
+ zipDir = temp;
+ zipDir += L"zip<date>";
+ zipDir = GetFileName(zipDir, L"", existingContacts, true);
+ DeleteDirectory(zipDir.c_str());
+ CreateDirectory(zipDir.c_str(), NULL);
+ zipFilePath = zipDir + L"\\" + zipFilePath;
+ }
+ error = ZipFiles(dir + L"\\", zipFilePath, to.zipPassword);
+ dir = zipDir;
+ if(error)
+ {
+ if(!errorStr.empty())
+ {
+ errorStr += L"\n";
+ }
+
+ errorStr += TranslateT("Cannot compress file(s).");
+ }
+ }
+
+ if(to.useFtp)
+ {
+ if(!error)
+ {
+ error = FtpFiles(dir, to.filePath, to.ftpName);
+ if(error)
+ {
+ if(!errorStr.empty())
+ {
+ errorStr += L"\n";
+ }
+
+ errorStr += TranslateT("Cannot send FTP file(s).");
+ }
+ }
+
+ DeleteDirectory(dir.c_str());
+ }
+ }
+
+ if(to.type == TaskOptions::Delete || to.type == TaskOptions::ExportAndDelete)
+ {
+ for(std::list<ExportManager*>::iterator it = managers.begin(); it != managers.end(); ++it)
+ {
+ if(!error)
+ {
+ (*it)->DeleteExportedEvents();
+ }
+
+ delete *it;
+ }
+ }
+
+ if(error)
+ {
+ DoError(to, errorStr.empty() ? TranslateT("Unknown error") : errorStr);
+ }
+
+ return error;
+}
+
+std::wstring GetFileName(const std::wstring &baseName, std::wstring contactName, std::map<std::wstring, bool>& existingContacts, bool replaceContact)
+{
+ std::wstring str = baseName;
+ size_t pos = baseName.find(_T("<contact>"));
+ if(replaceContact && pos < baseName.length())
+ {
+ str = baseName.substr(0, pos);
+ std::wstring baseName1 = contactName;
+ if(!baseName1.empty())
+ {
+ std::wstring name = baseName1;
+ int i = 0;
+ TCHAR buf[32];
+ std::map<std::wstring, bool>::iterator it = existingContacts.find(name);
+ while(it != existingContacts.end())
+ {
+ _itot_s(++i, buf, 10);
+ name = baseName1 + buf;
+ it = existingContacts.find(name);
+ }
+
+ str += name;
+ existingContacts[name] = true;
+ }
+ str += baseName.substr(pos + 9);
+ }
+
+ pos = str.find(_T("<date>"));
+ if(pos < str.length())
+ {
+ TCHAR time[256];
+ SYSTEMTIME st;
+ GetLocalTime(&st);
+ _stprintf_s(time, _T("%d-%02d-%02d %02d%02d"), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute);
+ std::wstring str1 = str.substr(0, pos);
+ str1 += time;
+ str1 += str.substr(pos + 6);
+ str = str1;
+ }
+
+ return str;
+}
+
+std::wstring GetDirectoryName(const std::wstring &path)
+{
+ size_t find = path.find_last_of(L"\\/");
+ if(find < path.length())
+ {
+ return path.substr(0, find);
+ }
+
+ return L"";
+}
+
+void ListDirectory(const std::wstring &basePath, const std::wstring &path, std::list<std::wstring>& files)
+{
+ WIN32_FIND_DATA findFileData;
+ HANDLE hFind = FindFirstFile((basePath + path + _T("*")).c_str(), &findFileData);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return;
+ do
+ {
+ if(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ std::wstring name = findFileData.cFileName;
+ if(name != L"." && name != L"..")
+ ListDirectory(basePath, path + findFileData.cFileName + _T("\\"), files);
+ }
+ else
+ {
+ files.push_back(path + findFileData.cFileName);
+ }
+ }
+ while(FindNextFile(hFind, &findFileData));
+ FindClose(hFind);
+}
+
+std::wstring ReplaceStr(const std::wstring& str, wchar_t oldCh, wchar_t newCh)
+{
+ std::wstring ret;
+ size_t start = 0;
+ size_t find;
+ while((find = str.find_first_of(oldCh, start)) < str.length())
+ {
+ ret += str.substr(start, find - start);
+ ret += newCh;
+ start = find + 1;
+ }
+
+ ret += str.substr(start, str.length() - start);
+ return ret;
+}
+
+time_t GetNextExportTime(TaskOptions& to)
+{
+ switch(to.trigerType)
+ {
+ case TaskOptions::Daily:
+ {
+ tm t;
+ localtime_s(&t, &to.lastExport);
+ t.tm_hour = to.dayTime/60;
+ t.tm_min = to.dayTime%60;
+ t.tm_sec = 0;
+ time_t newTime = mktime(&t);
+ if(newTime <= to.lastExport)
+ {
+ newTime += 60 * 60 * 24;
+ }
+
+ return newTime;
+ }
+ case TaskOptions::Weekly:
+ {
+ tm t;
+ localtime_s(&t, &to.lastExport);
+ t.tm_hour = to.dayTime/60;
+ t.tm_min = to.dayTime%60;
+ t.tm_sec = 0;
+ int dow = (to.dayOfWeek + 1) % 7;
+ time_t newTime = mktime(&t);
+ while(dow != t.tm_wday)
+ {
+ newTime += 60 * 60 * 24;
+ localtime_s(&t, &newTime);
+ newTime = mktime(&t);
+ }
+
+ if(newTime <= to.lastExport)
+ {
+ newTime += 7 * 60 * 60 * 24;
+ }
+
+ return newTime;
+ }
+ case TaskOptions::Monthly:
+ {
+ tm t;
+ localtime_s(&t, &to.lastExport);
+ t.tm_hour = to.dayTime/60;
+ t.tm_min = to.dayTime%60;
+ t.tm_sec = 0;
+ time_t newTime = mktime(&t);
+ int lastM = t.tm_mon;
+ int lastD;
+ while(to.dayOfMonth != t.tm_mday || newTime <= to.lastExport)
+ {
+ lastD = t.tm_mday;
+ newTime += 60 * 60 * 24;
+ localtime_s(&t, &newTime);
+ newTime = mktime(&t);
+ if(to.dayOfMonth > 28 && t.tm_mon != lastM && (newTime - 60 * 60 * 24) > to.lastExport)
+ {
+ lastM = t.tm_mon;
+ if(to.dayOfMonth > lastD)
+ {
+ newTime -= 60 * 60 * 24;
+ break;
+ }
+ }
+ }
+
+ return newTime;
+ }
+ case TaskOptions::DeltaMin:
+ return to.lastExport + to.deltaTime * 60;
+ case TaskOptions::DeltaHour:
+ return to.lastExport + to.deltaTime * 60 * 60;
+ default:
+ return to.lastExport;
+ }
+}
+
+void SchedulerThreadFunc(void*)
+{
+ if(initTask)
+ {
+ WaitForSingleObject(threadEvent, 5 * 1000);
+ initTask = false;
+ }
+
+ while(!finishThread)
+ {
+ DWORD timeWait;
+ time_t now = time(NULL);
+ while(nextExportTime <= now)
+ {
+ if(!ExecuteCurrentTask(now))
+ return;
+ }
+
+ time_t dif = nextExportTime - now;
+ timeWait = (dif > 60 * 60 * 24) ? (60 * 60 * 1000) : (60 * 1000);
+
+ WaitForSingleObject(threadEvent, timeWait);
+ }
+}
+
+void StartThread(bool init)
+{
+ StopThread();
+
+ initTask = false;
+ bool isExport = GetNextExportTime(init, time(NULL));
+ if(isExport)
+ {
+ finishThread = false;
+ threadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ thread = mir_forkthread(SchedulerThreadFunc, NULL);
+ }
+}
+
+void StopThread()
+{
+ if(thread != NULL)
+ {
+ finishThread = true;
+ SetEvent(threadEvent);
+ WaitForSingleObject(thread, INFINITE);
+ //CloseHandle(thread);
+ CloseHandle(threadEvent);
+ thread = NULL;
+ threadEvent = NULL;
+ }
+}
+
+bool GetNextExportTime(bool init, time_t now)
+{
+ EnterCriticalSection(&Options::instance->criticalSection);
+ bool isExport = false;
+ for(std::vector<TaskOptions>::iterator it = Options::instance->taskOptions.begin(); it != Options::instance->taskOptions.end(); ++it)
+ {
+ if(it->forceExecute)
+ {
+ nextExportTime = now;
+ isExport = true;
+ initTask = init;
+ break;
+ }
+ else if(it->active && it->trigerType != TaskOptions::AtStart && it->trigerType != TaskOptions::AtEnd)
+ {
+ time_t t = GetNextExportTime(*it);
+ if(isExport)
+ {
+ if(t < nextExportTime)
+ nextExportTime = t;
+ }
+ else
+ {
+ nextExportTime = t;
+ isExport = true;
+ initTask = init;
+ }
+ }
+ else if(it->active && it->trigerType == TaskOptions::AtStart && init)
+ {
+ it->forceExecute = true;
+ it->showMBAfterExecute = false;
+ nextExportTime = now;
+ isExport = true;
+ initTask = true;
+ }
+ }
+
+ LeaveCriticalSection(&Options::instance->criticalSection);
+ return isExport;
+}
+
+static void CALLBACK DoTaskFinishInMainAPCFunc(ULONG_PTR dwParam)
+{
+ TCHAR *item = (TCHAR*) dwParam;
+ MessageBox(NULL, item, TranslateT("Task finished"), MB_OK | MB_ICONINFORMATION);
+ delete[] item;
+}
+
+bool ExecuteCurrentTask(time_t now)
+{
+ EnterCriticalSection(&Options::instance->criticalSection);
+ TaskOptions to;
+ bool isExport = false;
+ for(std::vector<TaskOptions>::iterator it = Options::instance->taskOptions.begin(); it != Options::instance->taskOptions.end(); ++it)
+ {
+ if(it->forceExecute)
+ {
+ it->lastExport = time(NULL);
+ Options::instance->SaveTaskTime(*it);
+ to = *it;
+ isExport = true;
+ break;
+ }
+ else if(it->active && it->trigerType != TaskOptions::AtStart && it->trigerType != TaskOptions::AtEnd)
+ {
+ time_t t = GetNextExportTime(*it);
+ if(t <= now)
+ {
+ it->lastExport = time(NULL);
+ Options::instance->SaveTaskTime(*it);
+ to = *it;
+ isExport = true;
+ break;
+ }
+ }
+ }
+
+ LeaveCriticalSection(&Options::instance->criticalSection);
+
+ if(isExport)
+ {
+ bool error = DoTask(to);
+ if(to.forceExecute)
+ {
+ EnterCriticalSection(&Options::instance->criticalSection);
+ for(std::vector<TaskOptions>::iterator it = Options::instance->taskOptions.begin(); it != Options::instance->taskOptions.end(); ++it)
+ {
+ if(it->taskName == to.taskName)
+ {
+ it->forceExecute = false;
+ it->showMBAfterExecute = false;
+ break;
+ }
+ }
+
+ LeaveCriticalSection(&Options::instance->criticalSection);
+
+ if(to.showMBAfterExecute)
+ {
+ size_t size = to.taskName.size() + 1024;
+ TCHAR* name = new TCHAR[size];
+ if(error)
+ {
+ _stprintf_s(name, size, TranslateT("Task '%s' execution failed"), to.taskName.c_str());
+ }
+ else
+ {
+ _stprintf_s(name, size, TranslateT("Task '%s' finished successfully"), to.taskName.c_str());
+ }
+
+ QueueUserAPC(DoTaskFinishInMainAPCFunc, g_hMainThread, (ULONG_PTR) name);
+ }
+ }
+ }
+
+ return GetNextExportTime(false, now);
+}
+
+void GetZipFileTime(const TCHAR *file, uLong *dt)
+{
+ FILETIME ftLocal;
+ HANDLE hFind;
+ WIN32_FIND_DATA ff32;
+
+ hFind = FindFirstFile(file, &ff32);
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal);
+ FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0);
+ FindClose(hFind);
+ }
+}
+
+/* calculate the CRC32 of a file,
+ because to encrypt a file, we need known the CRC32 of the file before */
+bool GetFileCrc(const TCHAR* filenameinzip, unsigned char* buf, unsigned long size_buf, unsigned long* result_crc)
+{
+ unsigned long calculate_crc = 0;
+ bool error = true;
+ HANDLE hFile = CreateFile(filenameinzip, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ if(hFile != INVALID_HANDLE_VALUE)
+ {
+ DWORD readed;
+ do
+ {
+ if(!ReadFile(hFile, buf, 1024, &readed, NULL))
+ {
+ error = false;
+ break;
+ }
+
+ if (readed > 0)
+ {
+ calculate_crc = crc32(calculate_crc, buf, readed);
+ }
+ }
+ while (readed > 0);
+ CloseHandle(hFile);
+ }
+ else
+ {
+ error = false;
+ }
+
+ *result_crc=calculate_crc;
+ return error;
+}
+
+bool ZipFiles(const std::wstring& dir, std::wstring zipFilePath, const std::string& password)
+{
+ std::list<std::wstring> files;
+ std::map<std::wstring, bool> existingContacts;
+ ListDirectory(dir, L"", files);
+ bool error = false;
+ if(files.size() > 0)
+ {
+ zlib_filefunc_def pzlib_filefunc_def;
+ fill_win32_filefunc(&pzlib_filefunc_def);
+ zipFilePath = GetFileName(zipFilePath, L"", existingContacts, true);
+ zipFilePath = ReplaceExt(zipFilePath, L"zip");
+ zipFile zf = zipOpen2((LPCSTR)(LPTSTR)zipFilePath.c_str(), APPEND_STATUS_CREATE, NULL, &pzlib_filefunc_def);
+ if (zf != NULL)
+ {
+ unsigned char buf[1024];
+ char bufF[MAX_PATH + 20];
+ while(files.size() > 0)
+ {
+ std::wstring zipDir = *files.begin();
+ std::wstring localDir = dir + L"\\" + zipDir;
+ zip_fileinfo zi = {0};
+ GetZipFileTime(localDir.c_str(), &zi.dosDate);
+ if(zipDir.size() > MAX_PATH + 19)
+ {
+ error = true;
+ break;
+ }
+
+ BOOL badChar;
+ WideCharToMultiByte(CP_OEMCP, WC_NO_BEST_FIT_CHARS, zipDir.c_str(), -1, bufF, MAX_PATH + 20, NULL, &badChar);
+ int flag = 0;
+ if(badChar)
+ {
+ flag = 0x800; // UTF
+ WideCharToMultiByte(CP_UTF8, 0, zipDir.c_str(), -1, bufF, MAX_PATH + 20, NULL, NULL);
+ }
+
+ unsigned long calculate_crc = 0;
+ const char* passwordCh = NULL;
+ if(password.size() > 0)
+ {
+ if(!GetFileCrc(localDir.c_str(), buf, 1024, &calculate_crc))
+ {
+ error = true;
+ break;
+ }
+
+ passwordCh = password.c_str();
+ }
+
+ int err = zipOpenNewFileInZip4_64 (zf, bufF, &zi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0,
+ -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, passwordCh, calculate_crc, 0, flag, 0);
+ if (err == ZIP_OK)
+ {
+ HANDLE hFile = CreateFile(localDir.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ if(hFile != INVALID_HANDLE_VALUE)
+ {
+ DWORD readed;
+ do
+ {
+ err = ZIP_OK;
+ if(!ReadFile(hFile, buf, 1024, &readed, NULL))
+ {
+ error = true;
+ break;
+ }
+
+ if (readed > 0)
+ {
+ err = zipWriteInFileInZip(zf, buf, readed);
+ }
+ }
+ while ((err == ZIP_OK) && (readed > 0));
+ CloseHandle(hFile);
+ }
+
+ if(zipCloseFileInZip(zf) != ZIP_OK)
+ {
+ error = true;
+ break;
+ }
+ }
+ else
+ {
+ error = true;
+ break;
+ }
+
+ files.pop_front();
+ }
+
+ zipClose(zf, NULL);
+ }
+ else
+ {
+ error = true;
+ }
+ }
+
+ DeleteDirectory(dir.c_str());
+ return error;
+}
+
+bool UnzipFiles(const std::wstring& dir, std::wstring& zipFilePath, const std::string& password)
+{
+ std::list<std::wstring> files;
+ bool error = false;
+ zlib_filefunc_def pzlib_filefunc_def;
+ fill_win32_filefunc(&pzlib_filefunc_def);
+ std::wstring fileNameInZip;
+ std::map<std::wstring, bool> existingContacts;
+ zipFilePath = GetFileName(zipFilePath, L"", existingContacts, true);
+ zipFilePath = ReplaceExt(zipFilePath, L"zip");
+ unzFile zf = unzOpen2((LPCSTR)(LPTSTR)zipFilePath.c_str(), &pzlib_filefunc_def);
+ if (zf != NULL)
+ {
+ char buf[8192];
+ char bufF[MAX_PATH + 20];
+ unz_file_info file_info;
+ do
+ {
+ int err = unzGetCurrentFileInfo(zf, &file_info, bufF, MAX_PATH + 20, buf, 8192, NULL, 0);
+ if (err == UNZ_OK)
+ {
+ UINT cp = CP_OEMCP;
+ if(file_info.flag & 0x800)// UTF
+ {
+ cp = CP_UTF8;
+ }
+
+ // Get Unicode file name for InfoZip style archives, otherwise assume PKZip/WinZip style
+ if (file_info.size_file_extra)
+ {
+ char *p = buf;
+ unsigned long size = min(file_info.size_file_extra, 8192);
+ while (size > 0)
+ {
+ unsigned short id = *(unsigned short*)p;
+ unsigned len = *(unsigned short*)(p + 2);
+
+ if (size < (len + 4)) break;
+
+ if (id == 0x7075 && len > 5 && (len - 5) < MAX_PATH + 20 && *(p + 4) == 1)
+ {
+ memcpy(bufF, p + 9, len - 5);
+ bufF[len - 5] = 0;
+ cp = CP_UTF8;
+ break;
+ }
+ size -= len + 4;
+ p += len + 4;
+ }
+ }
+
+ int sizeC = (int)strlen(bufF);
+ int sizeW = MultiByteToWideChar(cp, 0, bufF, sizeC, NULL, 0);
+ fileNameInZip.resize(sizeW);
+ MultiByteToWideChar(cp, 0, bufF, sizeC, (wchar_t*)fileNameInZip.c_str(), sizeW);
+ fileNameInZip = dir + L"\\" + fileNameInZip;
+ for (size_t i = 0; i < fileNameInZip.length(); ++i)
+ {
+ if (fileNameInZip[i] == L'/')
+ fileNameInZip[i] = L'\\';
+ }
+
+ if (file_info.external_fa & FILE_ATTRIBUTE_DIRECTORY)
+ CreatePath(fileNameInZip.c_str());
+ else
+ {
+ const char* passwordCh = NULL;
+ if(password.size() > 0)
+ {
+ passwordCh = password.c_str();
+ }
+
+ err = unzOpenCurrentFilePassword(zf, passwordCh);
+ if (err == UNZ_OK)
+ {
+ CreatePath(GetDirectoryName(fileNameInZip).c_str());
+ HANDLE hFile = CreateFile(fileNameInZip.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
+ if(hFile != INVALID_HANDLE_VALUE)
+ {
+ DWORD writed;
+ for (;;)
+ {
+ err = unzReadCurrentFile(zf, buf, 8192);
+ if (err <= 0) break;
+
+ if (!WriteFile(hFile, buf, err, &writed, FALSE))
+ {
+ err = -1;
+ break;
+ }
+ }
+
+ CloseHandle(hFile);
+ if(err < 0)
+ {
+ error = true;
+ break;
+ }
+ }
+ else
+ {
+ unzCloseCurrentFile(zf);
+ error = true;
+ break;
+ }
+
+ if(unzCloseCurrentFile(zf) != ZIP_OK)
+ {
+ error = true;
+ break;
+ }
+ }
+ else
+ {
+ error = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ error = true;
+ break;
+ }
+ }
+ while (unzGoToNextFile(zf) == UNZ_OK);
+
+ unzClose(zf);
+ }
+ else
+ {
+ error = true;
+ }
+
+ return error;
+}
+
+bool FtpFiles(const std::wstring& dir, const std::wstring& filePath, const std::wstring& ftpName)
+{
+ std::list<std::wstring> files;
+ std::map<std::wstring, bool> existingContacts;
+ ListDirectory(dir, L"\\", files);
+ if(files.size() > 0)
+ {
+ std::wofstream stream ((dir + _T("\\script.sc")).c_str());
+ if(stream.is_open())
+ {
+ std::wstring ftpDir = GetDirectoryName(filePath);
+ ftpDir = GetFileName(ftpDir, L"", existingContacts, false);
+ stream << "option batch continue\noption confirm off\nopen \""
+ << ftpName << "\"\noption transfer binary\n";
+ std::wstring lastCD;
+ size_t filSize = files.size();
+ while(files.size() > 0)
+ {
+ std::wstring localDir = *files.begin();
+ std::wstring currentCD = ftpDir + GetDirectoryName(ReplaceStr(localDir, L'\\', L'/'));
+ if(currentCD != lastCD)
+ {
+ if(!currentCD.empty() && currentCD != L"/")
+ stream << "mkdir \"" << currentCD << "\"\n";
+ stream << "cd \"" << currentCD << "\"\n";
+ lastCD = currentCD;
+ }
+
+ std::wstring name = GetName(localDir);
+ stream << "call MDTM " << name << "\n";
+ stream << "put \"." << localDir << "\"\n";
+ stream << "call MDTM " << name << "\n";
+ files.pop_front();
+ }
+
+ stream.close();
+ std::wstring &log = Options::instance->ftpLogPath;
+ CreateDirectory(GetDirectoryName(log).c_str(), NULL);
+ DeleteFile(log.c_str());
+ TCHAR cmdLine[MAX_PATH];
+ _stprintf_s(cmdLine, _T("\"%s\" /nointeractiveinput /log=\"%s\" /script=script.sc"), Options::instance->ftpExePath.c_str(), log.c_str());
+ STARTUPINFO startupInfo = {0};
+ PROCESS_INFORMATION processInfo;
+ startupInfo.cb = sizeof(STARTUPINFO);
+ if(CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, dir.c_str(), &startupInfo, &processInfo))
+ {
+ WaitForSingleObject(processInfo.hProcess, INFINITE);
+ CloseHandle(processInfo.hThread);
+ CloseHandle(processInfo.hProcess);
+ if(log.empty())
+ {
+ return false;
+ }
+
+ std::wifstream logStream (log.c_str());
+ if(logStream.is_open())
+ {
+ bool isInMDTM = false;
+ std::list<std::wstring> dates;
+ while(!logStream.eof())
+ {
+ std::wstring lineStr;
+ std::getline(logStream, lineStr);
+ if(lineStr.length() > 1)
+ {
+ if(lineStr[0] == L'>')
+ {
+ if(isInMDTM)
+ {
+ if(lineStr.find(L"Script:") < lineStr.length())
+ {
+ dates.push_back(L"");
+ isInMDTM = false;
+ }
+ }
+
+ if(lineStr.find(L"Script: call MDTM") < lineStr.length())
+ {
+ isInMDTM = true;
+ }
+ }
+ else if(isInMDTM && lineStr[0] == L'<')
+ {
+ size_t ss = lineStr.find(L"Script: 213 ");
+ if(ss < lineStr.length())
+ {
+ ss += 12;
+ if(ss < lineStr.length())
+ {
+ lineStr = lineStr.substr(ss);
+ if(lineStr.size() == 14)
+ {
+ dates.push_back(lineStr);
+ isInMDTM = false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(dates.size() > 0 && dates.size() == filSize * 2)
+ {
+ for(std::list<std::wstring>::const_iterator it = dates.begin(); it != dates.end(); ++it)
+ {
+ std::wstring date1 = *it++;
+ if(it->empty() || date1 == *it)
+ return true;
+
+ }
+
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool FtpGetFiles(const std::wstring& dir, const std::list<std::wstring>& files, const std::wstring& ftpName)
+{
+ std::map<std::wstring, bool> existingContacts;
+ std::wstring script = dir + _T("\\script.sc");
+ std::wofstream stream (script.c_str());
+ if(stream.is_open())
+ {
+ stream << "option batch continue\noption confirm off\nopen \""
+ << ftpName << "\"\noption transfer binary\n";
+ std::wstring lastCD;
+ std::list<std::wstring> localFiles;
+ for(std::list<std::wstring>::const_iterator it = files.begin(); it != files.end(); ++it)
+ {
+ std::wstring fileName = GetName(*it);
+ localFiles.push_back(dir + L"\\" + fileName);
+ std::wstring currentCD = GetDirectoryName(*it);
+ if(currentCD != lastCD)
+ {
+ stream << "cd \"" << currentCD << "\"\n";
+ lastCD = currentCD;
+ }
+
+ stream << "get \"" << fileName << "\"\n";
+ }
+
+ stream.close();
+ std::wstring &log = Options::instance->ftpLogPath;
+ CreateDirectory(GetDirectoryName(log).c_str(), NULL);
+ DeleteFile(log.c_str());
+ TCHAR cmdLine[MAX_PATH];
+ _stprintf_s(cmdLine, _T("\"%s\" /nointeractiveinput /log=\"%s\" /script=script.sc"), Options::instance->ftpExePath.c_str(), log.c_str());
+ STARTUPINFO startupInfo = {0};
+ PROCESS_INFORMATION processInfo;
+ startupInfo.cb = sizeof(STARTUPINFO);
+ if(CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, dir.c_str(), &startupInfo, &processInfo))
+ {
+ WaitForSingleObject(processInfo.hProcess, INFINITE);
+ CloseHandle(processInfo.hThread);
+ CloseHandle(processInfo.hProcess);
+ }
+
+ DeleteFile(script.c_str());
+ for(std::list<std::wstring>::const_iterator it = localFiles.begin(); it != localFiles.end(); ++it)
+ {
+ DWORD atr = GetFileAttributes(it->c_str());
+ if(atr == INVALID_FILE_ATTRIBUTES || atr & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+void CreatePath(const TCHAR *szDir)
+{
+ if (!szDir) return;
+
+ DWORD dwAttributes;
+ TCHAR *pszLastBackslash, szTestDir[ MAX_PATH ];
+
+ lstrcpyn( szTestDir, szDir, SIZEOF( szTestDir ));
+ if (( dwAttributes = GetFileAttributes( szTestDir )) != INVALID_FILE_ATTRIBUTES && ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY ))
+ return;
+
+ pszLastBackslash = _tcsrchr( szTestDir, '\\' );
+ if ( pszLastBackslash == NULL )
+ return;
+
+ *pszLastBackslash = '\0';
+ CreatePath( szTestDir );
+ *pszLastBackslash = '\\';
+
+ CreateDirectory( szTestDir, NULL );
+}
+
+INT_PTR ExecuteTaskService(WPARAM wParam, LPARAM lParam)
+{
+ EnterCriticalSection(&Options::instance->criticalSection);
+ int taskNr = (int)wParam;
+ if(taskNr < 0 || taskNr >= (int)Options::instance->taskOptions.size())
+ {
+ LeaveCriticalSection(&Options::instance->criticalSection);
+ return FALSE;
+ }
+
+ Options::instance->taskOptions[taskNr].forceExecute = true;
+ Options::instance->taskOptions[taskNr].showMBAfterExecute = true;
+ LeaveCriticalSection(&Options::instance->criticalSection);
+ StartThread(false);
+ return TRUE;
+}
+
+void DoError(const TaskOptions& to, const std::wstring _error)
+{
+ TCHAR msg[256];
+ _stprintf_s(msg, TranslateT("Task '%s' execution failed:"), to.taskName.c_str());
+ if(Options::instance->schedulerHistoryAlerts)
+ {
+ std::wstring error = msg;
+ error += L"\n";
+ error += _error;
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(DBEVENTINFO);
+ dbei.szModule = MODULE;
+ dbei.flags = DBEF_UTF | DBEF_READ;
+ dbei.timestamp = time(NULL);
+ // For now I do not convert event data from string to blob, and event type must be message to handle it properly
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ int len = (int)error.length() + 1;
+ dbei.cbBlob = WideCharToMultiByte(CP_UTF8, 0, error.c_str(), len, NULL, 0, NULL, NULL);
+ char* buf = new char[dbei.cbBlob];
+ dbei.cbBlob = WideCharToMultiByte(CP_UTF8, 0, error.c_str(), len, buf, dbei.cbBlob, NULL, NULL);
+ dbei.pBlob = (PBYTE)buf;
+ CallService(MS_DB_EVENT_ADD, NULL, (LPARAM) & dbei);
+ }
+
+
+ if(Options::instance->schedulerAlerts)
+ {
+ if(CallService(MS_SYSTEM_TERMINATED, 0, 0)) return;
+ if(ServiceExists(MS_POPUP_ADDPOPUPCLASS))
+ {
+ ShowClassPopupT(MODULE, msg, (wchar_t*)_error.c_str());
+ }
+ else if (ServiceExists( MS_POPUP_ADDPOPUPT ))
+ {
+ POPUPDATAT ppd = {0};
+ ppd.lchIcon = LoadSkinnedIcon(SKINICON_OTHER_HISTORY);
+ _tcscpy_s(ppd.lptzContactName, msg);
+ _tcscpy_s(ppd.lptzText, _error.c_str());
+ CallService(MS_POPUP_ADDPOPUPT, (WPARAM)&ppd, 0);
+ }
+ }
+}
diff --git a/plugins/BasicHistory/src/SearchContext.h b/plugins/BasicHistory/src/SearchContext.h
new file mode 100644
index 0000000000..6b1655d857
--- /dev/null
+++ b/plugins/BasicHistory/src/SearchContext.h
@@ -0,0 +1,55 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+#include "EventList.h"
+
+class SearchContext : public EventList
+{
+public:
+ HWND editWindow;
+ HWND findWindow;
+ HWND toolbarWindow;
+ HWND listWindow;
+ int selected;
+
+ virtual void SelectEventGroup(int sel) = 0;
+ virtual HANDLE GetNextContact(HANDLE hContact, int adder) = 0;
+ virtual void SelectContact(HANDLE _hContact) = 0;
+
+ struct MessageData
+ {
+ MessageData(const std::wstring& _description, int _startPos, int _endPos, bool _isMe, DWORD _timestamp)
+ :description(_description)
+ {
+ startPos = _startPos;
+ endPos = _endPos;
+ isMe = _isMe;
+ timestamp = _timestamp;
+ }
+
+ std::wstring description;
+ int startPos;
+ int endPos;
+ bool isMe;
+ DWORD timestamp;
+ };
+
+ std::vector<MessageData> currentGroup;
+};
+
diff --git a/plugins/BasicHistory/src/Searcher.cpp b/plugins/BasicHistory/src/Searcher.cpp
new file mode 100644
index 0000000000..c93685bcf3
--- /dev/null
+++ b/plugins/BasicHistory/src/Searcher.cpp
@@ -0,0 +1,388 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "Searcher.h"
+#include "resource.h"
+
+Searcher::Searcher()
+ :lastFindSelection(-1),
+ findBack(false),
+ matchCase(false),
+ matchWholeWords(false),
+ onlyIn(false),
+ onlyOut(false),
+ context(NULL)
+{
+}
+
+void Searcher::ChangeFindDirection(bool isBack)
+{
+ if(isBack != findBack)
+ {
+ findBack = isBack;
+ ClearFind();
+ TBBUTTONINFO tbInfo;
+ tbInfo.cbSize = sizeof(TBBUTTONINFO);
+ tbInfo.dwMask = TBIF_TEXT | TBIF_IMAGE;
+ if(isBack)
+ {
+ tbInfo.pszText = TranslateT("Find Previous");
+ tbInfo.iImage = 1;
+ }
+ else
+ {
+ tbInfo.pszText = TranslateT("Find Next");
+ tbInfo.iImage = 0;
+ }
+ SendMessage(context->toolbarWindow, TB_SETBUTTONINFO, (WPARAM)IDM_FIND, (LPARAM)&tbInfo);
+ }
+
+ Find();
+}
+
+void Searcher::ClearFind()
+{
+ if(lastFindSelection != -1)
+ {
+ SendMessage(context->editWindow,EM_SETOPTIONS,ECOOP_AND,~ECO_NOHIDESEL);
+ lastFindSelection = -1;
+ }
+}
+
+inline TCHAR mytoupper(TCHAR a, std::locale* loc)
+{
+ return std::toupper<TCHAR>(a, *loc);
+}
+
+bool Searcher::CompareStr(std::wstring str, TCHAR *strFind)
+{
+ std::locale loc;
+ if(!matchCase)
+ std::transform(str.begin(), str.end(), str.begin(), std::bind2nd(std::ptr_fun(mytoupper), &loc));
+ if(!matchWholeWords)
+ return str.find(strFind) < str.length();
+ size_t findid = str.find(strFind);
+ size_t findLen = _tcslen(strFind);
+ while(findid < str.length())
+ {
+ if((findid == 0 || std::isspace(str[findid - 1], loc) || std::ispunct(str[findid - 1], loc)) &&
+ (findid + findLen >= str.length() || std::isspace(str[findid + findLen], loc) || std::ispunct(str[findid + findLen], loc)))
+ return true;
+ findid = str.find(strFind, findid + 1);
+ }
+
+ return false;
+}
+
+void Searcher::Find()
+{
+ FINDTEXTEX ft;
+ TCHAR str[128];
+ int curSel = 0;
+ bool isStart = false;
+ bool finished = false;
+ ft.chrg.cpMin = 0;
+ ft.chrg.cpMax = -1;
+ ft.lpstrText = str;
+ if(context->currentGroup.size() < 1)
+ {
+ SendMessage(context->editWindow,EM_SETOPTIONS,ECOOP_AND,~ECO_NOHIDESEL);
+ lastFindSelection = -1;
+ return;
+ }
+
+ GetWindowText(context->findWindow, str, 128);
+ if(!str[0])
+ {
+ TCHAR buf[256];
+ mir_sntprintf(buf, 256, TranslateT("\"%s\" not found"), str);
+ MessageBox(context->hWnd, buf, TranslateT("Search"), MB_OK | MB_ICONINFORMATION);
+ return;
+ }
+ if(!matchCase)
+ {
+ std::locale loc;
+ std::transform(str, str + _tcslen(str), str, std::bind2nd(std::ptr_fun(mytoupper), &loc));
+ }
+
+ bool findBack1 = findBack ^ !searchForInMes;
+ bool findBack2 = findBack ^ !searchForInLG;
+ int adder1 = findBack1 ? -1 : 1;
+ int adder2 = findBack2 ? -1 : 1;
+ WPARAM findStyle = (findBack1 ? 0 : FR_DOWN) | (matchCase ? FR_MATCHCASE : 0) | (matchWholeWords ? FR_WHOLEWORD : 0);
+ if(lastFindSelection >= 0 && lastFindSelection < (int)context->currentGroup.size())
+ {
+ if(onlyIn && context->currentGroup[lastFindSelection].isMe || onlyOut && !context->currentGroup[lastFindSelection].isMe)
+ {
+ curSel = lastFindSelection + adder1;
+ }
+ else
+ {
+ SendDlgItemMessage(context->hWnd,IDC_EDIT,EM_EXGETSEL,0,(LPARAM)&ft.chrg);
+ if(findBack1)
+ {
+ ft.chrg.cpMin = ft.chrg.cpMin < context->currentGroup[lastFindSelection].endPos ? ft.chrg.cpMin : context->currentGroup[lastFindSelection].endPos;
+ ft.chrg.cpMax = context->currentGroup[lastFindSelection].startPos;
+ }
+ else
+ {
+ ft.chrg.cpMin = ft.chrg.cpMax > context->currentGroup[lastFindSelection].startPos ? ft.chrg.cpMax : context->currentGroup[lastFindSelection].startPos;
+ ft.chrg.cpMax = context->currentGroup[lastFindSelection].endPos;
+ }
+ SendMessage(context->editWindow,EM_FINDTEXTEX, findStyle,(LPARAM)&ft);
+ if(ft.chrgText.cpMin < 0 || ft.chrgText.cpMax < 0)
+ {
+ curSel = lastFindSelection + adder1;
+ }
+ else
+ {
+ if(isFindContactChanged && startFindContact == context->hContact && isFindSelChanged && context->selected == startFindSel && ((!findBack1 && ft.chrg.cpMin >= startFindPos) || (findBack1 && ft.chrg.cpMax <= startFindPos)))
+ {
+ finished = true;
+ }
+ else
+ {
+ curSel = lastFindSelection;
+ SendMessage(context->editWindow,EM_EXSETSEL,0,(LPARAM)&ft.chrgText);
+ SendMessage(context->editWindow,EM_SETOPTIONS,ECOOP_OR,ECO_NOHIDESEL);
+ lastFindSelection = curSel;
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ isStart = true;
+ SendMessage(context->editWindow,EM_SETOPTIONS,ECOOP_OR,ECO_NOHIDESEL);
+ SendMessage(context->editWindow,EM_EXGETSEL,0,(LPARAM)&ft.chrg);
+ startFindPos = findBack1 ? ft.chrg.cpMin : (ft.chrg.cpMax >= 0 ? ft.chrg.cpMax : ft.chrg.cpMin);
+ startFindSel = context->selected;
+ if(startFindPos < 0)
+ startFindPos = 0;
+ isFindSelChanged = false;
+ startFindContact = context->hContact;
+ isFindContactChanged = !allUsers;
+ if(findBack1)
+ for(curSel = (int)context->currentGroup.size() - 1; curSel >= 0; --curSel)
+ {
+ if(context->currentGroup[curSel].startPos < startFindPos)
+ break;
+ }
+ else
+ for(; curSel < (int)context->currentGroup.size(); ++curSel)
+ {
+ if(context->currentGroup[curSel].endPos > startFindPos)
+ break;
+ }
+ }
+
+ if(!finished)
+ {
+ for(; curSel < (int)context->currentGroup.size() && curSel >= 0; curSel += adder1)
+ {
+ if(onlyIn && context->currentGroup[curSel].isMe || onlyOut && !context->currentGroup[curSel].isMe)
+ continue;
+ if(CompareStr(context->currentGroup[curSel].description, str))
+ {
+ if(findBack1)
+ {
+ ft.chrg.cpMin = context->currentGroup[curSel].endPos;
+ ft.chrg.cpMax = context->currentGroup[curSel].startPos;
+ if(!isFindSelChanged && ft.chrg.cpMin > startFindPos)
+ ft.chrg.cpMin = startFindPos;
+ }
+ else
+ {
+ ft.chrg.cpMin = context->currentGroup[curSel].startPos;
+ ft.chrg.cpMax = context->currentGroup[curSel].endPos;
+ if(!isFindSelChanged && ft.chrg.cpMin < startFindPos)
+ ft.chrg.cpMin = startFindPos;
+ }
+ SendMessage(context->editWindow,EM_FINDTEXTEX, findStyle,(LPARAM)&ft);
+ if(!(ft.chrgText.cpMin < 0 || ft.chrgText.cpMax < 0))
+ {
+ if(isFindContactChanged && startFindContact == context->hContact && isFindSelChanged && context->selected == startFindSel && ((!findBack1 && ft.chrg.cpMin >= startFindPos) || (findBack1 && ft.chrg.cpMax <= startFindPos)))
+ {
+ finished = true;
+ break;
+ }
+ SendMessage(context->editWindow,EM_EXSETSEL,0,(LPARAM)&ft.chrgText);
+ SendMessage(context->editWindow,EM_SETOPTIONS,ECOOP_OR,ECO_NOHIDESEL);
+ lastFindSelection = curSel;
+ return;
+ }
+ }
+ }
+ }
+
+ if(isFindContactChanged && startFindContact == context->hContact && isFindSelChanged && context->selected == startFindSel)
+ {
+ finished = true;
+ }
+
+ if(!finished)
+ {
+ isFindSelChanged = true;
+ if(onlyGroup)
+ {
+ if(IsInSel(context->selected, str))
+ {
+ CHARRANGE ch;
+ ch.cpMin = ch.cpMax = findBack1 ? MAXLONG : 0;
+ SendMessage(context->editWindow,EM_EXSETSEL,0,(LPARAM)&ch);
+ lastFindSelection = findBack1 ? (int)context->currentGroup.size() - 1 : 0;
+ Find();
+ return;
+ }
+ }
+ else
+ {
+ for(int sel = context->selected + adder2; ; sel += adder2)
+ {
+ if(sel < 0)
+ {
+ isFindContactChanged = true;
+ if(allUsers)
+ {
+ HANDLE hNext = context->hContact;
+ do
+ {
+ hNext = context->GetNextContact(hNext, adder2);
+ }
+ while(hNext != startFindContact && !context->SearchInContact(hNext, str, this));
+ context->SelectContact(hNext);
+ }
+
+ sel = (int)context->eventList.size() - 1;
+ }
+ else if(sel >= (int)context->eventList.size())
+ {
+ isFindContactChanged = true;
+ if(allUsers)
+ {
+ HANDLE hNext = context->hContact;
+ do
+ {
+ hNext = context->GetNextContact(hNext, adder2);
+ }
+ while(hNext != startFindContact && !context->SearchInContact(hNext, str, this));
+ context->SelectContact(hNext);
+ }
+
+ sel = 0;
+ }
+ if(IsInSel(sel, str))
+ {
+ LVITEM item = {0};
+ item.mask = LVIF_STATE;
+ item.iItem = context->selected;
+ item.state = 0;
+ item.stateMask = LVIS_SELECTED;
+ ListView_SetItem(context->listWindow, &item);
+ item.iItem = sel;
+ item.state = LVIS_SELECTED;
+ ListView_SetItem(context->listWindow, &item);
+ ListView_EnsureVisible(context->listWindow, sel, FALSE);
+ CHARRANGE ch;
+ ch.cpMin = ch.cpMax = findBack1 ? MAXLONG : 0;
+ SendMessage(context->editWindow,EM_EXSETSEL,0,(LPARAM)&ch);
+ SendMessage(context->editWindow,EM_SETOPTIONS,ECOOP_OR,ECO_NOHIDESEL);
+ lastFindSelection = findBack1 ? (int)context->currentGroup.size() - 1 : 0;
+ isFindSelChanged = true;
+ Find();
+ return;
+ }
+ if(startFindContact == context->hContact && sel == startFindSel)
+ break;
+ }
+ }
+ }
+
+ if(startFindContact != context->hContact)
+ {
+ context->SelectContact(startFindContact);
+ }
+
+ if(startFindSel != context->selected)
+ {
+ LVITEM item = {0};
+ item.mask = LVIF_STATE;
+ item.iItem = context->selected;
+ item.state = 0;
+ item.stateMask = LVIS_SELECTED;
+ ListView_SetItem(context->listWindow, &item);
+ item.iItem = startFindSel;
+ item.state = LVIS_SELECTED;
+ ListView_SetItem(context->listWindow, &item);
+ ListView_EnsureVisible(context->listWindow, startFindSel, FALSE);
+ context->SelectEventGroup(startFindSel);
+ SendMessage(context->editWindow,EM_SETOPTIONS,ECOOP_OR,ECO_NOHIDESEL);
+ }
+ ft.chrgText.cpMin = startFindPos;
+ ft.chrgText.cpMax = startFindPos;
+ SendMessage(context->editWindow,EM_EXSETSEL,0,(LPARAM)&ft.chrgText);
+ SendMessage(context->editWindow,EM_SETOPTIONS,ECOOP_AND,~ECO_NOHIDESEL);
+ lastFindSelection = -1;
+ if(isStart)
+ {
+ TCHAR buf[256];
+ GetWindowText(context->findWindow, str, 128);
+ mir_sntprintf(buf, 256, TranslateT("\"%s\" not found"), str);
+ MessageBox(context->hWnd, buf, TranslateT("Search"), MB_OK | MB_ICONINFORMATION);
+ }
+ else
+ {
+ MessageBox(context->hWnd, TranslateTS(onlyGroup ? LPGENT("You have reached the end of the group.") : LPGENT("You have reached the end of the history.")), TranslateT("Search"), MB_OK | MB_ICONINFORMATION);
+ }
+}
+
+bool Searcher::IsInSel(int sel, TCHAR *strFind)
+{
+ if(sel < 0 || sel >= (int)context->eventList.size())
+ return false;
+
+ TCHAR str[MAXSELECTSTR + 8]; // for safety reason
+ EventList::EventData data;
+ for(std::deque<EventList::EventIndex>::iterator it = context->eventList[sel].begin(); it != context->eventList[sel].end(); ++it)
+ {
+ EventList::EventIndex hDbEvent = *it;
+ if(context->GetEventData(hDbEvent, data))
+ {
+ bool isMe = data.isMe;
+ if(onlyIn && isMe || onlyOut && !isMe)
+ continue;
+ context->GetEventMessage(hDbEvent, str);
+ if(CompareStr(str, strFind))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool Searcher::Compare(const bool isMe, const std::wstring& message, TCHAR *strFind)
+{
+ if(onlyIn && isMe || onlyOut && !isMe)
+ return false;
+
+ return CompareStr(message, strFind);
+}
diff --git a/plugins/BasicHistory/src/Searcher.h b/plugins/BasicHistory/src/Searcher.h
new file mode 100644
index 0000000000..2f5aff6e8b
--- /dev/null
+++ b/plugins/BasicHistory/src/Searcher.h
@@ -0,0 +1,119 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+#include "SearchContext.h"
+
+class Searcher : ComparatorInterface
+{
+private:
+ SearchContext* context;
+ int lastFindSelection;
+ int startFindPos;
+ int startFindSel;
+ HANDLE startFindContact;
+ bool isFindSelChanged;
+ bool isFindContactChanged;
+ bool findBack, matchCase, matchWholeWords, onlyIn, onlyOut, onlyGroup, allUsers, searchForInLG, searchForInMes;
+
+ bool CompareStr(std::wstring str, TCHAR *strFind);
+ bool IsInSel(int sel, TCHAR *strFind);
+public:
+ Searcher();
+ virtual bool Compare(const bool isMe, const std::wstring& message, TCHAR *strFind);
+ void Find();
+ void ChangeFindDirection(bool isBack);
+ void ClearFind();
+ void SetContect(SearchContext* _context){
+ context = _context;
+ }
+ void SetMatchCase(bool val){
+ matchCase = val;
+ ClearFind();
+ }
+ void SetMatchWholeWords(bool val){
+ matchWholeWords = val;
+ ClearFind();
+ }
+ bool IsMatchCase(){
+ return matchCase;
+ }
+ bool IsMatchWholeWords(){
+ return matchWholeWords;
+ }
+ bool IsFindBack(){
+ return findBack;
+ }
+ void SetOnlyIn(bool val){
+ onlyIn = val;
+ if(val && onlyOut)
+ onlyOut = false;
+ ClearFind();
+ }
+ void SetOnlyOut(bool val){
+ onlyOut = val;
+ if(val && onlyIn)
+ onlyIn = false;
+ ClearFind();
+ }
+ bool IsOnlyIn(){
+ return onlyIn;
+ }
+ bool IsOnlyOut(){
+ return onlyOut;
+ }
+ void SetOnlyGroup(bool val){
+ onlyGroup = val;
+ if(onlyGroup)
+ allUsers = false;
+ ClearFind();
+ }
+ bool IsOnlyGroup(){
+ return onlyGroup;
+ }
+ void SetAllUsers(bool val){
+ allUsers = val;
+ if(allUsers)
+ onlyGroup = false;
+ ClearFind();
+ }
+ bool IsAllUsers(){
+ return allUsers;
+ }
+ void SetSearchForInLG(bool val){
+ if(searchForInLG != val)
+ {
+ searchForInLG = val;
+ ClearFind();
+ }
+ }
+ void SetSearchForInMes(bool val){
+ if(searchForInMes != val)
+ {
+ searchForInMes = val;
+ ClearFind();
+ }
+ }
+ bool IsSearchForInLG(){
+ return searchForInLG;
+ }
+ bool IsSearchForInMes(){
+ return searchForInMes;
+ }
+};
+
diff --git a/plugins/BasicHistory/src/TxtExport.cpp b/plugins/BasicHistory/src/TxtExport.cpp
new file mode 100644
index 0000000000..a5ca8afddf
--- /dev/null
+++ b/plugins/BasicHistory/src/TxtExport.cpp
@@ -0,0 +1,65 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "StdAfx.h"
+#include "TxtExport.h"
+#define EXP_FILE (*stream)
+
+TxtExport::~TxtExport()
+{
+}
+
+void TxtExport::WriteHeader(const std::wstring &fileName, const std::wstring &filterName, const std::wstring &myName, const std::wstring &myId, const std::wstring &name1, const std::wstring &proto1, const std::wstring &id1, const std::string& baseProto1, const std::wstring& encoding)
+{
+ TCHAR* start = TranslateT("###");
+ EXP_FILE << start << "\n" << start << _T(" ") << TranslateT("History Log") << _T("\n");
+ EXP_FILE << start << _T(" ") << myName;
+ if(proto1.length() || myId.length())
+ {
+ EXP_FILE << _T(" (") << proto1 << _T(": ") << myId << _T(") - ");
+ }
+ else
+ {
+ EXP_FILE << _T(" - ");
+ }
+
+ EXP_FILE << name1;
+ if(proto1.length() || id1.length())
+ {
+ EXP_FILE << _T(" (") << proto1 << _T(": ") << id1 << _T(")\n");
+ }
+ else
+ {
+ EXP_FILE << _T("\n");
+ }
+
+ EXP_FILE << start << _T(" ") << TranslateT("Filter:") << _T(" ") << filterName << _T("\n") << start << _T("\n");
+}
+
+void TxtExport::WriteFooter()
+{
+}
+
+void TxtExport::WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText)
+{
+}
+
+void TxtExport::WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei)
+{
+ EXP_FILE << "\n[" << longDate << "] " << user << ":\n" << message << "\n";
+}
diff --git a/plugins/BasicHistory/src/TxtExport.h b/plugins/BasicHistory/src/TxtExport.h
new file mode 100644
index 0000000000..bee2718649
--- /dev/null
+++ b/plugins/BasicHistory/src/TxtExport.h
@@ -0,0 +1,38 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+#include "IExport.h"
+
+class TxtExport :
+ public IExport
+{
+public:
+ virtual const TCHAR* GetExt()
+ {
+ return _T("txt");
+ }
+
+ virtual void WriteHeader(const std::wstring &fileName, const std::wstring &filterName, const std::wstring &myName, const std::wstring &myId, const std::wstring &name1, const std::wstring &proto1, const std::wstring &id1, const std::string& baseProto1, const std::wstring& encoding);
+ virtual void WriteFooter();
+ virtual void WriteGroup(bool isMe, const std::wstring &time, const std::wstring &user, const std::wstring &eventText);
+ virtual void WriteMessage(bool isMe, const std::wstring &longDate, const std::wstring &shortDate, const std::wstring &user, const std::wstring &message, const DBEVENTINFO& dbei);
+
+ virtual ~TxtExport();
+};
+
diff --git a/plugins/BasicHistory/src/codecvt_CodePage.h b/plugins/BasicHistory/src/codecvt_CodePage.h
new file mode 100644
index 0000000000..37b6b7d26f
--- /dev/null
+++ b/plugins/BasicHistory/src/codecvt_CodePage.h
@@ -0,0 +1,98 @@
+/*
+Basic History plugin
+Copyright (C) 2011-2012 Krzysztof Kral
+
+This program is free software; 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
+
+template<class _Elem>
+class codecvt_CodePage : public std::codecvt<_Elem, char, mbstate_t>
+{
+private:
+ UINT codePage;
+public:
+ typedef std::codecvt<_Elem, char, mbstate_t> _Mybase;
+ typedef typename _Mybase::result result;
+ typedef char _Byte;
+ typedef _Elem intern_type;
+ typedef _Byte extern_type;
+ typedef mbstate_t state_type;
+
+ explicit codecvt_CodePage(UINT _codePage)
+ : _Mybase(0),
+ codePage(_codePage)
+ { // construct with ref count
+ }
+
+ virtual ~codecvt_CodePage()
+ { // destroy the object
+ }
+
+protected:
+ virtual result do_in(mbstate_t& _State, const _Byte *_First1, const _Byte *_Last1, const _Byte *& _Mid1, _Elem *_First2, _Elem *_Last2, _Elem *& _Mid2) const
+ { // convert bytes [_First1, _Last1) to [_First2, _Last)
+ return (_Mybase::error); // not implemented
+ }
+
+ virtual result do_out(mbstate_t& _State, const _Elem *_First1, const _Elem *_Last1, const _Elem *& _Mid1, _Byte *_First2, _Byte *_Last2, _Byte *& _Mid2) const
+ { // convert [_First1, _Last1) to bytes [_First2, _Last)
+ _Mid1 = _First1;
+ _Mid2 = _First2;
+
+ int conv = WideCharToMultiByte(codePage, 0, _First1, _Last1 - _First1, _First2, _Last2 - _First2, NULL, NULL);
+ if(conv == 0)
+ {
+ if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ return (_Mybase::partial);
+ else
+ return (_Mybase::error);
+ }
+ else
+ {
+ _Mid1 = _Last1;
+ _Mid2 = _First2 + conv;
+ }
+
+ return (_Mybase::ok);
+ }
+
+ virtual result do_unshift(mbstate_t&,
+ _Byte *_First2, _Byte *, _Byte *& _Mid2) const
+ { // generate bytes to return to default shift state
+ _Mid2 = _First2;
+ return (_Mybase::ok);
+ }
+
+ virtual int do_length(const mbstate_t& _State, const _Byte *_First1, const _Byte *_Last1, size_t _Count) const _THROW0()
+ { // return min(_Count, converted length of bytes [_First1, _Last1))
+ return (int)_Count; // not implemented
+ }
+
+ virtual bool do_always_noconv() const _THROW0()
+ { // return true if conversions never change input
+ return (false);
+ }
+
+ virtual int do_max_length() const _THROW0()
+ { // return maximum length required for a conversion
+ return 6;
+ }
+
+ virtual int do_encoding() const _THROW0()
+ { // return length of code sequence (from codecvt)
+ return 0; // -1 => state dependent, 0 => varying length
+ }
+};
diff --git a/plugins/BasicHistory/src/dllmain.cpp b/plugins/BasicHistory/src/dllmain.cpp
new file mode 100644
index 0000000000..ad89773ec8
--- /dev/null
+++ b/plugins/BasicHistory/src/dllmain.cpp
@@ -0,0 +1,14 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#include "stdafx.h"
+
+HINSTANCE hInst;
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ hInst = hModule;
+ return TRUE;
+}
+
diff --git a/plugins/BasicHistory/src/resource.h b/plugins/BasicHistory/src/resource.h
new file mode 100644
index 0000000000..71484f2343
--- /dev/null
+++ b/plugins/BasicHistory/src/resource.h
@@ -0,0 +1,157 @@
+
+#define IDD_HISTORY 101
+#define IDD_OPT_MAIN 102
+#define IDD_OPT_GROUPLIST 103
+#define IDD_OPT_MESSAGES 104
+#define IDD_OPT_SEARCHING 105
+#define IDD_OPT_EXPORT 106
+#define IDD_OPT_SCHEDULER 107
+#define IDD_DLG_TASK 108
+#define IDC_EDIT 1001
+#define IDC_LIST 1002
+#define IDC_DELETEHISTORY 1004
+#define IDC_SPLITTER 1005
+#define IDC_FIND_TEXT 1006
+#define IDC_LIST_CONTACTS 1007
+#define IDC_SPLITTERV 1008
+#define IDC_SHOWHIDE 1009
+#define IDC_TOOLBAR 1010
+#define IDC_SHOWCONTACTS 1011
+#define IDC_NEWONTOP 1012
+#define IDC_SHOWEVENTS 1013
+#define IDC_SHOWTIME 1014
+#define IDC_SHOWNAME 1015
+#define IDC_SHOWMESSAGE 1016
+#define IDC_MESSAGELEN 1017
+#define IDC_MESSAGELEN_DESC 1018
+#define IDC_GROUPTIME 1019
+#define IDC_LIMITMESSAGES 1020
+#define IDC_SHOWDATE 1021
+#define IDC_SHOWSECOND 1022
+#define IDC_SHOWSMILEYS 1023
+#define IDC_FORLIST 1024
+#define IDC_FORMES 1025
+#define IDC_MATCHCASE 1026
+#define IDC_MATCHWHOLE 1027
+#define IDC_ONLYIN 1028
+#define IDC_ONLYOUT 1029
+#define IDC_ONLYGROUP 1030
+#define IDC_DEFFILTER 1031
+#define IDC_LIST_FILTERS 1032
+#define IDC_LIST_EVENTS 1033
+#define IDC_ADD_FILTER 1034
+#define IDC_DELETE_FILTER 1035
+#define IDC_FILTER_NAME 1036
+#define IDC_EVENT 1037
+#define IDC_ADD_EVENT 1038
+#define IDC_DELETE_EVENT 1039
+#define IDC_SHOWCONTACTGROUPS 1040
+#define IDC_TXTENC 1041
+#define IDC_HTML1ENC 1042
+#define IDC_HTML1DATE 1043
+#define IDC_HTML2ENC 1044
+#define IDC_HTML2DATE 1045
+#define IDC_HTML2SHOWSMILEYS 1046
+#define IDC_HTML2EXTCSS 1047
+#define IDC_HTML2EXTCSSFILE 1048
+#define IDC_LIST_TASKS 1049
+#define IDC_ADD_TASK 1050
+#define IDC_EDIT_TASK 1051
+#define IDC_DELETE_TASK 1052
+#define IDC_TASK_TYPE 1053
+#define IDC_TASK_FILTER 1054
+#define IDC_LIST_CONTACTSEX 1055
+#define IDC_EVENT_TIME 1056
+#define IDC_EVENT_UNIT 1057
+#define IDC_TRIGER_TYPE 1058
+#define IDC_EXPORT_TYPE 1059
+#define IDC_EXPORT_TYPE_LABEL 1060
+#define IDC_COMPRESS 1061
+#define IDC_EXPORT_PATH 1062
+#define IDC_EXPORT_PATH_LABEL 1063
+#define IDC_TASK_STAR 1064
+#define IDC_FTP 1065
+#define IDC_UPLOAD 1066
+#define IDC_FTP_LABEL 1067
+#define IDC_TRIGER_TIME 1068
+#define IDC_TRIGER_TIME_LABEL 1069
+#define IDC_TRIGER_WEEK 1070
+#define IDC_TRIGER_WEEK_LABEL 1071
+#define IDC_TRIGER_DAY 1072
+#define IDC_TRIGER_DAY_LABEL 1073
+#define IDC_TRIGER_DELTA_TIME 1074
+#define IDC_TRIGER_DELTA_TIME_LABEL 1075
+#define IDC_WINSCP 1076
+#define IDC_WINSCPLOG 1077
+#define IDC_TASK_NAME 1078
+#define IDC_TASK_ACTIVE 1079
+#define IDC_TASK_FILTER_LABEL 1080
+#define IDC_EVENT_LABEL 1081
+#define IDC_IMPORT_TYPE 1082
+#define IDC_PASSWORD 1083
+#define IDC_PASSWORD_LABEL 1084
+#define IDC_EXPIMP 1085
+#define IDC_WINSCP_BROWSE 1086
+#define IDC_WINSCPLOG_BROWSE 1087
+#define IDC_CSS_BROWSE 1088
+#define IDC_SCHEDULER_ALERTS 1089
+#define IDC_SCHEDULER_HISTORY_ALERTS 1090
+#define IDC_ALLCONTACTS 1091
+#define IDI_INM 20000
+#define IDI_OUTM 20001
+#define IDI_SHOW 20002
+#define IDI_HIDE 20003
+#define IDI_FINDNEXT 20004
+#define IDI_FINDPREV 20005
+#define IDI_STATUS 20008
+#define IDR_CSS 20009
+#define IDR_JS 20010
+#define IDI_PLUSEX 20011
+#define IDI_MINUSEX 20012
+#define IDM_FIND 40001
+#define IDM_CONFIG 40002
+#define IDM_FINDNEXT 40003
+#define IDM_FINDPREV 40004
+#define IDM_MATCHCASE 40005
+#define IDM_MATCHWHOLE 40006
+#define IDM_OPTIONS 40007
+#define IDM_FONTS 40008
+#define IDM_ICONS 40009
+#define IDM_HOTKEYS 40010
+#define IDM_SAVEPOS 40011
+#define IDM_SAVEPOSALL 40012
+#define IDM_ONLYIN 40013
+#define IDM_ONLYOUT 40014
+#define IDM_DELETE 40015
+#define IDM_DELETEGROUP 40016
+#define IDM_DELETEUSER 40017
+#define IDM_MESSAGE 40018
+#define IDM_COPY 40019
+#define IDM_OPENNEW 40020
+#define IDM_OPENEXISTING 40021
+#define IDM_COPYLINK 40022
+#define IDM_ONLYGROUP 40023
+#define IDM_QUOTE 40024
+#define IDM_FILTERDEF 40025
+#define IDM_FILTERALL 40026
+#define IDM_EXPORTRHTML 40027
+#define IDM_EXPORTPHTML 40028
+#define IDM_EXPORTTXT 40029
+#define IDM_EXPORTBINARY 40030
+#define IDM_IMPORTBINARY 40031
+#define IDM_EXPORTDAT 40032
+#define IDM_IMPORTDAT 40033
+#define IDM_ALLUSERS 40034
+
+#define CUSTOMRES 300
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 106
+#define _APS_NEXT_COMMAND_VALUE 40024
+#define _APS_NEXT_CONTROL_VALUE 1030
+#define _APS_NEXT_SYMED_VALUE 106
+#endif
+#endif
diff --git a/plugins/BasicHistory/src/stdafx.cpp b/plugins/BasicHistory/src/stdafx.cpp
new file mode 100644
index 0000000000..dd47c41552
--- /dev/null
+++ b/plugins/BasicHistory/src/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// BasicHistory.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/plugins/BasicHistory/src/stdafx.h b/plugins/BasicHistory/src/stdafx.h
new file mode 100644
index 0000000000..bc670d4358
--- /dev/null
+++ b/plugins/BasicHistory/src/stdafx.h
@@ -0,0 +1,88 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#ifndef _WIN64
+#define _USE_32BIT_TIME_T
+#endif
+
+#define _CRT_SECURE_NO_WARNINGS
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include <windows.h>
+#include <windowsx.h>
+#include <richedit.h>
+#include <time.h>
+#include <commctrl.h>
+#include <commdlg.h>
+#include <tom.h>
+#include <richole.h>
+#include <Shlobj.h>
+#include <shellapi.h>
+
+#include <map>
+#include <hash_set>
+#include <vector>
+#include <queue>
+#include <locale>
+#include <string>
+#include <fstream>
+
+#define MIRANDA_VER 0x0900
+#define MIRANDA_CUSTOM_LP
+
+#include <newpluginapi.h>
+#include <m_langpack.h>
+#include <m_system.h>
+#include <m_clist.h>
+#include <m_skin.h>
+#include <m_history.h>
+#include <m_database.h>
+#include <m_icolib.h>
+#include <m_timezones.h>
+#include <m_contacts.h>
+#include <m_protocols.h>
+#include <m_button.h>
+#include <m_options.h>
+#include <m_fontservice.h>
+#include <m_hotkeys.h>
+#include <m_message.h>
+#include <m_protosvc.h>
+#include <m_icq.h>
+#include <m_clc.h>
+#include <m_utils.h>
+#include <m_popup.h>
+
+#include <win2k.h>
+
+#include "m_updater.h"
+#include "m_smileyadd.h"
+#include "m_toolbar.h"
+#include "m_metacontacts.h"
+
+#define HISTORY_HK_FIND 100
+#define HISTORY_HK_FINDNEXT 101
+#define HISTORY_HK_FINDPREV 102
+#define HISTORY_HK_MATCHCASE 103
+#define HISTORY_HK_MATCHWHOLE 104
+#define HISTORY_HK_SHOWCONTACTS 105
+#define HISTORY_HK_ONLYIN 106
+#define HISTORY_HK_ONLYOUT 107
+#define HISTORY_HK_DELETE 108
+#define HISTORY_HK_ONLYGROUP 109
+#define HISTORY_HK_EXRHTML 110
+#define HISTORY_HK_EXPHTML 111
+#define HISTORY_HK_EXTXT 112
+#define HISTORY_HK_EXBIN 113
+#define HISTORY_HK_IMPBIN 114
+#define HISTORY_HK_EXDAT 115
+#define HISTORY_HK_IMPDAT 116
+#define HISTORY_HK_ALLCONTACTS 117
+
+#define EVENTTYPE_STATUSCHANGE 25368
+#define EVENTTYPE_SMTPSIMPLE 2350 \ No newline at end of file
diff --git a/plugins/BasicHistory/src/targetver.h b/plugins/BasicHistory/src/targetver.h
new file mode 100644
index 0000000000..90e767bfce
--- /dev/null
+++ b/plugins/BasicHistory/src/targetver.h
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>
diff --git a/plugins/BasicHistory/src/version.h b/plugins/BasicHistory/src/version.h
new file mode 100644
index 0000000000..8b7d5ce949
--- /dev/null
+++ b/plugins/BasicHistory/src/version.h
@@ -0,0 +1,20 @@
+#define __MAJOR_VERSION 1
+#define __MINOR_VERSION 0
+#define __RELEASE_NUM 1
+#define __BUILD_NUM 6
+
+#define __FILEVERSION_STRING __MAJOR_VERSION,__MINOR_VERSION,__RELEASE_NUM,__BUILD_NUM
+#define __FILEVERSION_DOTS __MAJOR_VERSION.__MINOR_VERSION.__RELEASE_NUM.__BUILD_NUM
+
+#define __STRINGIFY_IMPL(x) #x
+#define __STRINGIFY(x) __STRINGIFY_IMPL(x)
+#define __VERSION_STRING __STRINGIFY(__FILEVERSION_DOTS)
+
+#define __PLUGIN_NAME "Basic History"
+#define __INTERNAL_NAME "BasicHistory"
+#define __FILENAME "BasicHistory.dll"
+#define __DESCRIPTION "History viewer for Miranda IM."
+#define __AUTHOR "Krzysztof Kral"
+#define __AUTHOREMAIL "krzysztof.kral@gmail.com"
+#define __AUTHORWEB "http://programista.free.of.pl/miranda/"
+#define __COPYRIGHT "Copyright (c) 2011-2012 Krzysztof Kral"