diff options
author | George Hazan <ghazan@miranda.im> | 2019-03-02 12:32:44 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2019-03-02 12:32:55 +0300 |
commit | 931a7dc1ac0dbc7e6c1083583ced915e572f5b47 (patch) | |
tree | 9fe9a6448d44030e26aa7107ce16044ed413e0d0 /protocols/CurrencyRates/src/CurrencyRatesProviderBase.cpp | |
parent | dd7d9954042254e66e3bbbec7195c6be8b1a0663 (diff) |
all protocols (even virtual ones) moved to the Protocols folder
Diffstat (limited to 'protocols/CurrencyRates/src/CurrencyRatesProviderBase.cpp')
-rw-r--r-- | protocols/CurrencyRates/src/CurrencyRatesProviderBase.cpp | 935 |
1 files changed, 935 insertions, 0 deletions
diff --git a/protocols/CurrencyRates/src/CurrencyRatesProviderBase.cpp b/protocols/CurrencyRates/src/CurrencyRatesProviderBase.cpp new file mode 100644 index 0000000000..592fd551ba --- /dev/null +++ b/protocols/CurrencyRates/src/CurrencyRatesProviderBase.cpp @@ -0,0 +1,935 @@ +#include "StdAfx.h" + +extern bool g_bAutoUpdate; +extern HANDLE g_hEventWorkThreadStop; + +struct CXMLFileInfo +{ + CXMLFileInfo() : m_qs(L"Unknown") {} + ICurrencyRatesProvider::CProviderInfo m_pi; + CCurrencyRateSection m_qs; + tstring m_sURL; +}; + +inline tstring get_ini_file_name(LPCTSTR pszFileName) +{ + return CreateFilePath(pszFileName); +} + +bool parse_currencyrate(const TiXmlNode *pTop, CCurrencyRate &q) +{ + tstring sSymbol, sDescription, sID; + + for (auto *pNode : TiXmlEnum(pTop)) { + const char *sName = pNode->Value(); + if (!mir_strcmpi(sName, "symbol")) { + sSymbol = GetNodeText(pNode); + if (sSymbol.empty()) + return false; + } + else if (!mir_strcmpi(sName, "description")) { + sDescription = GetNodeText(pNode); + } + else if (!mir_strcmpi(sName, "id")) { + sID = GetNodeText(pNode); + if (sID.empty()) + return false; + } + } + + q = CCurrencyRate(sID, TranslateW(sSymbol.c_str()), TranslateW(sDescription.c_str())); + return true; +} + +bool parse_section(const TiXmlNode *pTop, CCurrencyRateSection &qs) +{ + CCurrencyRateSection::TSections aSections; + CCurrencyRateSection::TCurrencyRates aCurrencyRates; + tstring sSectionName; + + for (auto *pNode : TiXmlEnum(pTop)) { + const char *sName = pNode->Value(); + if (!mir_strcmpi(sName, "section")) { + CCurrencyRateSection qs1; + if (true == parse_section(pNode, qs1)) + aSections.push_back(qs1); + } + else if (!mir_strcmpi(sName, "currencyrate")) { + CCurrencyRate q; + if (true == parse_currencyrate(pNode, q)) + aCurrencyRates.push_back(q); + } + else if (!mir_strcmpi(sName, "name")) { + sSectionName = GetNodeText(pNode); + if (sSectionName.empty()) + return false; + } + } + + qs = CCurrencyRateSection(TranslateW(sSectionName.c_str()), aSections, aCurrencyRates); + return true; +} + +const TiXmlNode* find_provider(const TiXmlNode *pRoot) +{ + for (auto *pNode : TiXmlEnum(pRoot)) { + const char *sName = pNode->Value(); + if (!mir_strcmpi(sName, "Provider")) + return pNode; + + if (auto *pProvider = find_provider(pNode)) + return pProvider; + } + + return nullptr; +} + +CXMLFileInfo parse_ini_file(const tstring &rsXMLFile, bool &rbSucceded) +{ + CXMLFileInfo res; + CCurrencyRateSection::TSections aSections; + + TiXmlDocument doc; + if (doc.LoadFile(_T2A(rsXMLFile.c_str())) == tinyxml2::XML_SUCCESS) { + const TiXmlNode *pProvider = find_provider(&doc); + if (pProvider) { + rbSucceded = true; + for (auto *pNode : TiXmlEnum(pProvider)) { + const char *sName = pNode->Value(); + if (!mir_strcmpi(sName, "section")) { + CCurrencyRateSection qs; + if (parse_section(pNode, qs)) + aSections.push_back(qs); + } + else if (!mir_strcmpi(sName, "Name")) + res.m_pi.m_sName = GetNodeText(pNode); + else if (!mir_strcmpi(sName, "ref")) + res.m_pi.m_sURL = GetNodeText(pNode); + else if (!mir_strcmpi(sName, "url")) + res.m_sURL = GetNodeText(pNode); + } + } + } + + res.m_qs = CCurrencyRateSection(res.m_pi.m_sName, aSections); + return res; +} + +CXMLFileInfo init_xml_info(LPCTSTR pszFileName, bool& rbSucceded) +{ + rbSucceded = false; + tstring sIniFile = get_ini_file_name(pszFileName); + return parse_ini_file(sIniFile, rbSucceded); +} + +CCurrencyRatesProviderBase::CCurrencyRatesProviderBase() : + m_hEventSettingsChanged(::CreateEvent(nullptr, FALSE, FALSE, nullptr)), + m_hEventRefreshContact(::CreateEvent(nullptr, FALSE, FALSE, nullptr)), + m_bRefreshInProgress(false) +{ +} + +CCurrencyRatesProviderBase::~CCurrencyRatesProviderBase() +{ + delete m_pXMLInfo; + + ::CloseHandle(m_hEventSettingsChanged); + ::CloseHandle(m_hEventRefreshContact); +} + +bool CCurrencyRatesProviderBase::Init() +{ + bool bSucceded = (m_pXMLInfo == nullptr); + if (!m_pXMLInfo) + m_pXMLInfo = new CXMLFileInfo(init_xml_info(DB_DEF_IniFileName, bSucceded)); + + return bSucceded; +} + +const CCurrencyRatesProviderBase::CProviderInfo& CCurrencyRatesProviderBase::GetInfo() const +{ + return m_pXMLInfo->m_pi; +} + +const CCurrencyRateSection& CCurrencyRatesProviderBase::GetCurrencyRates() const +{ + return m_pXMLInfo->m_qs; +} + +const tstring& CCurrencyRatesProviderBase::GetURL() const +{ + return m_pXMLInfo->m_sURL; +} + +bool CCurrencyRatesProviderBase::IsOnline() +{ + return /*g_bAutoUpdate*/true; +} + +void CCurrencyRatesProviderBase::AddContact(MCONTACT hContact) +{ + // CCritSection cs(m_cs); + assert(m_aContacts.end() == std::find(m_aContacts.begin(), m_aContacts.end(), hContact)); + + m_aContacts.push_back(hContact); +} + +void CCurrencyRatesProviderBase::DeleteContact(MCONTACT hContact) +{ + mir_cslock lck(m_cs); + + TContacts::iterator i = std::find(m_aContacts.begin(), m_aContacts.end(), hContact); + if (i != m_aContacts.end()) + m_aContacts.erase(i); +} + +void CCurrencyRatesProviderBase::SetContactStatus(MCONTACT hContact, int nNewStatus) +{ + int nStatus = db_get_w(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_STATUS, ID_STATUS_OFFLINE); + if (nNewStatus != nStatus) { + db_set_w(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_STATUS, nNewStatus); + + if (ID_STATUS_ONLINE != nNewStatus) { + db_unset(hContact, LIST_MODULE_NAME, STATUS_MSG_NAME); + tstring sSymbol = CurrencyRates_DBGetStringW(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_SYMBOL); + if (false == sSymbol.empty()) + db_set_ws(hContact, LIST_MODULE_NAME, CONTACT_LIST_NAME, sSymbol.c_str()); + + SetContactExtraImage(hContact, eiEmpty); + } + } +} + +class CTendency +{ + enum { NumValues = 2 }; + enum EComparison + { + NonValid, + Greater, + Less, + Equal, + GreaterOrEqual, + LessOrEqual + }; + +public: + enum EResult + { + NotChanged, + Up, + Down + }; + +public: + CTendency() : m_nComparison(NonValid) {} + + bool Parse(CCurrencyRatesProviderBase *pProvider, const tstring& rsFrmt, MCONTACT hContact) + { + m_abValueFlags[0] = false; + m_abValueFlags[1] = false; + m_nComparison = NonValid; + bool bValid = true; + int nCurValue = 0; + for (tstring::const_iterator i = rsFrmt.begin(); i != rsFrmt.end() && bValid && nCurValue < NumValues;) { + wchar_t chr = *i; + switch (chr) { + default: + if (false == std::isspace(chr)) + bValid = false; + else + ++i; + break; + + case '%': + ++i; + if (i != rsFrmt.end()) { + wchar_t t = *i; + ++i; + + double d; + bValid = pProvider->ParseSymbol(hContact, t, d); + if (bValid) { + m_adValues[nCurValue] = d; + m_abValueFlags[nCurValue] = true; + ++nCurValue; + } + } + else bValid = false; + break; + case '>': + m_nComparison = Greater; + ++i; + break; + case '<': + m_nComparison = Less; + ++i; + break; + case '=': + switch (m_nComparison) { + default: + bValid = false; + break; + case NonValid: + m_nComparison = Equal; + break; + case Greater: + m_nComparison = GreaterOrEqual; + break; + case Less: + m_nComparison = LessOrEqual; + break; + } + ++i; + break; + } + } + + return (bValid && IsValid()); + } + + bool IsValid() const { return (m_abValueFlags[0] && m_abValueFlags[1] && (m_nComparison != NonValid)); } + + EResult Compare() const + { + switch (m_nComparison) { + case Greater: + if (true == IsWithinAccuracy(m_adValues[0], m_adValues[1])) + return NotChanged; + + if (m_adValues[0] > m_adValues[1]) + return Up; + return Down; + + case GreaterOrEqual: + if ((true == IsWithinAccuracy(m_adValues[0], m_adValues[1])) || (m_adValues[0] > m_adValues[1])) + return Up; + return Down; + + case Less: + if (true == IsWithinAccuracy(m_adValues[0], m_adValues[1])) + return NotChanged; + + if (m_adValues[0] < m_adValues[1]) + return Up; + return Down; + + case LessOrEqual: + if ((true == IsWithinAccuracy(m_adValues[0], m_adValues[1])) || (m_adValues[0] < m_adValues[1])) + return Up; + return Down; + + case Equal: + if (true == IsWithinAccuracy(m_adValues[0], m_adValues[1])) + return Up; + return Down; + } + return NotChanged; + } + +private: + double m_adValues[NumValues]; + bool m_abValueFlags[NumValues]; + EComparison m_nComparison; +}; + +tstring format_rate(const ICurrencyRatesProvider *pProvider, MCONTACT hContact, const tstring &rsFrmt) +{ + tstring sResult; + + for (tstring::const_iterator i = rsFrmt.begin(); i != rsFrmt.end();) { + wchar_t chr = *i; + switch (chr) { + default: + sResult += chr; + ++i; + break; + + case '\\': + ++i; + if (i != rsFrmt.end()) { + wchar_t t = *i; + switch (t) { + case '%': sResult += L"%"; break; + case 't': sResult += L"\t"; break; + case 'n': sResult += L"\n"; break; + case '\\': sResult += L"\\"; break; + default: sResult += chr; sResult += t; break; + } + ++i; + } + else sResult += chr; + break; + + case '%': + ++i; + if (i != rsFrmt.end()) { + chr = *i; + + byte nWidth = 0; + if (::isdigit(chr)) { + nWidth = chr - 0x30; + ++i; + if (i == rsFrmt.end()) { + sResult += chr; + break; + } + else chr = *i; + } + + sResult += pProvider->FormatSymbol(hContact, chr, nWidth); + ++i; + } + else sResult += chr; + break; + } + } + + return sResult; +} + +void log_to_file(const ICurrencyRatesProvider *pProvider, + MCONTACT hContact, + const tstring& rsLogFileName, + const tstring& rsFormat) +{ + CreatePathToFileW(rsLogFileName.c_str()); + + tofstream file(rsLogFileName.c_str(), std::ios::app | std::ios::out); + file.imbue(GetSystemLocale()); + if (file.good()) { + tstring s = format_rate(pProvider, hContact, rsFormat); + file << s; + } +} + +void log_to_history(const ICurrencyRatesProvider *pProvider, + MCONTACT hContact, + time_t nTime, + const tstring& rsFormat) +{ + tstring s = format_rate(pProvider, hContact, rsFormat); + T2Utf psz(s.c_str()); + + DBEVENTINFO dbei = {}; + dbei.szModule = CURRENCYRATES_MODULE_NAME; + dbei.timestamp = static_cast<DWORD>(nTime); + dbei.flags = DBEF_READ | DBEF_UTF; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = (int)::mir_strlen(psz) + 1; + dbei.pBlob = (PBYTE)(char*)psz; + db_event_add(hContact, &dbei); +} + +bool do_set_contact_extra_icon(MCONTACT hContact, const CTendency& tendency) +{ + CTendency::EResult nComparison = tendency.Compare(); + + if (CTendency::NotChanged == nComparison) + return SetContactExtraImage(hContact, eiNotChanged); + + if (CTendency::Up == nComparison) + return SetContactExtraImage(hContact, eiUp); + + if (CTendency::Down == nComparison) + return SetContactExtraImage(hContact, eiDown); + + return false; +} + +bool show_popup(const ICurrencyRatesProvider *pProvider, + MCONTACT hContact, + const CTendency& tendency, + const tstring& rsFormat, + const CPopupSettings& ps) +{ + if (!ServiceExists(MS_POPUP_ADDPOPUPW)) + return false; + + POPUPDATAW ppd; + memset(&ppd, 0, sizeof(ppd)); + ppd.lchContact = hContact; + + if (tendency.IsValid()) { + CTendency::EResult nComparison = tendency.Compare(); + if (CTendency::NotChanged == nComparison) + ppd.lchIcon = CurrencyRates_LoadIconEx(IDI_ICON_NOTCHANGED); + else if (CTendency::Up == nComparison) + ppd.lchIcon = CurrencyRates_LoadIconEx(IDI_ICON_UP); + else if (CTendency::Down == nComparison) + ppd.lchIcon = CurrencyRates_LoadIconEx(IDI_ICON_DOWN); + } + + mir_wstrncpy(ppd.lpwzContactName, pProvider->FormatSymbol(hContact, 's').c_str(), MAX_CONTACTNAME); + { + ptrW ss(variables_parsedup((wchar_t*)rsFormat.c_str(), nullptr, hContact)); + tstring sText = format_rate(pProvider, hContact, tstring(ss)); + mir_wstrncpy(ppd.lpwzText, sText.c_str(), MAX_SECONDLINE); + } + + if (CPopupSettings::colourDefault == ps.GetColourMode()) { + ppd.colorText = CPopupSettings::GetDefColourText(); + ppd.colorBack = CPopupSettings::GetDefColourBk(); + } + else { + ppd.colorText = ps.GetColourText(); + ppd.colorBack = ps.GetColourBk(); + } + + switch (ps.GetDelayMode()) { + default: + assert(!"Unknown popup delay mode"); + case CPopupSettings::delayFromPopup: + ppd.iSeconds = 0; + break; + case CPopupSettings::delayPermanent: + ppd.iSeconds = -1; + break; + case CPopupSettings::delayCustom: + ppd.iSeconds = ps.GetDelayTimeout(); + break; + } + + LPARAM lp = 0; + if (false == ps.GetHistoryFlag()) + lp |= 0x08; + + return (0 == CallService(MS_POPUP_ADDPOPUPW, reinterpret_cast<WPARAM>(&ppd), lp)); +} + +void CCurrencyRatesProviderBase::WriteContactRate(MCONTACT hContact, double dRate, const tstring& rsSymbol/* = ""*/) +{ + time_t nTime = ::time(0); + + if (false == rsSymbol.empty()) + db_set_ws(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_SYMBOL, rsSymbol.c_str()); + + double dPrev = 0.0; + bool bValidPrev = CurrencyRates_DBReadDouble(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_CURR_VALUE, dPrev); + if (true == bValidPrev) + CurrencyRates_DBWriteDouble(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_PREV_VALUE, dPrev); + + CurrencyRates_DBWriteDouble(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_CURR_VALUE, dRate); + db_set_dw(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_FETCH_TIME, nTime); + + tstring sSymbol = rsSymbol; + + tostringstream oNick; + oNick.imbue(GetSystemLocale()); + if (false == m_sContactListFormat.empty()) { + tstring s = format_rate(this, hContact, m_sContactListFormat); + oNick << s; + } + else { + if (true == sSymbol.empty()) + sSymbol = CurrencyRates_DBGetStringW(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_SYMBOL); + + oNick << std::setfill(L' ') << std::setw(10) << std::left << sSymbol << std::setw(6) << std::right << dRate; + } + CTendency tendency; + + if (true == tendency.Parse(this, m_sTendencyFormat, hContact)) + do_set_contact_extra_icon(hContact, tendency); + + db_set_ws(hContact, LIST_MODULE_NAME, CONTACT_LIST_NAME, oNick.str().c_str()); + + tstring sStatusMsg = format_rate(this, hContact, m_sStatusMsgFormat); + if (false == sStatusMsg.empty()) + db_set_ws(hContact, LIST_MODULE_NAME, STATUS_MSG_NAME, sStatusMsg.c_str()); + else + db_unset(hContact, LIST_MODULE_NAME, STATUS_MSG_NAME); + + bool bUseContactSpecific = (db_get_b(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CONTACT_SPEC_SETTINGS, 0) > 0); + + CAdvProviderSettings global_settings(this); + + WORD dwMode = (bUseContactSpecific) + ? db_get_w(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_LOG, static_cast<WORD>(lmDisabled)) + : global_settings.GetLogMode(); + if (dwMode&lmExternalFile) { + bool bAdd = true; + bool bOnlyIfChanged = (bUseContactSpecific) + ? (db_get_w(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_LOG_FILE_CONDITION, 1) > 0) + : global_settings.GetLogOnlyChangedFlag(); + if (true == bOnlyIfChanged) { + bAdd = ((false == bValidPrev) || (false == IsWithinAccuracy(dRate, dPrev))); + } + if (true == bAdd) { + tstring sLogFileName = (bUseContactSpecific) + ? CurrencyRates_DBGetStringW(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_LOG_FILE, global_settings.GetLogFileName().c_str()) + : global_settings.GetLogFileName(); + + if (true == sSymbol.empty()) { + sSymbol = CurrencyRates_DBGetStringW(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_SYMBOL); + } + + sLogFileName = GenerateLogFileName(sLogFileName, sSymbol); + + tstring sFormat = global_settings.GetLogFormat(); + if (bUseContactSpecific) + sFormat = CurrencyRates_DBGetStringW(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_FORMAT_LOG_FILE, DB_DEF_LogFormat); + + log_to_file(this, hContact, sLogFileName, sFormat); + } + } + if (dwMode&lmInternalHistory) { + bool bAdd = true; + bool bOnlyIfChanged = (bUseContactSpecific) + ? (db_get_w(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_HISTORY_CONDITION, 1) > 0) + : global_settings.GetHistoryOnlyChangedFlag(); + + if (true == bOnlyIfChanged) { + bAdd = ((false == bValidPrev) || (false == IsWithinAccuracy(dRate, dPrev))); + } + if (true == bAdd) { + tstring sFormat = (bUseContactSpecific) + ? CurrencyRates_DBGetStringW(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_FORMAT_HISTORY, global_settings.GetHistoryFormat().c_str()) + : global_settings.GetHistoryFormat(); + + log_to_history(this, hContact, nTime, sFormat); + } + } + + if (dwMode&lmPopup) { + bool bOnlyIfChanged = (bUseContactSpecific) + ? (1 == db_get_b(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_POPUP_CONDITION, 1) > 0) + : global_settings.GetShowPopupIfValueChangedFlag(); + if ((false == bOnlyIfChanged) + || ((true == bOnlyIfChanged) && (true == bValidPrev) && (false == IsWithinAccuracy(dRate, dPrev)))) { + tstring sFormat = (bUseContactSpecific) + ? CurrencyRates_DBGetStringW(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_FORMAT_POPUP, global_settings.GetPopupFormat().c_str()) + : global_settings.GetPopupFormat(); + + CPopupSettings ps = *(global_settings.GetPopupSettingsPtr()); + ps.InitForContact(hContact); + show_popup(this, hContact, tendency, sFormat, ps); + } + } + + SetContactStatus(hContact, ID_STATUS_ONLINE); +} + +MCONTACT CCurrencyRatesProviderBase::CreateNewContact(const tstring& rsName) +{ + MCONTACT hContact = db_add_contact(); + if (hContact) { + if (0 == Proto_AddToContact(hContact, CURRENCYRATES_PROTOCOL_NAME)) { + tstring sProvName = GetInfo().m_sName; + db_set_ws(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_PROVIDER, sProvName.c_str()); + db_set_ws(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_SYMBOL, rsName.c_str()); + db_set_ws(hContact, LIST_MODULE_NAME, CONTACT_LIST_NAME, rsName.c_str()); + + mir_cslock lck(m_cs); + m_aContacts.push_back(hContact); + } + else { + db_delete_contact(hContact); + hContact = NULL; + } + } + + return hContact; +} + +DWORD get_refresh_timeout_miliseconds() +{ + if (!g_bAutoUpdate) + return INFINITE; + + int nRefreshRateType = db_get_w(0, CURRENCYRATES_MODULE_NAME, DB_KEY_RefreshRateType, RRT_MINUTES); + if (nRefreshRateType < RRT_SECONDS || nRefreshRateType > RRT_HOURS) + nRefreshRateType = RRT_MINUTES; + + DWORD nTimeout = db_get_w(0, CURRENCYRATES_MODULE_NAME, DB_KEY_RefreshRateValue, 1); + switch (nRefreshRateType) { + default: + case RRT_SECONDS: + if (nTimeout < 1 || nTimeout > 60) + nTimeout = 1; + + nTimeout *= 1000; + break; + case RRT_MINUTES: + if (nTimeout < 1 || nTimeout > 60) + nTimeout = 1; + + nTimeout *= 1000 * 60; + break; + case RRT_HOURS: + if (nTimeout < 1 || nTimeout > 24) + nTimeout = 1; + + nTimeout *= 1000 * 60 * 60; + break; + } + + return nTimeout; +} + +class CBoolGuard +{ +public: + CBoolGuard(bool& rb) : m_b(rb) { m_b = true; } + ~CBoolGuard() { m_b = false; } + +private: + bool m_b; +}; + +void CCurrencyRatesProviderBase::Run() +{ + DWORD nTimeout = get_refresh_timeout_miliseconds(); + m_sContactListFormat = CurrencyRates_DBGetStringW(NULL, CURRENCYRATES_MODULE_NAME, DB_KEY_DisplayNameFormat, DB_DEF_DisplayNameFormat); + m_sStatusMsgFormat = CurrencyRates_DBGetStringW(NULL, CURRENCYRATES_MODULE_NAME, DB_KEY_StatusMsgFormat, DB_DEF_StatusMsgFormat); + m_sTendencyFormat = CurrencyRates_DBGetStringW(NULL, CURRENCYRATES_MODULE_NAME, DB_KEY_TendencyFormat, DB_DEF_TendencyFormat); + + enum + { + STOP_THREAD = 0, + SETTINGS_CHANGED = 1, + REFRESH_CONTACT = 2, + COUNT_SYNC_OBJECTS = 3 + }; + + HANDLE anEvents[COUNT_SYNC_OBJECTS]; + anEvents[STOP_THREAD] = g_hEventWorkThreadStop; + anEvents[SETTINGS_CHANGED] = m_hEventSettingsChanged; + anEvents[REFRESH_CONTACT] = m_hEventRefreshContact; + + TContacts anContacts; + { + mir_cslock lck(m_cs); + anContacts = m_aContacts; + } + + bool bGoToBed = false; + + if (g_bAutoUpdate) { + CBoolGuard bg(m_bRefreshInProgress); + RefreshCurrencyRates(anContacts); + } + + while (false == bGoToBed) { + anContacts.clear(); + + DWORD dwBegin = ::GetTickCount(); + DWORD dwResult = ::WaitForMultipleObjects(COUNT_SYNC_OBJECTS, anEvents, FALSE, nTimeout); + switch (dwResult) { + case WAIT_FAILED: + assert(!"WaitForMultipleObjects failed"); + bGoToBed = true; + break; + + case WAIT_ABANDONED_0 + STOP_THREAD: + case WAIT_ABANDONED_0 + SETTINGS_CHANGED: + case WAIT_ABANDONED_0 + REFRESH_CONTACT: + assert(!"WaitForMultipleObjects abandoned"); + + case WAIT_OBJECT_0 + STOP_THREAD: + bGoToBed = true; + break; + + case WAIT_OBJECT_0 + SETTINGS_CHANGED: + nTimeout = get_refresh_timeout_miliseconds(); + m_sContactListFormat = CurrencyRates_DBGetStringW(NULL, CURRENCYRATES_MODULE_NAME, DB_KEY_DisplayNameFormat, DB_DEF_DisplayNameFormat); + m_sStatusMsgFormat = CurrencyRates_DBGetStringW(NULL, CURRENCYRATES_MODULE_NAME, DB_KEY_StatusMsgFormat, DB_DEF_StatusMsgFormat); + m_sTendencyFormat = CurrencyRates_DBGetStringW(NULL, CURRENCYRATES_MODULE_NAME, DB_KEY_TendencyFormat, DB_DEF_TendencyFormat); + { + mir_cslock lck(m_cs); + anContacts = m_aContacts; + } + break; + + case WAIT_OBJECT_0 + REFRESH_CONTACT: + { + DWORD dwTimeRest = ::GetTickCount() - dwBegin; + if (INFINITE != nTimeout && dwTimeRest < nTimeout) + nTimeout -= dwTimeRest; + + { + mir_cslock lck(m_cs); + anContacts = m_aRefreshingContacts; + m_aRefreshingContacts.clear(); + } + + CBoolGuard bg(m_bRefreshInProgress); + RefreshCurrencyRates(anContacts); + } + break; + + case WAIT_TIMEOUT: + nTimeout = get_refresh_timeout_miliseconds(); + { + mir_cslock lck(m_cs); + anContacts = m_aContacts; + } + { + CBoolGuard bg(m_bRefreshInProgress); + RefreshCurrencyRates(anContacts); + } + break; + + default: + assert(!"What is the hell?"); + } + } + + OnEndRun(); +} + +void CCurrencyRatesProviderBase::OnEndRun() +{ + TContacts anContacts; + { + mir_cslock lck(m_cs); + anContacts = m_aContacts; + m_aRefreshingContacts.clear(); + } + + CBoolGuard bg(m_bRefreshInProgress); + for (auto &it : anContacts) + SetContactStatus(it, ID_STATUS_OFFLINE); +} + +void CCurrencyRatesProviderBase::RefreshSettings() +{ + ::SetEvent(m_hEventSettingsChanged); +} + +void CCurrencyRatesProviderBase::RefreshAllContacts() +{ + { mir_cslock lck(m_cs); + m_aRefreshingContacts.clear(); + for (auto &hContact : m_aContacts) + m_aRefreshingContacts.push_back(hContact); + } + + ::SetEvent(m_hEventRefreshContact); +} + +void CCurrencyRatesProviderBase::RefreshContact(MCONTACT hContact) +{ + { mir_cslock lck(m_cs); + m_aRefreshingContacts.push_back(hContact); + } + + ::SetEvent(m_hEventRefreshContact); +} + +void CCurrencyRatesProviderBase::FillFormat(TFormatSpecificators &array) const +{ + array.push_back(CFormatSpecificator(L"%S", TranslateT("Source of Information"))); + array.push_back(CFormatSpecificator(L"%r", TranslateT("Rate Value"))); + array.push_back(CFormatSpecificator(L"%p", TranslateT("Previous Rate Value"))); + array.push_back(CFormatSpecificator(L"%X", TranslateT("Fetch Time"))); + array.push_back(CFormatSpecificator(L"%x", TranslateT("Fetch Date"))); + array.push_back(CFormatSpecificator(L"%t", TranslateT("Fetch Time and Date"))); + array.push_back(CFormatSpecificator(L"\\%", TranslateT("Percentage Character (%)"))); + array.push_back(CFormatSpecificator(L"\\t", TranslateT("Tabulation"))); + array.push_back(CFormatSpecificator(L"\\\\", TranslateT("Left slash (\\)"))); +} + +bool CCurrencyRatesProviderBase::ParseSymbol(MCONTACT hContact, wchar_t c, double &d) +{ + switch (c) { + case 'r': + case 'R': + return CurrencyRates_DBReadDouble(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_CURR_VALUE, d); + + case 'p': + case 'P': + return CurrencyRates_DBReadDouble(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_PREV_VALUE, d); + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool get_fetch_time(MCONTACT hContact, time_t &rTime) +{ + DBVARIANT dbv; + if (db_get(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_FETCH_TIME, &dbv) || (DBVT_DWORD != dbv.type)) + return false; + + rTime = dbv.dVal; + return true; +} + +static tstring format_fetch_time(MCONTACT hContact, const tstring &rsFormat) +{ + time_t nTime; + if (true == get_fetch_time(hContact, nTime)) { + boost::posix_time::ptime time = boost::date_time::c_local_adjustor<boost::posix_time::ptime>::utc_to_local(boost::posix_time::from_time_t(nTime)); + tostringstream k; + k.imbue(std::locale(GetSystemLocale(), new ttime_facet(rsFormat.c_str()))); + k << time; + return k.str(); + } + + return tstring(); +} + +static tstring format_double(double dValue, int nWidth) +{ + tostringstream o; + o.imbue(GetSystemLocale()); + + if (nWidth > 0 && nWidth <= 9) + o << std::setprecision(nWidth) << std::showpoint << std::fixed; + + o << dValue; + + return o.str(); +} + +tstring CCurrencyRatesProviderBase::FormatSymbol(MCONTACT hContact, wchar_t c, int nWidth) const +{ + tstring ret; + double d = 0.0; + + switch (c) { + case '%': + case '\t': + case '\\': + ret = c; + break; + case 'S': + ret = CurrencyRates_DBGetStringW(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_PROVIDER); + break; + case 's': + ret = CurrencyRates_DBGetStringW(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_SYMBOL); + break; + case 'X': + ret = format_fetch_time(hContact, CurrencyRates_GetTimeFormat(true)); + break; + case 'x': + ret = format_fetch_time(hContact, CurrencyRates_GetDateFormat(true)); + break; + case 't': + { + tstring sFrmt = CurrencyRates_GetDateFormat(true); + sFrmt += L" "; + sFrmt += CurrencyRates_GetTimeFormat(true); + ret = format_fetch_time(hContact, sFrmt); + } + break; + case 'r': + case 'R': + if (true == CurrencyRates_DBReadDouble(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_CURR_VALUE, d)) + ret = format_double(d, nWidth); + else + ret = L"-"; + break; + + case 'p': + case 'P': + if (true == CurrencyRates_DBReadDouble(hContact, CURRENCYRATES_MODULE_NAME, DB_STR_CURRENCYRATE_PREV_VALUE, d)) + ret = format_double(d, nWidth); + else + ret = L"-"; + break; + } + + return ret; +} |