/*
Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "stdafx.h"
/////////////////////////////////////////////////////////////////////////////////////////
// Pair adding dialog
class CPairAddDialog : public CDlgBase
{
CCtrlCombo cmbFrom, cmbTo;
CCtrlButton btnOk;
public:
CCurrencyRatesProviderBase::TRateInfo ri;
CPairAddDialog() :
CDlgBase(g_plugin, IDD_ADD_PAIR),
btnOk(this, IDOK),
cmbTo(this, IDC_COMBO_CONVERT_INTO),
cmbFrom(this, IDC_COMBO_CONVERT_FROM)
{
cmbTo.OnSelChanged = cmbFrom.OnSelChanged = Callback(this, &CPairAddDialog::onSelChange_From);
}
bool OnInitDialog() override
{
auto &rSection = g_pCurrentProvider->GetSection();
for (auto &it : rSection.GetCurrencyRates()) {
CMStringW sName = it.MakeName();
cmbFrom.AddString(TranslateW(sName), LPARAM(&it));
cmbTo.AddString(TranslateW(sName), LPARAM(&it));
}
return true;
}
bool OnApply() override
{
auto *c1 = (CCurrencyRate*)cmbFrom.GetCurData();
auto *c2 = (CCurrencyRate *)cmbTo.GetCurData();
if (c1 && c2) {
ri.first = *c1;
ri.second = *c2;
}
return true;
}
void onSelChange_From(CCtrlCombo *)
{
int nFrom = cmbFrom.GetCurSel();
int nTo = cmbTo.GetCurSel();
btnOk.Enable((CB_ERR != nFrom) && (CB_ERR != nTo) && (nFrom != nTo));
}
};
/////////////////////////////////////////////////////////////////////////////////////////
// General options dialog
class COptionsDlg : public CDlgBase
{
CMStringW make_contact_name(const CMStringW &rsSymbolFrom, const CMStringW &rsSymbolTo)
{
return rsSymbolFrom + L"/" + rsSymbolTo;
};
CMStringW make_rate_name(const CCurrencyRatesProviderBase::TRateInfo &ri)
{
if ((false == ri.first.GetName().IsEmpty()) && (false == ri.second.GetName().IsEmpty()))
return make_contact_name(ri.first.GetName(), ri.second.GetName());
return make_contact_name(ri.first.GetSymbol(), ri.second.GetSymbol());
};
using TWatchedRates = std::vector;
TWatchedRates g_aWatchedRates;
CCurrencyRatesProviderBase *m_pProvider;
CCtrlEdit edtKey;
CCtrlCombo cmbProvider, cmbRefresh;
CCtrlButton btnAdd, btnRemove, btnDescr, btnGetKey;
CCtrlListBox m_list;
public:
COptionsDlg() :
CDlgBase(g_plugin, IDD_OPTIONS_GENERAL),
m_pProvider(g_pCurrentProvider),
m_list(this, IDC_LIST_RATES),
cmbRefresh(this, IDC_COMBO_REFRESH_RATE),
cmbProvider(this, IDC_PROVIDER),
edtKey(this, IDC_EDIT_PERSONAL_KEY),
btnAdd(this, IDC_BUTTON_ADD),
btnDescr(this, IDC_BUTTON_DESCRIPTION),
btnGetKey(this, IDC_GET_KEY),
btnRemove(this, IDC_BUTTON_REMOVE)
{
CreateLink(edtKey, DB_KEY_ApiKey, L"");
btnAdd.OnClick = Callback(this, &COptionsDlg::onClick_Add);
btnDescr.OnClick = Callback(this, &COptionsDlg::onClick_Descr);
btnGetKey.OnClick = Callback(this, &COptionsDlg::onClick_GetKey);
btnRemove.OnClick = Callback(this, &COptionsDlg::onClick_Remove);
m_list.OnSelChange = Callback(this, &COptionsDlg::onSelChange_Rates);
cmbRefresh.OnSelChanged = Callback(this, &COptionsDlg::onSelChange_Refresh);
cmbProvider.OnSelChanged = Callback(this, &COptionsDlg::onSelChange_Provider);
}
bool OnInitDialog() override
{
for (auto &it : g_apProviders) {
int idx = cmbProvider.AddString(it->GetInfo().m_sName, (LPARAM)it);
if (it == g_pCurrentProvider)
cmbProvider.SetCurSel(idx);
}
// set contact list display format
::SetDlgItemTextW(m_hwnd, IDC_EDIT_CONTACT_LIST_FORMAT, g_plugin.getMStringW(DB_KEY_DisplayNameFormat, DB_DEF_DisplayNameFormat));
// set status message display format
::SetDlgItemTextW(m_hwnd, IDC_EDIT_STATUS_MESSAGE_FORMAT, g_plugin.getMStringW(DB_KEY_StatusMsgFormat, DB_DEF_StatusMsgFormat));
// set tendency format
::SetDlgItemTextW(m_hwnd, IDC_EDIT_TENDENCY_FORMAT, g_plugin.getMStringW(DB_KEY_TendencyFormat, DB_DEF_TendencyFormat));
// refresh rate
cmbRefresh.AddString(TranslateT("Seconds"));
cmbRefresh.AddString(TranslateT("Minutes"));
cmbRefresh.AddString(TranslateT("Hours"));
int nRefreshRateType = g_plugin.getWord(DB_KEY_RefreshRateType, RRT_MINUTES);
if (nRefreshRateType < RRT_SECONDS || nRefreshRateType > RRT_HOURS)
nRefreshRateType = RRT_MINUTES;
UINT nRate = g_plugin.getWord(DB_KEY_RefreshRateValue, 1);
switch (nRefreshRateType) {
case RRT_SECONDS:
case RRT_MINUTES:
if (nRate < 1 || nRate > 60)
nRate = 1;
spin_set_range(::GetDlgItem(m_hwnd, IDC_SPIN_REFRESH_RATE), 1, 60);
break;
case RRT_HOURS:
if (nRate < 1 || nRate > 24)
nRate = 1;
spin_set_range(::GetDlgItem(m_hwnd, IDC_SPIN_REFRESH_RATE), 1, 24);
break;
}
cmbRefresh.SetCurSel(nRefreshRateType);
::SetDlgItemInt(m_hwnd, IDC_EDIT_REFRESH_RATE, nRate, FALSE);
g_aWatchedRates.clear();
for (auto &cc : Contacts(MODULENAME)) {
CCurrencyRatesProviderBase::TRateInfo ri;
if (true == m_pProvider->GetWatchedRateInfo(cc, ri)) {
g_aWatchedRates.push_back(ri);
CMStringW sRate = make_rate_name(ri);
m_list.AddString(sRate);
}
}
btnRemove.Disable();
return true;
}
bool OnApply() override
{
BOOL bOk = FALSE;
UINT nRefreshRate = ::GetDlgItemInt(m_hwnd, IDC_EDIT_REFRESH_RATE, &bOk, FALSE);
g_plugin.setWord(DB_KEY_RefreshRateType, cmbRefresh.GetCurSel());
g_plugin.setWord(DB_KEY_RefreshRateValue, nRefreshRate);
g_plugin.setWString(DB_KEY_DisplayNameFormat, get_window_text(::GetDlgItem(m_hwnd, IDC_EDIT_CONTACT_LIST_FORMAT)));
g_plugin.setWString(DB_KEY_StatusMsgFormat, get_window_text(::GetDlgItem(m_hwnd, IDC_EDIT_STATUS_MESSAGE_FORMAT)));
g_plugin.setWString(DB_KEY_TendencyFormat, get_window_text(::GetDlgItem(m_hwnd, IDC_EDIT_TENDENCY_FORMAT)));
TWatchedRates aTemp(g_aWatchedRates);
TWatchedRates aRemove;
for (auto &cc : Contacts(MODULENAME)) {
CCurrencyRatesProviderBase::TRateInfo ri;
if (true == m_pProvider->GetWatchedRateInfo(cc, ri)) {
auto it = std::find_if(aTemp.begin(), aTemp.end(), [&ri](const auto &other)->bool
{
return ((0 == mir_wstrcmpi(ri.first.GetID(), other.first.GetID()))
&& ((0 == mir_wstrcmpi(ri.second.GetID(), other.second.GetID()))));
});
if (it == aTemp.end()) {
aRemove.push_back(ri);
}
else {
aTemp.erase(it);
}
}
}
for (auto &it : aRemove)
m_pProvider->WatchForRate(it, false);
for (auto &it : aTemp)
m_pProvider->WatchForRate(it, true);
m_pProvider->RefreshSettings();
g_pCurrentProvider = (CCurrencyRatesProviderBase *)cmbProvider.GetCurData();
g_plugin.setWString(DB_STR_PROVIDER, g_pCurrentProvider->GetInfo().m_sName);
return true;
}
void onSelChange_Rates(CCtrlListBox *)
{
btnRemove.Enable(LB_ERR != m_list.GetCurSel());
}
void onSelChange_Provider(CCtrlCombo *)
{
auto *pProvider = (CCurrencyRatesProviderBase *)cmbProvider.GetCurData();
bool bEnabled = pProvider->HasAuth();
edtKey.Enable(bEnabled);
btnGetKey.Enable(bEnabled);
}
void onSelChange_Refresh(CCtrlCombo *)
{
switch (cmbRefresh.GetCurSel()) {
case RRT_SECONDS:
case RRT_MINUTES:
spin_set_range(::GetDlgItem(m_hwnd, IDC_SPIN_REFRESH_RATE), 1, 60);
break;
case RRT_HOURS:
spin_set_range(::GetDlgItem(m_hwnd, IDC_SPIN_REFRESH_RATE), 1, 24);
BOOL bOk = FALSE;
UINT nRefreshRate = ::GetDlgItemInt(m_hwnd, IDC_EDIT_REFRESH_RATE, &bOk, FALSE);
if (TRUE == bOk && nRefreshRate > 24)
::SetDlgItemInt(m_hwnd, IDC_EDIT_REFRESH_RATE, 24, FALSE);
break;
}
}
void onClick_Add(CCtrlButton *)
{
CPairAddDialog dlg;
dlg.SetParent(m_hwnd);
if (dlg.DoModal() == IDOK) {
g_aWatchedRates.push_back(dlg.ri);
CMStringW sRate = make_rate_name(dlg.ri);
m_list.AddString(sRate);
NotifyChange();
}
}
void onClick_Descr(CCtrlButton *)
{
show_variable_list(m_hwnd, m_pProvider);
}
void onClick_GetKey(CCtrlButton *)
{
if (auto *pProvider = (CCurrencyRatesProviderBase *)cmbProvider.GetCurData())
Utils_OpenUrlW(pProvider->GetInfo().m_sURL);
}
void onClick_Remove(CCtrlButton *)
{
int nSel = m_list.GetCurSel();
if (LB_ERR != nSel) {
m_list.DeleteString(nSel);
if (nSel < static_cast(g_aWatchedRates.size())) {
g_aWatchedRates.erase(g_aWatchedRates.begin() + nSel);
NotifyChange();
}
}
nSel = m_list.GetCurSel();
btnRemove.Enable(LB_ERR != nSel);
}
};
/////////////////////////////////////////////////////////////////////////////////////////
// Advanced settings dialog
void select_log_file(HWND hDlg);
void update_file_controls(HWND hDlg);
void update_history_controls(HWND hDlg);
class CAdvancedOptDlg : public CDlgBase
{
CCtrlEdit edtHistoryFormat, edtLogFilename, edtLogFormat;
CCtrlCheck chkExternalFile, chkHistory, chkHistoryCond, chkLogFileCond;
CCtrlButton btnBrowse, btnDescr1, btnDescr2;
public:
CAdvancedOptDlg() :
CDlgBase(g_plugin, IDD_OPTIONS_ADVANCED),
btnDescr1(this, IDC_BUTTON_HISTORY_DESCRIPTION),
btnDescr2(this, IDC_BUTTON_LOG_FILE_DESCRIPTION),
btnBrowse(this, IDC_BUTTON_BROWSE),
chkHistory(this, IDC_CHECK_INTERNAL_HISTORY),
chkHistoryCond(this, IDC_CHECK_HISTORY_CONDITION),
chkLogFileCond(this, IDC_CHECK_LOG_FILE_CONDITION),
chkExternalFile(this, IDC_CHECK_EXTERNAL_FILE),
edtLogFormat(this, IDC_EDIT_LOG_FILE_FORMAT),
edtLogFilename(this, IDC_EDIT_FILE_NAME),
edtHistoryFormat(this, IDC_EDIT_HISTORY_FORMAT)
{
CreateLink(edtLogFormat, g_plugin.wszLogFileFormat);
CreateLink(edtLogFilename, g_plugin.wszLogFileName);
CreateLink(edtHistoryFormat, g_plugin.wszHistoryFormat);
CreateLink(chkHistoryCond, g_plugin.bIsOnlyChangedHistory);
CreateLink(chkLogFileCond, g_plugin.bIsOnlyChangedLogFile);
btnDescr1.OnClick = btnDescr2.OnClick = Callback(this, &CAdvancedOptDlg::onClick_Description);
btnBrowse.OnClick = Callback(this, &CAdvancedOptDlg::onClick_Browse);
chkHistory.OnChange = Callback(this, &CAdvancedOptDlg::onChange_History);
chkExternalFile.OnChange = Callback(this, &CAdvancedOptDlg::onChange_ExternalFile);
}
bool OnInitDialog() override
{
// log to history
uint16_t dwLogMode = g_plugin.wLogMode;
chkHistory.SetState((dwLogMode & lmInternalHistory) != 0);
// log to file
chkExternalFile.SetState((dwLogMode & lmExternalFile) != 0);
return true;
}
bool OnApply() override
{
uint16_t nLogMode = lmDisabled;
if (chkExternalFile.IsChecked()) nLogMode |= lmExternalFile;
if (chkHistory.IsChecked()) nLogMode |= lmInternalHistory;
bool bOk = true;
ptrW sLogFile(edtLogFilename.GetText());
ptrW sLogFileFormat(edtLogFormat.GetText());
if ((nLogMode & lmExternalFile)) {
if (!mir_wstrlen(sLogFile)) {
prepare_edit_ctrl_for_error(edtLogFilename.GetHwnd());
CurrencyRates_MessageBox(m_hwnd, TranslateT("Enter log file name."), MB_OK | MB_ICONERROR);
bOk = false;
}
else if (!mir_wstrlen(sLogFileFormat)) {
prepare_edit_ctrl_for_error(edtLogFormat.GetHwnd());
CurrencyRates_MessageBox(m_hwnd, TranslateT("Enter log file format."), MB_OK | MB_ICONERROR);
bOk = false;
}
}
ptrW sHistoryFormat(edtHistoryFormat.GetText());
if ((true == bOk) && (nLogMode & lmInternalHistory) && (!mir_wstrlen(sHistoryFormat))) {
prepare_edit_ctrl_for_error(edtHistoryFormat.GetHwnd());
CurrencyRates_MessageBox(m_hwnd, TranslateT("Enter history format."), MB_OK | MB_ICONERROR);
bOk = false;
}
if (!bOk)
return false;
g_plugin.wLogMode = nLogMode;
return true;
}
void onClick_Description(CCtrlButton *)
{
show_variable_list(m_hwnd, g_pCurrentProvider);
}
void onChange_ExternalFile(CCtrlCheck *)
{
update_file_controls(m_hwnd);
}
void onChange_History(CCtrlCheck *)
{
update_history_controls(m_hwnd);
}
void onClick_Browse(CCtrlButton *)
{
select_log_file(m_hwnd);
}
};
/////////////////////////////////////////////////////////////////////////////////////////
// Popup options
class CPopupOptDlg : public CDlgBase
{
CCtrlEdit edtPopupFormat, edtDelay;
CCtrlColor clrBack, clrText;
CCtrlCheck chkPopup, chkChanged, chkHistory;
CCtrlButton btnPreview;
public:
CPopupOptDlg() :
CDlgBase(g_plugin, IDD_OPTIONS_POPUP),
clrBack(this, IDC_BGCOLOR),
clrText(this, IDC_TEXTCOLOR),
chkPopup(this, IDC_CHECK_SHOW_POPUP),
chkHistory(this, IDC_CHECK_DONT_USE_POPUPHISTORY),
chkChanged(this, IDC_CHECK_SHOW_POPUP_ONLY_VALUE_CHANGED),
edtDelay(this, IDC_DELAY),
edtPopupFormat(this, IDC_EDIT_POPUP_FORMAT),
btnPreview(this, IDC_PREV)
{
CreateLink(chkPopup, g_plugin.bUsePopups);
CreateLink(chkHistory, g_plugin.bUseHistory);
CreateLink(chkChanged, g_plugin.bShowPopupIfValueChanged);
CreateLink(clrBack, g_plugin.rgbBkg);
CreateLink(clrText, g_plugin.rgbText);
CreateLink(edtDelay, g_plugin.wDelay);
CreateLink(edtPopupFormat, g_plugin.wszPopupFormat);
chkPopup.OnChange = Callback(this, &CPopupOptDlg::onChange_Popup);
btnPreview.OnClick = Callback(this, &CPopupOptDlg::onClick_Preview);
}
bool OnInitDialog() override
{
::CheckRadioButton(m_hwnd, IDC_RADIO_DEFAULT_COLOURS, IDC_RADIO_USER_DEFINED_COLOURS, (colourDefault == g_plugin.modeColour) ? IDC_RADIO_DEFAULT_COLOURS : IDC_RADIO_USER_DEFINED_COLOURS);
UINT n;
switch (g_plugin.modeDelay) {
case delayFromPopup:
n = IDC_DELAYFROMPU;
break;
case delayCustom:
n = IDC_DELAYCUSTOM;
break;
case delayPermanent:
default:
n = IDC_DELAYPERMANENT;
break;
}
::CheckRadioButton(m_hwnd, IDC_DELAYFROMPU, IDC_DELAYPERMANENT, n);
return true;
}
bool OnApply() override
{
uint8_t nModeDelay;
if (1 == ::IsDlgButtonChecked(m_hwnd, IDC_DELAYFROMPU))
nModeDelay = delayFromPopup;
else if (1 == ::IsDlgButtonChecked(m_hwnd, IDC_DELAYCUSTOM))
nModeDelay = delayCustom;
else
nModeDelay = delayPermanent;
g_plugin.modeDelay = nModeDelay;
if (1 == ::IsDlgButtonChecked(m_hwnd, IDC_RADIO_DEFAULT_COLOURS))
g_plugin.modeColour = colourDefault;
else
g_plugin.modeColour = colourUserDefined;
return true;
}
void OnChange() override
{
bool bIsColoursEnabled = 1 == IsDlgButtonChecked(m_hwnd, IDC_RADIO_USER_DEFINED_COLOURS);
::EnableWindow(::GetDlgItem(m_hwnd, IDC_BGCOLOR), bIsColoursEnabled);
::EnableWindow(::GetDlgItem(m_hwnd, IDC_TEXTCOLOR), bIsColoursEnabled);
bool bIsDelayEnabled = 1 == IsDlgButtonChecked(m_hwnd, IDC_DELAYCUSTOM);
::EnableWindow(::GetDlgItem(m_hwnd, IDC_DELAY), bIsDelayEnabled);
}
void onChange_Popup(CCtrlCheck *)
{
bool bEnable = chkPopup.IsChecked();
::EnableWindow(::GetDlgItem(m_hwnd, IDC_EDIT_POPUP_FORMAT), bEnable);
::EnableWindow(::GetDlgItem(m_hwnd, IDC_CHECK_SHOW_POPUP_ONLY_VALUE_CHANGED), bEnable);
::EnableWindow(::GetDlgItem(m_hwnd, IDC_STATIC_POPUP_FORMAT), bEnable);
::EnableWindow(::GetDlgItem(m_hwnd, IDC_BUTTON_POPUP_FORMAT_DESCRIPTION), bEnable);
}
void onClick_Preview(CCtrlButton *)
{
show_popup(0, 0, 1, L"");
}
};
/////////////////////////////////////////////////////////////////////////////////////////
// Module entry point
int CurrencyRatesEventFunc_OptInitialise(WPARAM wp, LPARAM)
{
OPTIONSDIALOGPAGE odp = {};
odp.position = 910000000;
odp.szTitle.a = LPGEN("Currency Rates");
odp.szGroup.a = LPGEN("Network");
odp.flags = ODPF_USERINFOTAB;
odp.pDialog = new COptionsDlg();
odp.szTab.a = LPGEN("General");
g_plugin.addOptions(wp, &odp);
odp.pDialog = new CAdvancedOptDlg();
odp.szTab.a = LPGEN("Advanced");
g_plugin.addOptions(wp, &odp);
odp.position = 910000002;
odp.szGroup.a = LPGEN("Popups");
odp.szTab.a = nullptr;
odp.pDialog = new CPopupOptDlg();
g_plugin.addOptions(wp, &odp);
return 0;
}