From 6b3ded37e4a4825be2df3612bdcbb7dfc00a1800 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Tue, 4 Mar 2014 20:41:13 +0000 Subject: HistoryStats sources git-svn-id: http://svn.miranda-ng.org/main/trunk@8397 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/!NotAdopted/HistoryStats/main.cpp | 631 ++++++++++++++++++++++++++++++ 1 file changed, 631 insertions(+) create mode 100644 plugins/!NotAdopted/HistoryStats/main.cpp (limited to 'plugins/!NotAdopted/HistoryStats/main.cpp') diff --git a/plugins/!NotAdopted/HistoryStats/main.cpp b/plugins/!NotAdopted/HistoryStats/main.cpp new file mode 100644 index 0000000000..c0f0ac983d --- /dev/null +++ b/plugins/!NotAdopted/HistoryStats/main.cpp @@ -0,0 +1,631 @@ +#include "_globals.h" +#include "main.h" + +#include + +#include "dlgoption.h" +#include "resource.h" +#include "column.h" +#include "bandctrlimpl.h" +#include "optionsctrlimpl.h" +#include "themeapi.h" +#include "iconlib.h" +#include "dlgconfigure.h" + +HINSTANCE g_hInst; + +static const int g_pluginFileListID = MU_DO_BOTH(2534, 2535); +static const MUUID g_MPIDHistoryStatsA = { 0x287dd477, 0xf217, 0x4482, { 0xab, 0x88, 0x60, 0x0d, 0x2e, 0x8d, 0x31, 0x0f } }; +static const MUUID g_MPIDHistoryStatsW = { 0xf184f5a0, 0xc198, 0x4454, { 0xa9, 0xb4, 0xf6, 0xe2, 0xfd, 0x53, 0x41, 0x33 } }; + +const PLUGININFOEX g_pluginInfoEx = { + /* .cbSize = */ sizeof(PLUGININFOEX), + /* .shortName = */ MU_DO_BOTH(muA("HistoryStats"), muA("HistoryStats (Unicode)")), + /* .version = */ PLUGIN_MAKE_VERSION(0, 1, 5, 3), + /* .description = */ muA("Creates nice statistics using your message history.\r\n") + muA("(Requires Miranda IM ") MU_DO_BOTH(muA("0.6.7"), muA("0.6.7 Unicode")) muA(" or above.)"), + /* .author = */ muA("Martin Afanasjew"), + /* .authorEmail = */ muA("miranda@dark-passage.de"), + /* .copyright = */ muA("2005-2007 by Martin Afanasjew (see README for further credits)"), + /* .homepage = */ muA("http://addons.miranda-im.org/details.php?action=viewfile&id=") MU_DO_BOTH(muA("2534"), muA("2535")), + /* .flags = */ MU_DO_BOTH(0, UNICODE_AWARE), + /* .replacesDefaultModule = */ 0, + /* .uuid = */ MU_DO_BOTH(g_MPIDHistoryStatsA, g_MPIDHistoryStatsW), +}; + +const PLUGININFO g_pluginInfo = { + /* .cbSize = */ sizeof(PLUGININFO), + /* .shortName = */ g_pluginInfoEx.shortName, + /* .version = */ g_pluginInfoEx.version, + /* .description = */ g_pluginInfoEx.description, + /* .author = */ g_pluginInfoEx.author, + /* .authorEmail = */ g_pluginInfoEx.authorEmail, + /* .copyright = */ g_pluginInfoEx.copyright, + /* .homepage = */ g_pluginInfoEx.homepage, + /* .flags = */ g_pluginInfoEx.flags, + /* .replacesDefaultModule = */ g_pluginInfoEx.replacesDefaultModule, +}; + +SettingsSerializer* g_pSettings = NULL; + +bool g_bMainMenuExists = false; +bool g_bContactMenuExists = false; +bool g_bExcludeLock = false; +bool g_bConfigureLock = false; + +std::vector g_hRegisteredServices; +std::vector g_hHookedEvents; + +static HANDLE g_hMenuCreateStatistics = NULL; +static HANDLE g_hMenuShowStatistics = NULL; +static HANDLE g_hMenuConfigure = NULL; +static HANDLE g_hMenuToggleExclude = NULL; + +#if defined(HISTORYSTATS_HISTORYCOPY) +static HANDLE g_hMenuHistoryCopy = NULL; +static HANDLE g_hMenuHistoryPaste = NULL; + +static HANDLE g_hHistoryCopyContact = NULL; +#endif + +/* + * services (see m_historystats.h for details) + */ + +static int SvcIsExcluded(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = reinterpret_cast(wParam); + + if (hContact) + { + MirandaSettings db; + + db.setContact(hContact); + db.setModule(con::ModHistoryStats); + + return db.readBool(con::SettExclude, false) ? 1 : 0; + } + + return 0; +} + +static int SvcSetExclude(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = reinterpret_cast(wParam); + + if (hContact) + { + MirandaSettings db; + + db.setContact(hContact); + db.setModule(con::ModHistoryStats); + + if (db.readBool(con::SettExclude, false)) + { + if (!lParam) + { + db.delSetting(con::SettExclude); + } + } + else + { + if (lParam) + { + db.writeBool(con::SettExclude, true); + } + } + } + + return 0; +} + +/* + * global menu stuff + */ + +static void MenuIconsChanged(LPARAM lParam) +{ + if (g_hMenuCreateStatistics) + { + mu::clist::modifyMenuItem(g_hMenuCreateStatistics, CMIM_ICON, NULL, 0, IconLib::getIcon(IconLib::iiMenuCreateStatistics)); + } + + if (g_hMenuShowStatistics) + { + mu::clist::modifyMenuItem(g_hMenuShowStatistics, CMIM_ICON, NULL, 0, IconLib::getIcon(IconLib::iiMenuShowStatistics)); + } + + if (g_hMenuConfigure) + { + mu::clist::modifyMenuItem(g_hMenuConfigure, CMIM_ICON, NULL, 0, IconLib::getIcon(IconLib::iiMenuConfigure)); + } + + if (g_hMenuToggleExclude) + { + mu::clist::modifyMenuItem(g_hMenuToggleExclude, CMIM_ICON, NULL, 0, IconLib::getIcon(IconLib::iiContactMenu)); + } +} + +/* + * main menu related stuff + */ + +static int MenuCreateStatistics(WPARAM wParam, LPARAM lParam) +{ + Statistic::run(*g_pSettings, Statistic::fromMenu, g_hInst); + + return 0; +} + +static int MenuShowStatistics(WPARAM wParam, LPARAM lParam) +{ + if (g_pSettings->canShowStatistics()) + { + g_pSettings->showStatistics(); + } + else + { + MessageBox( + 0, + i18n(muT("The statistics can't be found. Either you never created them or the last created statistics were moved to a different location and can't be found anymore.")), + i18n(muT("HistoryStats - Warning")), + MB_ICONWARNING | MB_OK); + } + + return 0; +} + +static int MenuConfigure(WPARAM wParam, LPARAM lParam) +{ + DlgConfigure::showModal(); + + return 0; +} + +void AddMainMenu() +{ + if (!g_pSettings->m_ShowMainMenu || g_bMainMenuExists) + { + return; + } + + g_bMainMenuExists = true; + + bool bInPopup = g_pSettings->m_ShowMainMenuSub; + + g_hRegisteredServices << mu::_link::createServiceFunction(con::SvcCreateStatistics, MenuCreateStatistics); + g_hRegisteredServices << mu::_link::createServiceFunction(con::SvcShowStatistics , MenuShowStatistics ); + g_hRegisteredServices << mu::_link::createServiceFunction(con::SvcConfigure , MenuConfigure ); + + g_hMenuCreateStatistics = mu::clist::addMainMenuItem( + I18N(muT("Create statistics")), // MEMO: implicit translation + 0, + 1910000000, + IconLib::getIcon(IconLib::iiMenuCreateStatistics), + con::SvcCreateStatistics, + bInPopup ? I18N(muT("Statistics")) : NULL, // MEMO: implicit translation + bInPopup ? 1910000000 : 0); + + g_hMenuShowStatistics = mu::clist::addMainMenuItem( + I18N(muT("Show statistics")), // MEMO: implicit translation + 0, + 1910000001, + IconLib::getIcon(IconLib::iiMenuShowStatistics), + con::SvcShowStatistics, + bInPopup ? I18N(muT("Statistics")) : NULL, // MEMO: implicit translation + bInPopup ? 1910000000 : 0); + + g_hMenuConfigure = mu::clist::addMainMenuItem( + bInPopup ? I18N(muT("Configure...")) : I18N(muT("Configure statistics...")), // MEMO: implicit translation + 0, + 1910000002, + IconLib::getIcon(IconLib::iiMenuConfigure), + con::SvcConfigure, + bInPopup ? I18N(muT("Statistics")) : NULL, // MEMO: implicit translation + bInPopup ? 1910000000 : 0); +} + +/* + * contact menu related stuff + */ + +static int MenuToggleExclude(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = reinterpret_cast(wParam); + + if (hContact) + { + MirandaSettings db; + + db.setContact(hContact); + db.setModule(con::ModHistoryStats); + + if (db.readBool(con::SettExclude, false)) + { + db.delSetting(con::SettExclude); + } + else + { + db.writeBool(con::SettExclude, true); + } + } + + return 0; +} + +#if defined(HISTORYSTATS_HISTORYCOPY) +static int MenuHistoryCopy(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = reinterpret_cast(wParam); + + if (hContact) + { + g_hHistoryCopyContact = hContact; + } + + return 0; +} + +static int MenuHistoryPaste(WPARAM wParam, LPARAM lParam) +{ + HANDLE hTarget = reinterpret_cast(wParam); + + if (!hTarget) + { + return 0; + } + + // ask user if this is really what he wants + ext::string strConfirm = ext::str(ext::kformat(i18n(muT("You're going to copy the complete history of #{source_name} (#{source_proto}) to #{target_name} (#{target_proto}). Afterwards, the target history will contain entries from both histories. There is no way to revert this operation. Be careful! This is a rather big operation and has the potential to damage your database. Be sure to have a backup of this database before performing this operation.\r\n\r\nAre you sure you would like to continue?"))) + % muT("#{source_name}") * mu::clist::getContactDisplayName(g_hHistoryCopyContact) + % muT("#{source_proto}") * utils::fromA(mu::proto::getContactBaseProto(g_hHistoryCopyContact)) + % muT("#{target_name}") * mu::clist::getContactDisplayName(hTarget) + % muT("#{target_proto}") * utils::fromA(mu::proto::getContactBaseProto(hTarget))); + + if (MessageBox(0, strConfirm.c_str(), i18n(muT("HistoryStats - Confirm")), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES) + { + return 0; + } + + // turn off safety mode + mu::db::setSafetyMode(false); + + // copy history + DWORD dwCountSuccess = 0, dwCountFailRead = 0, dwCountFailAdd = 0; + DBEVENTINFO dbe; + int blobBuffer = 4096; + HANDLE hEvent = mu::db_event::findFirst(g_hHistoryCopyContact); + + ZeroMemory(&dbe, sizeof(dbe)); + dbe.cbSize = sizeof(dbe); + dbe.pBlob = reinterpret_cast(malloc(blobBuffer)); + + while (hEvent) + { + dbe.cbBlob = mu::db_event::getBlobSize(hEvent); + + if (blobBuffer < dbe.cbBlob) + { + blobBuffer = 4096 * ((4095 + dbe.cbBlob) / 4096); + dbe.pBlob = reinterpret_cast(realloc(dbe.pBlob, blobBuffer)); + } + + if (mu::db_event::get(hEvent, &dbe) == 0) + { + ++dwCountSuccess; + + // clear "first" flag + dbe.flags &= ~DBEF_FIRST; + + if (mu::db_event::add(hTarget, &dbe) == NULL) + { + ++dwCountFailAdd; + } + } + else + { + ++dwCountFailRead; + } + + hEvent = mu::db_event::findNext(hEvent); + } + + free(dbe.pBlob); + + // turn safety mode back on + mu::db::setSafetyMode(true); + + // output summary + ext::string strSummary = ext::str(ext::kformat(i18n(muT("Successfully read #{success} events of which #{fail_add} couldn't be added to the target history. #{fail} events couldn't be read from the source history."))) + % muT("#{success}") * dwCountSuccess + % muT("#{fail}") * dwCountFailRead + % muT("#{fail_add}") * dwCountFailAdd); + + MessageBox(0, strSummary.c_str(), i18n(muT("HistoryStats - Information")), MB_ICONINFORMATION); + + g_hHistoryCopyContact = NULL; + + return 0; +} +#endif + +static int EventPreBuildContactMenu(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = reinterpret_cast(wParam); + + if (hContact) + { + const mu_ansi* szProto = mu::proto::getContactBaseProto(hContact); + + if ((!g_pSettings->m_ShowContactMenuPseudo && (!szProto || !(mu::protosvc::getCaps(szProto, PFLAGNUM_2) & ~mu::protosvc::getCaps(szProto, PFLAGNUM_5)))) || + g_pSettings->m_HideContactMenuProtos.find(szProto) != g_pSettings->m_HideContactMenuProtos.end()) + { + mu::clist::modifyMenuItem(g_hMenuToggleExclude, CMIM_FLAGS, NULL, CMIF_HIDDEN); + } + else + { + MirandaSettings db; + + db.setContact(hContact); + db.setModule(con::ModHistoryStats); + + int menuState = db.readBool(con::SettExclude, false) ? CMIF_CHECKED : 0; + + // avoid collision with options page + if (g_bExcludeLock) + { + menuState |= CMIF_GRAYED; + } + + // set menu state + mu::clist::modifyMenuItem(g_hMenuToggleExclude, CMIM_FLAGS, NULL, menuState); + } + +#if defined(HISTORYSTATS_HISTORYCOPY) + int menuStateCopy = (g_hHistoryCopyContact && g_hHistoryCopyContact != hContact) ? 0 : CMIF_GRAYED; + + mu::clist::modifyMenuItem(g_hMenuHistoryPaste, CMIM_FLAGS, NULL, menuStateCopy); +#endif + } + + return 0; +} + +void AddContactMenu() +{ + if (!g_pSettings->m_ShowContactMenu || g_bContactMenuExists) + { + return; + } + + g_bContactMenuExists = true; + + g_hRegisteredServices << mu::_link::createServiceFunction(con::SvcToggleExclude, MenuToggleExclude); + + g_hMenuToggleExclude = mu::clist::addContactMenuItem( + I18N(muT("Exclude from statistics")), // MEMO: implicit translation + 0, + 800000, + IconLib::getIcon(IconLib::iiContactMenu), + con::SvcToggleExclude); + +#if defined(HISTORYSTATS_HISTORYCOPY) + g_hRegisteredServices << mu::_link::createServiceFunction(con::SvcHistoryCopy , MenuHistoryCopy ); + g_hRegisteredServices << mu::_link::createServiceFunction(con::SvcHistoryPaste, MenuHistoryPaste); + + g_hMenuHistoryCopy = mu::clist::addContactMenuItem( + I18N(muT("Copy history")), // MEMO: implicit translation + 0, + 800001, + NULL, + con::SvcHistoryCopy); + + g_hMenuHistoryPaste = mu::clist::addContactMenuItem( + I18N(muT("Paste history...")), // MEMO: implicit translation + 0, + 800002, + NULL, + con::SvcHistoryPaste); +#endif + + g_hHookedEvents << mu::_link::hookEvent(ME_CLIST_PREBUILDCONTACTMENU, EventPreBuildContactMenu); +} + +/* + * options integration + */ + +static int EventOptInitialise(WPARAM wParam, LPARAM lParam) +{ + mu::opt::addPage( + wParam, + i18n(muT("History")), + i18n(muT("Statistics")), + NULL, + DlgOption::staticDlgProc, + MAKEINTRESOURCEA(IDD_OPTIONS), + g_hInst); + + return 0; +} + +/* + * second initialization phase + */ + +static int EventModulesLoaded(WPARAM wParam, LPARAM lParam) +{ + // register all known columns + Column::registerColumns(); + + // read settings + g_pSettings = new SettingsSerializer(con::ModHistoryStats); + g_pSettings->readFromDB(); + + // integrate into options dialog + g_hHookedEvents << mu::_link::hookEvent(ME_OPT_INITIALISE, EventOptInitialise); + + // integrate with icolib + IconLib::init(); + IconLib::registerCallback(MenuIconsChanged, 0); + + // register with Updater plugin, if available + if (mu::updater::_available()) + { + mu::updater::registerFL(g_pluginFileListID, &g_pluginInfo); + } + + // integrate into main/contact menu, if selected + AddMainMenu(); + AddContactMenu(); + + // create statistics on startup, if activated + if (g_pSettings->m_OnStartup) + { + Statistic::run(*g_pSettings, Statistic::fromStartup, g_hInst); + } + + return 0; +} + +/* + * external interface + */ + +extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + { + DisableThreadLibraryCalls(hinstDLL); + g_hInst = hinstDLL; + +#if defined(_DEBUG) + // dump memory leak report at end of program + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif + } + break; + } + + return TRUE; +} + +extern "C" __declspec(dllexport) const PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion) +{ + OutputDebugString(muT("HistoryStats: MirandaPluginInfo() was called.\n")); + + // MEMO: (don't) fail, if version is below minimum + return &g_pluginInfo; +} + +extern "C" __declspec(dllexport) const PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion) +{ + OutputDebugString(muT("HistoryStats: MirandaPluginInfoEx() was called.\n")); + + // MEMO: (don't) fail, if version is below minimum + return &g_pluginInfoEx; +} + +extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces() +{ + OutputDebugString(muT("HistoryStats: MirandaPluginInterfaces() was called.\n")); + + static const MUUID interfaces[] = { + MIID_HISTORYSTATS, + MIID_LAST, + }; + + return interfaces; +} + +extern "C" __declspec(dllexport) int Load(PLUGINLINK* link) +{ + // init themeing api + ThemeAPI::init(); + + // init COM, needed for GUID generation + CoInitialize(NULL); + + // register our own window classes + if (!BandCtrlImpl::registerClass() || !OptionsCtrlImpl::registerClass()) + { + MessageBox( + 0, + muT("Failed to register a required window class. Can't continue loading plugin."), + muT("HistoryStats - Error"), + MB_OK | MB_ICONERROR); + + return 1; + } + + // load "mu" system (includes version check) + if (!mu::load(link)) + { + MessageBox( + 0, + muT("This version of HistoryStats isn't compatible with your Miranda IM ") + muT("version. Possibly, your Miranda IM is outdated or you are trying to ") + muT("use the Unicode version with a non-Unicode Miranda IM.\r\n\r\n") + muT("Please go to the plugin's homepage and check the requirements."), + muT("HistoryStats - Error"), + MB_OK | MB_ICONERROR); + + return 1; + } + + // use system's locale to format date/time/numbers + Locale::init(); + + // load rtfconv.dll if available + RTFFilter::init(); + + // init global variables + g_bMainMenuExists = false; + g_bContactMenuExists = false; + + // register provided services + g_hRegisteredServices << mu::_link::createServiceFunction(MS_HISTORYSTATS_ISEXCLUDED, SvcIsExcluded); + g_hRegisteredServices << mu::_link::createServiceFunction(MS_HISTORYSTATS_SETEXCLUDE, SvcSetExclude); + + // hook "modules loaded" to perform further initialization + g_hHookedEvents << mu::_link::hookEvent(ME_SYSTEM_MODULESLOADED, EventModulesLoaded); + + return 0; +} + +extern "C" __declspec(dllexport) int Unload() +{ + // unhook previously hooked events + std::for_each(g_hHookedEvents.begin(), g_hHookedEvents.end(), std::ptr_fun(mu::_link::unhookEvent)); + g_hHookedEvents.clear(); + + // unregister previously registered services + std::for_each(g_hRegisteredServices.begin(), g_hRegisteredServices.end(), std::ptr_fun(mu::_link::destroyServiceFunction)); + g_hRegisteredServices.clear(); + + // free global settings object + delete g_pSettings; + + // uninit iconlib + IconLib::uninit(); + + // free rtfconv.dll if loaded + RTFFilter::uninit(); + + // unload "mu" system + mu::unload(); + + // unregister our own window classes + OptionsCtrlImpl::unregisterClass(); + BandCtrlImpl::unregisterClass(); + + // uninit COM, needed for GUID generation + CoUninitialize(); + + // free themeing api + ThemeAPI::uninit(); + + return 0; +} -- cgit v1.2.3