File indexing completed on 2024-04-28 05:02:32

0001 /*
0002     SPDX-FileCopyrightText: 2004-2019 Thomas Baumgart tbaumgart @kde.org
0003 
0004     This file is part of libalkimia.
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "alkonlinequoteswidget.h"
0010 
0011 #include "alknewstuffwidget.h"
0012 #include "alkonlinequote.h"
0013 #include "alkonlinequotesprofile.h"
0014 #include "alkonlinequotesprofilemanager.h"
0015 #include "alkonlinequotesource.h"
0016 #include "alkonlinequoteuploaddialog.h"
0017 #include "alkwebpage.h"
0018 
0019 #include <QCheckBox>
0020 #include <QDesktopServices>
0021 #include <QtDebug>
0022 #include <QTreeWidget>
0023 #include <QKeyEvent>
0024 
0025 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
0026     #include <QIcon>
0027     #include <KIconLoader>
0028     #include <KMessageWidget>
0029     #define KIcon QIcon
0030 #else
0031     #include <KComponentData>
0032     #include <KIcon>
0033     #include <KIconLoader>
0034 #endif
0035 
0036 #include <KGuiItem>
0037 #include <KMessageBox>
0038 
0039 #include <ui_alkonlinequotedetails.h>
0040 #include <ui_alkonlinequotes.h>
0041 #include <ui_alkonlinequotesdebug.h>
0042 #include <ui_alkonlinequotesprofiledetails.h>
0043 #include <ui_alkonlinequotesprofiles.h>
0044 
0045 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
0046 #include <klocale.h>
0047 static KLocale _locale(TRANSLATION_DOMAIN);
0048 #define i18nc(context, text) ki18nc(context, text).toString(&_locale)
0049 #define i18n(text) ki18n(text).toString(&_locale)
0050 #define tr2i18n(text, context) ki18nc(context, text).toString(&_locale)
0051 #endif
0052 
0053 class AlkOnlineQuotesWidget::Private
0054     : public QWidget
0055     , public Ui::AlkOnlineQuoteDetailsWidget
0056     , public Ui::AlkOnlineQuotesDebugWidget
0057     , public Ui::AlkOnlineQuotesProfileDetailsWidget
0058     , public Ui::AlkOnlineQuotesProfilesWidget
0059     , public Ui::AlkOnlineQuotesWidget
0060 {
0061     Q_OBJECT
0062 public:
0063     QString m_acceptLanguage;
0064     QList<AlkOnlineQuoteSource> m_resetList;
0065     AlkOnlineQuoteSource m_currentItem;
0066     bool m_quoteInEditing;
0067     AlkOnlineQuotesProfile *m_profile;
0068     bool m_showProfiles;
0069     bool m_showUpload;
0070     bool m_ghnsEditable;
0071     bool m_disableUpdate;
0072     QPixmap m_emptyIcon;
0073     QPixmap m_inWorkIcon;
0074     QPixmap m_okIcon;
0075     QPixmap m_failIcon;
0076     QPixmap m_unknownIcon;
0077     QDialog *m_webPageDialog;
0078 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
0079     KMessageWidget* m_infoMessage;
0080 #endif
0081 
0082     Private(bool showProfiles, bool showUpload, QWidget *parent);
0083     ~Private();
0084 
0085 public Q_SLOTS:
0086     void slotNewProfile();
0087     void slotDeleteProfile();
0088     void slotSelectProfile();
0089     void slotLoadProfile();
0090 
0091     void slotDeleteEntry();
0092     void slotDuplicateEntry();
0093     void slotUpdateEntry();
0094     void slotLoadQuoteSource();
0095     void slotEntryChanged();
0096     void slotNewEntry();
0097     void slotCheckEntry();
0098     void slotLogStatus(const QString &s);
0099     void slotLogError(const QString &s);
0100     void slotLogFailed(const QString &id, const QString &symbol);
0101     void slotLogQuote(const QString &id, const QString &symbol, const QDate &date, double price);
0102     void slotLogQuotes(const QString &id, const QString &symbol, const AlkDatePriceMap &prices);
0103     void slotQuoteSourceRenamed(QTreeWidgetItem *item, int column);
0104     void slotQuoteSourceStartRename(QTreeWidgetItem *item, int column);
0105     void slotInstallEntries();
0106     void slotUploadEntry();
0107     void slotShowButton();
0108 
0109 public:
0110     void loadProfiles();
0111     void loadQuotesList(const bool updateResetList = false);
0112     void clearIcons();
0113     void initIcons();
0114     void setupIcons(const AlkOnlineQuote::Errors &errors);
0115     QString singleSymbol() const;
0116     QStringList doubleSymbol() const;
0117     QString expandedUrl() const;
0118     void updateButtonState();
0119     bool eventFilter(QObject* o, QEvent* e) override;
0120 };
0121 
0122 AlkOnlineQuotesWidget::Private::Private(bool showProfiles, bool showUpload, QWidget *parent)
0123     : QWidget(parent)
0124     , m_quoteInEditing(false)
0125     , m_profile(nullptr)
0126     , m_showProfiles(showProfiles)
0127     , m_showUpload(showUpload)
0128     , m_ghnsEditable(false)
0129     , m_disableUpdate(false)
0130 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
0131     , m_inWorkIcon(BarIcon("view-refresh"))
0132     , m_okIcon(BarIcon("dialog-ok-apply"))
0133     , m_failIcon(BarIcon("dialog-cancel"))
0134 #else
0135     , m_inWorkIcon(QIcon::fromTheme("view-refresh").pixmap(16))
0136     , m_okIcon(QIcon::fromTheme("dialog-ok-apply").pixmap(16))
0137     , m_failIcon(QIcon::fromTheme("dialog-cancel").pixmap(16))
0138 #endif
0139     , m_webPageDialog(nullptr)
0140 
0141 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
0142     , m_infoMessage(nullptr)
0143 #endif
0144 {
0145 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
0146     static KComponentData alk(TRANSLATION_DOMAIN);
0147 #endif
0148     Ui::AlkOnlineQuoteDetailsWidget::setupUi(parent);
0149     Ui::AlkOnlineQuotesDebugWidget::setupUi(parent);
0150     Ui::AlkOnlineQuotesProfileDetailsWidget::setupUi(parent);
0151     Ui::AlkOnlineQuotesProfilesWidget::setupUi(parent);
0152     Ui::AlkOnlineQuotesWidget::setupUi(parent);
0153 
0154     if (!QString(BUILD_KEY).isEmpty())
0155         m_buildKey->setText(QString("<small>version: %1</small>").arg(BUILD_KEY));
0156     else
0157         m_buildKey->setText(QString());
0158 
0159     profilesGroupBox->setVisible(showProfiles);
0160     profileDetailsBox->setVisible(showProfiles);
0161     m_showButton->setVisible(!showProfiles && AlkOnlineQuotesProfileManager::instance().webPageEnabled());
0162     m_ghnsSource->setEnabled(showProfiles);
0163     m_uploadButton->setVisible(showUpload);
0164     m_urlCheckLabel->setMinimumWidth(m_okIcon.width());
0165 
0166     loadProfiles();
0167 
0168     // TODO move to ui file
0169     KGuiItem updateButtenItem(i18nc("Accepts the entered data and stores it", "&Accept"),
0170                               "dialog-ok",
0171                               i18n("Accepts the entered data and stores it"),
0172                               i18n("Use this to accept the modified data."));
0173 
0174     KGuiItem deleteButtenItem(i18n("&Delete"),
0175                               "edit-delete",
0176                               i18n("Delete the selected source entry"),
0177                               i18n("Use this to delete the selected online source entry"));
0178 
0179     KGuiItem checkButtonItem(i18nc("Check the selected source entry", "&Check Source"),
0180                              "document-edit-verify",
0181                              i18n("Check the selected source entry"),
0182                              i18n("Use this to check the selected online source entry"));
0183 
0184     KGuiItem showButtonItem(i18nc("Show the selected source entry in a web browser", "&Show page"),
0185                             "applications-internet",
0186                             i18n("Show the selected source entry in a web browser"),
0187                             i18n("Use this to show the selected online source entry"));
0188 
0189     KGuiItem newButtenItem(i18nc("Create a new source entry for online quotes", "&New..."),
0190                            "document-new",
0191                            i18n("Create a new source entry for online quotes"),
0192                            i18n("Use this to create a new entry for online quotes"));
0193 
0194 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
0195     KGuiItem::assign(m_updateButton, updateButtenItem);
0196     KGuiItem::assign(m_deleteButton, deleteButtenItem);
0197     KGuiItem::assign(m_checkButton, checkButtonItem);
0198     KGuiItem::assign(m_showButton, showButtonItem);
0199     KGuiItem::assign(m_newButton, newButtenItem);
0200 
0201     m_infoMessage = new KMessageWidget(onlineQuotesGroupBox);
0202     groupBoxLayout->insertWidget(0, m_infoMessage);
0203     m_infoMessage->hide();
0204 #else
0205     m_updateButton->setGuiItem(updateButtenItem);
0206     m_deleteButton->setGuiItem(deleteButtenItem);
0207     m_checkButton->setGuiItem(checkButtonItem);
0208     m_showButton->setGuiItem(showButtonItem);
0209     m_newButton->setGuiItem(newButtenItem);
0210 #endif
0211 
0212     connect(m_newProfile, SIGNAL(clicked()), this, SLOT(slotNewProfile()));
0213     connect(m_deleteProfile, SIGNAL(clicked()), this, SLOT(slotDeleteProfile()));
0214     connect(m_profileList, SIGNAL(itemSelectionChanged()), this, SLOT(slotLoadProfile()));
0215 
0216     connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(slotLoadQuoteSource()));
0217     connect(m_updateButton, SIGNAL(clicked()), this, SLOT(slotUpdateEntry()));
0218     connect(m_newButton, SIGNAL(clicked()), this, SLOT(slotNewEntry()));
0219     connect(m_checkButton, SIGNAL(clicked()), this, SLOT(slotCheckEntry()));
0220     connect(m_deleteButton, SIGNAL(clicked()), this, SLOT(slotDeleteEntry()));
0221     connect(m_duplicateButton, SIGNAL(clicked()), this, SLOT(slotDuplicateEntry()));
0222     connect(m_installButton, SIGNAL(clicked()), this, SLOT(slotInstallEntries()));
0223     connect(m_uploadButton, SIGNAL(clicked()), this, SLOT(slotUploadEntry()));
0224 
0225     m_quoteSourceList->setColumnCount(2);
0226     m_quoteSourceList->setHeaderLabels(QStringList() << i18n("Name") << i18n("Source"));
0227     m_quoteSourceList->setRootIsDecorated(false);
0228     m_quoteSourceList->header()->resizeSection(0, 5);
0229     m_quoteSourceList->header()->setStretchLastSection(false);
0230 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
0231     m_quoteSourceList->header()->setSectionResizeMode(0,QHeaderView::Stretch);
0232 #else
0233     m_quoteSourceList->header()->setResizeMode(0,QHeaderView::Stretch);
0234 #endif
0235     m_quoteSourceList->setSortingEnabled(true);
0236     m_quoteSourceList->installEventFilter(this);
0237 
0238     connect(m_quoteSourceList, SIGNAL(itemSelectionChanged()), this, SLOT(slotLoadQuoteSource()));
0239     connect(m_quoteSourceList, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this,
0240             SLOT(slotQuoteSourceRenamed(QTreeWidgetItem*,int)));
0241     connect(m_quoteSourceList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this,
0242             SLOT(slotQuoteSourceStartRename(QTreeWidgetItem*,int)));
0243 
0244     connect(m_editURL, SIGNAL(textChanged(QString)), this, SLOT(slotEntryChanged()));
0245     connect(m_editIdentifier, SIGNAL(textChanged(QString)), this, SLOT(slotEntryChanged()));
0246 
0247     m_editIdSelector->addItem(i18nc("@item:inlistbox Stock", "Symbol"), AlkOnlineQuoteSource::IdSelector::Symbol);
0248     m_editIdSelector->addItem(i18nc("@item:inlistbox Stock", "Identification number"), AlkOnlineQuoteSource::IdSelector::IdentificationNumber);
0249     m_editIdSelector->addItem(i18nc("@item:inlistbox Stock", "Name"), AlkOnlineQuoteSource::IdSelector::Name);
0250     connect(m_editIdSelector, SIGNAL(currentIndexChanged(int)), this,  SLOT(slotEntryChanged()));
0251 
0252     connect(m_editDate, SIGNAL(textChanged(QString)), this, SLOT(slotEntryChanged()));
0253     connect(m_editDateFormat, SIGNAL(textChanged(QString)), this, SLOT(slotEntryChanged()));
0254     connect(m_editDefaultId, SIGNAL(textChanged(QString)), this, SLOT(slotEntryChanged()));
0255     connect(m_editPrice, SIGNAL(textChanged(QString)), this, SLOT(slotEntryChanged()));
0256 
0257     m_editDataFormat->addItem(i18nc("@item:inlistbox Stock", "Stripped HTML"), AlkOnlineQuoteSource::DataFormat::StrippedHTML);
0258     m_editDataFormat->addItem(i18nc("@item:inlistbox Stock", "HTML"), AlkOnlineQuoteSource::DataFormat::HTML);
0259     m_editDataFormat->addItem(i18nc("@item:inlistbox Stock", "CSV"), AlkOnlineQuoteSource::DataFormat::CSV);
0260     connect(m_editDataFormat, SIGNAL(currentIndexChanged(int)), this, SLOT(slotEntryChanged()));
0261 
0262     connect(m_ghnsSource, SIGNAL(toggled(bool)), this, SLOT(slotEntryChanged()));
0263     connect(m_showButton, SIGNAL(clicked()), this, SLOT(slotShowButton()));
0264 
0265     m_updateButton->setEnabled(false);
0266     slotLoadProfile();
0267 }
0268 
0269 AlkOnlineQuotesWidget::Private::~Private()
0270 {
0271     m_webPageDialog->deleteLater();
0272 }
0273 
0274 void AlkOnlineQuotesWidget::Private::loadProfiles()
0275 {
0276     AlkOnlineQuotesProfileList list = AlkOnlineQuotesProfileManager::instance().profiles();
0277     if (list.isEmpty())
0278         return;
0279     for (AlkOnlineQuotesProfile *profile : list) {
0280         QListWidgetItem *item = new QListWidgetItem(dynamic_cast<QListWidget *>(m_profileList));
0281         item->setText(profile->name());
0282         item->setFlags(item->flags() | Qt::ItemIsEditable);
0283     }
0284     m_profileList->setCurrentRow(0);
0285     m_profile = AlkOnlineQuotesProfileManager::instance().profiles().first();
0286     loadQuotesList(true);
0287 }
0288 
0289 QString sourceTypeString(AlkOnlineQuoteSource &source)
0290 {
0291     if (source.isGHNS()) {
0292         if (source.profile()->GHNSFilePath(source.name()).isEmpty())
0293             return i18n("Remote unpublished");
0294         else
0295             return i18n("Remote");
0296     } else if (source.isFinanceQuote())
0297         return i18n("Finance::Quote");
0298     return i18n("Local");
0299 }
0300 
0301 void AlkOnlineQuotesWidget::Private::loadQuotesList(const bool updateResetList)
0302 {
0303     m_quoteInEditing = false;
0304     QStringList groups = m_profile->quoteSources();
0305 
0306     if (updateResetList) {
0307         m_resetList.clear();
0308     }
0309     m_quoteSourceList->blockSignals(true);
0310     m_quoteSourceList->clear();
0311     QStringList::Iterator it;
0312     for (it = groups.begin(); it != groups.end(); ++it) {
0313         AlkOnlineQuoteSource source(*it, m_profile);
0314         if (!source.isValid()) {
0315             continue;
0316         }
0317 
0318         QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << *it << sourceTypeString(source));
0319         Qt::ItemFlag editFlag = source.isGHNS() ? Qt::NoItemFlags : Qt::ItemIsEditable;
0320         item->setFlags(editFlag | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
0321         m_quoteSourceList->addTopLevelItem(item);
0322         if (updateResetList) {
0323             m_resetList += source;
0324         }
0325     }
0326     m_quoteSourceList->sortItems(0, Qt::AscendingOrder);
0327 
0328     QTreeWidgetItem *item = nullptr;
0329     if (!m_currentItem.name().isEmpty()) {
0330         QList<QTreeWidgetItem*> items = m_quoteSourceList->findItems(m_currentItem.name(), Qt::MatchExactly);
0331         if (items.size() > 0)
0332             item = items.at(0);
0333         if (item)
0334             m_quoteSourceList->setCurrentItem(item);
0335     }
0336     if (!item) {
0337         item = m_quoteSourceList->topLevelItem(0);
0338         if (item)
0339             m_quoteSourceList->setCurrentItem(item);
0340     }
0341     m_quoteSourceList->blockSignals(false);
0342     slotLoadQuoteSource();
0343     updateButtonState();
0344 }
0345 
0346 void AlkOnlineQuotesWidget::Private::slotNewProfile()
0347 {
0348     QTreeWidgetItem *item = new QTreeWidgetItem(dynamic_cast<QTreeWidget *>(m_profileList));
0349     item->setText(0, QLatin1String("new profile"));
0350     item->setFlags(item->flags() | Qt::ItemIsEditable);
0351 }
0352 
0353 void AlkOnlineQuotesWidget::Private::slotDeleteProfile()
0354 {
0355     delete m_profileList->currentItem();
0356 }
0357 
0358 void AlkOnlineQuotesWidget::Private::slotSelectProfile()
0359 {
0360     slotLoadProfile();
0361 }
0362 
0363 void AlkOnlineQuotesWidget::Private::slotLoadProfile()
0364 {
0365     const AlkOnlineQuotesProfileList list = AlkOnlineQuotesProfileManager::instance().profiles();
0366     if (!m_showProfiles) {
0367         if (list.isEmpty())
0368             return;
0369         m_profile = list.first();
0370         m_installButton->setVisible(m_profile->hasGHNSSupport());
0371         loadQuotesList(true);
0372         return;
0373     }
0374 
0375     for (AlkOnlineQuotesProfile *profile : list) {
0376         if (m_profileList->currentItem() && m_profileList->currentItem()->text() == profile->name()) {
0377             m_profile = profile;
0378             loadQuotesList(true);
0379             m_installButton->setVisible(profile->hasGHNSSupport());
0380             break;
0381         }
0382     }
0383 
0384     bool visible = m_profile->type() != AlkOnlineQuotesProfile::Type::None;
0385     m_configFilePath->setText(m_profile->kConfigFile());
0386     m_configFilePath->setVisible(visible);
0387     m_configLabel->setEnabled(visible);
0388 
0389     visible = m_profile->hasGHNSSupport();
0390     m_GHNSConfigFilePath->setText(m_profile->hotNewStuffConfigFile());
0391     m_GHNSConfigFilePath->setVisible(visible);
0392     m_GHNSConfigLabel->setEnabled(visible);
0393 
0394     m_GHNSDataPath->setText(m_profile->hotNewStuffReadPath().join(" "));
0395     m_GHNSDataPath->setVisible(visible);
0396     m_GHNSDataLabel->setEnabled(visible);
0397 }
0398 
0399 void AlkOnlineQuotesWidget::Private::slotLoadQuoteSource()
0400 {
0401     m_quoteInEditing = false;
0402 
0403     m_disableUpdate = true;
0404     m_editURL->clear();
0405     m_editIdentifier->clear();
0406     m_editIdSelector->setCurrentIndex(AlkOnlineQuoteSource::IdSelector::Symbol);
0407     m_editPrice->clear();
0408     m_editDate->clear();
0409     m_editDateFormat->clear();
0410     m_editDefaultId->clear();
0411 
0412     QTreeWidgetItem *item = m_quoteSourceList->currentItem();
0413     if (item) {
0414         m_currentItem = AlkOnlineQuoteSource(item->text(0), m_profile);
0415         m_editURL->setText(m_currentItem.url());
0416         m_editIdentifier->setText(m_currentItem.idRegex());
0417         m_editIdSelector->setCurrentIndex(m_currentItem.idSelector());
0418         m_editPrice->setText(m_currentItem.priceRegex());
0419         m_editDataFormat->setCurrentIndex(m_currentItem.dataFormat());
0420         m_editDate->setText(m_currentItem.dateRegex());
0421         m_editDateFormat->setText(m_currentItem.dateFormat());
0422         m_editDefaultId->setText(m_currentItem.defaultId());
0423         m_ghnsSource->setChecked(m_currentItem.isGHNS());
0424     }
0425 
0426     bool isFinanceQuoteSource = (item && AlkOnlineQuoteSource::isFinanceQuote(item->text(0))) ||
0427             m_profile->type() == AlkOnlineQuotesProfile::Type::Script;
0428     bool enabled = item;
0429     if (isFinanceQuoteSource || (m_currentItem.isGHNS() && !m_ghnsEditable))
0430         enabled = false;
0431 
0432     m_editURL->setEnabled(enabled);
0433     m_editIdentifier->setEnabled(enabled);
0434     m_editIdSelector->setEnabled(enabled);
0435     m_editPrice->setEnabled(enabled);
0436     m_editDate->setEnabled(enabled);
0437     m_editDateFormat->setEnabled(enabled);
0438     m_editDefaultId->setEnabled(enabled);
0439     m_ghnsSource->setEnabled(enabled);
0440     m_editDataFormat->setEnabled(enabled);
0441 
0442     m_disableUpdate = false;
0443 
0444     updateButtonState();
0445 }
0446 
0447 void AlkOnlineQuotesWidget::Private::slotEntryChanged()
0448 {
0449     if (!m_disableUpdate)
0450         updateButtonState();
0451 }
0452 
0453 void AlkOnlineQuotesWidget::Private::updateButtonState()
0454 {
0455     clearIcons();
0456     bool modified = m_editURL->text() != m_currentItem.url()
0457                     || m_editIdentifier->text() != m_currentItem.idRegex()
0458                     || m_editIdSelector->currentIndex() != static_cast<int>(m_currentItem.idSelector())
0459                     || m_editDataFormat->currentIndex() != static_cast<int>(m_currentItem.dataFormat())
0460                     || m_editDate->text() != m_currentItem.dateRegex()
0461                     || m_editDateFormat->text() != m_currentItem.dateFormat()
0462                     || m_editDefaultId->text() != m_currentItem.defaultId()
0463                     || m_editPrice->text() != m_currentItem.priceRegex()
0464                     || m_ghnsSource->isChecked() != m_currentItem.isGHNS();
0465 
0466     bool isFinanceQuote = m_currentItem.isFinanceQuote() || m_profile->type() == AlkOnlineQuotesProfile::Type::Script;
0467     bool hasWriteSupport = (m_profile->type() != AlkOnlineQuotesProfile::Type::None && !isFinanceQuote) || m_profile->hasGHNSSupport();
0468     m_newButton->setEnabled(hasWriteSupport);
0469     m_cancelButton->setEnabled(modified);
0470     m_duplicateButton->setEnabled(hasWriteSupport);
0471     m_deleteButton->setEnabled(!m_currentItem.isReadOnly() && !m_currentItem.isGHNS());
0472     m_uploadButton->setEnabled(m_profile->hasGHNSSupport() && m_currentItem.isGHNS() && AlkOnlineQuoteUploadDialog::isSupported());
0473     m_updateButton->setEnabled(modified);
0474     m_checkButton->setEnabled(isFinanceQuote || !modified);
0475     m_checkSymbol->setEnabled(!m_currentItem.url().contains("%2"));
0476     m_checkSymbol2->setEnabled(m_currentItem.url().contains("%2"));
0477     m_editIdSelector->setVisible(m_profile->type() == AlkOnlineQuotesProfile::Type::KMyMoney5);
0478     m_editIdSelectorLabel->setVisible(m_profile->type() == AlkOnlineQuotesProfile::Type::KMyMoney5);
0479     if (m_currentItem.requiresTwoIdentifier()) {
0480         m_checkSymbol->setText("");
0481         m_checkSymbol2->setText(!m_currentItem.defaultId().isEmpty() ? m_currentItem.defaultId() : "BTC GBP");
0482     } else {
0483         m_checkSymbol->setText(!m_currentItem.defaultId().isEmpty() ? m_currentItem.defaultId() : "ORCL");
0484         m_checkSymbol2->setText("");
0485     }
0486     bool isCSVSource = m_currentItem.dataFormat() == AlkOnlineQuoteSource::CSV;
0487     m_startDateLabel->setVisible(isCSVSource);
0488     m_endDateLabel->setVisible(isCSVSource);
0489     m_startDateEdit->setVisible(isCSVSource);
0490     m_endDateEdit->setVisible(isCSVSource);
0491 }
0492 
0493 void AlkOnlineQuotesWidget::Private::slotDeleteEntry()
0494 {
0495     QList<QTreeWidgetItem *> items = m_quoteSourceList->findItems(
0496         m_currentItem.name(), Qt::MatchExactly);
0497     if (items.isEmpty()) {
0498         return;
0499     }
0500     QTreeWidgetItem *item = items.at(0);
0501     if (!item) {
0502         return;
0503     }
0504 
0505     int ret = KMessageBox::warningContinueCancel(this,
0506                                                  i18n("Are you sure to delete this online quote ?"),
0507                                                  i18n("Delete online quote"),
0508                                                  KStandardGuiItem::cont(),
0509                                                  KStandardGuiItem::cancel(),
0510                                                  QString("DeletingOnlineQuote"));
0511     if (ret == KMessageBox::Cancel) {
0512         return;
0513     }
0514 
0515     // keep this order to avoid deleting the wrong current item
0516     m_currentItem.remove();
0517     delete item;
0518     updateButtonState();
0519 }
0520 
0521 void AlkOnlineQuotesWidget::Private::slotDuplicateEntry()
0522 {
0523     QList<QTreeWidgetItem *> items = m_quoteSourceList->findItems(
0524         m_currentItem.name(), Qt::MatchExactly);
0525     if (items.isEmpty()) {
0526         return;
0527     }
0528     QTreeWidgetItem *item = items.at(0);
0529     if (!item) {
0530         return;
0531     }
0532 
0533     AlkOnlineQuoteSource copy(m_currentItem);
0534     copy.setName(copy.name() + i18n(".copy"));
0535     copy.setGHNS(false);
0536     copy.write();
0537     m_currentItem = copy;
0538     loadQuotesList();
0539 }
0540 
0541 void AlkOnlineQuotesWidget::Private::slotUpdateEntry()
0542 {
0543     m_currentItem.setUrl(m_editURL->text());
0544     m_currentItem.setIdRegex(m_editIdentifier->text());
0545     m_currentItem.setIdSelector(static_cast<AlkOnlineQuoteSource::IdSelector>(m_editIdSelector->currentIndex()));
0546     m_currentItem.setDataFormat(static_cast<AlkOnlineQuoteSource::DataFormat>(m_editDataFormat->currentIndex()));
0547     m_currentItem.setDateRegex(m_editDate->text());
0548     m_currentItem.setDateFormat(m_editDateFormat->text());
0549     m_currentItem.setDefaultId(m_editDefaultId->text());
0550     m_currentItem.setPriceRegex(m_editPrice->text());
0551     m_currentItem.setGHNS(m_ghnsSource->isChecked());
0552     m_currentItem.write();
0553     m_checkButton->setEnabled(true);
0554     loadQuotesList();
0555     updateButtonState();
0556 }
0557 
0558 void AlkOnlineQuotesWidget::Private::slotNewEntry()
0559 {
0560     const auto newEntries = m_quoteSourceList->findItems(i18n("New Quote Source"), Qt::MatchExactly);
0561     if (newEntries.isEmpty()) {
0562         AlkOnlineQuoteSource newSource(i18n("New Quote Source"), m_profile);
0563         newSource.write();
0564         m_currentItem = newSource;
0565         loadQuotesList();
0566     } else {
0567 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
0568         if (!m_infoMessage->isVisible() && !m_infoMessage->isShowAnimationRunning()) {
0569             m_infoMessage->resize(width(), m_infoMessage->heightForWidth(width()));
0570             m_infoMessage->setText(
0571                 i18nc("@info Detail that only one new entry can exist at any time", "<b>New Quote Source</b> already exists."));
0572             m_infoMessage->setMessageType(KMessageWidget::Information);
0573             m_infoMessage->animatedShow();
0574         }
0575 #endif
0576         const auto item = newEntries.at(0);
0577         m_quoteSourceList->setCurrentItem(item);
0578         m_quoteSourceList->scrollToItem(item);
0579     }
0580 }
0581 
0582 void AlkOnlineQuotesWidget::Private::clearIcons()
0583 {
0584     m_urlCheckLabel->setPixmap(m_emptyIcon);
0585     m_dateCheckLabel->setPixmap(m_emptyIcon);
0586     m_priceCheckLabel->setPixmap(m_emptyIcon);
0587     m_symbolCheckLabel->setPixmap(m_emptyIcon);
0588     m_dateFormatCheckLabel->setPixmap(m_emptyIcon);
0589 }
0590 
0591 void AlkOnlineQuotesWidget::Private::initIcons()
0592 {
0593     m_urlCheckLabel->setPixmap(m_inWorkIcon);
0594     m_dateCheckLabel->setPixmap(m_inWorkIcon);
0595     m_priceCheckLabel->setPixmap(m_inWorkIcon);
0596     m_symbolCheckLabel->setPixmap(m_inWorkIcon);
0597     m_dateFormatCheckLabel->setPixmap(m_inWorkIcon);
0598 }
0599 
0600 void AlkOnlineQuotesWidget::Private::setupIcons(const AlkOnlineQuote::Errors &errors)
0601 {
0602     clearIcons();
0603     if (errors & AlkOnlineQuote::Errors::URL) {
0604         m_urlCheckLabel->setPixmap(m_failIcon);
0605     } else {
0606         m_urlCheckLabel->setPixmap(m_okIcon);
0607         m_symbolCheckLabel->setPixmap((errors & AlkOnlineQuote::Errors::Symbol) ? m_failIcon : m_okIcon);
0608         m_priceCheckLabel->setPixmap((errors & AlkOnlineQuote::Errors::Price) ? m_failIcon : m_okIcon);
0609         if (errors & AlkOnlineQuote::Errors::Date) {
0610             m_dateCheckLabel->setPixmap(m_failIcon);
0611         } else {
0612             if (m_currentItem.dateRegex().isEmpty()) {
0613                 m_dateCheckLabel->setPixmap(m_emptyIcon);
0614                 m_dateFormatCheckLabel->setPixmap(m_emptyIcon);
0615             } else {
0616                 m_dateCheckLabel->setPixmap(m_okIcon);
0617                 m_dateFormatCheckLabel->setPixmap(
0618                     (errors & AlkOnlineQuote::Errors::DateFormat) ? m_failIcon : m_okIcon);
0619             }
0620         }
0621     }
0622 }
0623 
0624 void AlkOnlineQuotesWidget::Private::slotCheckEntry()
0625 {
0626     AlkOnlineQuote quote(m_profile);
0627     m_logWindow->setVisible(true);
0628     m_logWindow->clear();
0629     clearIcons();
0630     quote.setAcceptLanguage(m_acceptLanguage);
0631 
0632     connect(&quote, SIGNAL(status(QString)), this, SLOT(slotLogStatus(QString)));
0633     connect(&quote, SIGNAL(error(QString)), this, SLOT(slotLogError(QString)));
0634     connect(&quote, SIGNAL(failed(QString,QString)), this, SLOT(slotLogFailed(QString,QString)));
0635     connect(&quote, SIGNAL(quote(QString,QString,QDate,double)), this,
0636             SLOT(slotLogQuote(QString,QString,QDate,double)));
0637     connect(&quote, SIGNAL(quotes(QString,QString,AlkDatePriceMap)), this,
0638             SLOT(slotLogQuotes(QString,QString,AlkDatePriceMap)));
0639     initIcons();
0640     if (m_currentItem.dataFormat() == AlkOnlineQuoteSource::CSV) {
0641         quote.setDateRange(m_startDateEdit->date(), m_endDateEdit->date());
0642     } else {
0643         quote.setDateRange(QDate(), QDate());
0644     }
0645     if (m_currentItem.url().contains("%2")) {
0646         quote.launch(m_checkSymbol2->text(), m_checkSymbol2->text(), m_currentItem.name());
0647     } else {
0648         quote.launch(m_checkSymbol->text(), m_checkSymbol->text(), m_currentItem.name());
0649     }
0650     setupIcons(quote.errors());
0651 }
0652 
0653 void AlkOnlineQuotesWidget::Private::slotLogStatus(const QString &s)
0654 {
0655     m_logWindow->append(s);
0656 }
0657 
0658 void AlkOnlineQuotesWidget::Private::slotLogError(const QString &s)
0659 {
0660     slotLogStatus(QString("<font color=\"red\"><b>") + s + QString("</b></font>"));
0661 }
0662 
0663 void AlkOnlineQuotesWidget::Private::slotLogFailed(const QString &id, const QString &symbol)
0664 {
0665     slotLogStatus(QString("%1 %2").arg(id, symbol));
0666 }
0667 
0668 void AlkOnlineQuotesWidget::Private::slotLogQuote(const QString &id, const QString &symbol,
0669                                          const QDate &date, double price)
0670 {
0671     slotLogStatus(QString("<font color=\"green\">%1 %2 %3 %4</font>").arg(id, symbol,
0672                                                                           date.toString()).arg(
0673                       price));
0674 }
0675 
0676 void AlkOnlineQuotesWidget::Private::slotLogQuotes(const QString &id, const QString &symbol,
0677                                          const AlkDatePriceMap &prices)
0678 {
0679     slotLogStatus(QString("<font color=\"green\">%1 %2</font>").arg(id, symbol));
0680 
0681     slotLogStatus(QString("<font color=\"green\">date price</font>"));
0682     for (auto i = prices.constBegin(), end = prices.constEnd(); i != end; ++i) {
0683         slotLogStatus(QString("<font color=\"green\">%1 %2</font>")
0684                       .arg(i.key().toString(Qt::ISODate)).arg(i.value().toDouble()));
0685     }
0686 }
0687 
0688 void AlkOnlineQuotesWidget::Private::slotQuoteSourceStartRename(QTreeWidgetItem *item, int column)
0689 {
0690     Q_UNUSED(column);
0691 
0692     m_quoteInEditing = true;
0693     m_quoteSourceList->editItem(item);
0694 }
0695 
0696 void AlkOnlineQuotesWidget::Private::slotQuoteSourceRenamed(QTreeWidgetItem *item, int column)
0697 {
0698     Q_UNUSED(column);
0699     //if there is no current item selected, exit
0700     if (m_quoteInEditing == false || !m_quoteSourceList->currentItem()
0701         || item != m_quoteSourceList->currentItem()) {
0702         // revert name change
0703         item->setText(0, m_currentItem.name());
0704         return;
0705     }
0706 
0707     m_quoteInEditing = false;
0708     QString text = item->text(0);
0709     int nameCount = 0;
0710     for (int i = 0; i < m_quoteSourceList->topLevelItemCount(); ++i) {
0711         if (m_quoteSourceList->topLevelItem(i)->text(0) == text) {
0712             ++nameCount;
0713         }
0714     }
0715 
0716     // Make sure we get a non-empty and unique name
0717     if (text.length() > 0 && nameCount == 1) {
0718         m_currentItem.rename(text);
0719     } else {
0720         // revert name change
0721         item->setText(0, m_currentItem.name());
0722     }
0723     m_quoteSourceList->sortItems(0, Qt::AscendingOrder);
0724     updateButtonState();
0725 }
0726 
0727 void AlkOnlineQuotesWidget::Private::slotInstallEntries()
0728 {
0729     QString configFile = m_profile->hotNewStuffConfigFile();
0730 
0731     AlkNewStuffWidget engine;
0732     engine.init(configFile);
0733     if (engine.showInstallDialog()) {
0734         loadQuotesList();
0735     }
0736 }
0737 
0738 void AlkOnlineQuotesWidget::Private::slotUploadEntry()
0739 {
0740     QPointer<AlkOnlineQuoteUploadDialog> dialog = new AlkOnlineQuoteUploadDialog(m_currentItem, false, this);
0741     dialog->exec();
0742     delete dialog;
0743 }
0744 
0745 void AlkOnlineQuotesWidget::Private::slotShowButton()
0746 {
0747     if (!m_webPageDialog) {
0748         m_webPageDialog = new QDialog;
0749         m_webPageDialog->setWindowTitle(i18n("Online Quote HTML Result Window"));
0750         QVBoxLayout *layout = new QVBoxLayout;
0751 #ifdef BUILD_WITH_WEBENGINE
0752         AlkWebPage::setWebInspectorEnabled(true);
0753 #endif
0754         AlkWebPage *webPage = AlkOnlineQuotesProfileManager::instance().webPage();
0755 #ifndef BUILD_WITH_WEBENGINE
0756         webPage->setWebInspectorEnabled(true);
0757 #endif
0758         layout->addWidget(webPage->widget());
0759         m_webPageDialog->setLayout(layout);
0760     }
0761     m_webPageDialog->show();
0762 }
0763 
0764 QString AlkOnlineQuotesWidget::Private::expandedUrl() const
0765 {
0766     if (m_currentItem.url().contains("%2")) {
0767         return m_currentItem.url().arg(m_checkSymbol2->text());
0768     } else {
0769         return m_currentItem.url().arg(m_checkSymbol->text());
0770     }
0771 }
0772 
0773 bool AlkOnlineQuotesWidget::Private::eventFilter(QObject* o, QEvent* e)
0774 {
0775     if (o == m_quoteSourceList && e->type() == QEvent::KeyRelease) {
0776         QKeyEvent *k = dynamic_cast<QKeyEvent*>(e);
0777 #ifdef Q_OS_DARWIN
0778         if (k->key() == Qt::Key_Enter) {
0779 #else
0780         if (k->key() == Qt::Key_F2) {
0781 #endif
0782             slotQuoteSourceStartRename(m_quoteSourceList->currentItem(), 0);
0783         }
0784     }
0785     return false;
0786 }
0787 
0788 AlkOnlineQuotesWidget::AlkOnlineQuotesWidget(bool showProfiles, bool showUpload, QWidget *parent)
0789     : QWidget(parent)
0790     , d(new Private(showProfiles, showUpload, this))
0791 {
0792 }
0793 
0794 AlkOnlineQuotesWidget::~AlkOnlineQuotesWidget()
0795 {
0796     delete d;
0797 }
0798 
0799 QWidget *AlkOnlineQuotesWidget::profilesWidget()
0800 {
0801     QFrame *frame = new QFrame;
0802     frame->setLayout(d->profilesGroupBox->layout());
0803     return frame;
0804 }
0805 
0806 QWidget *AlkOnlineQuotesWidget::profileDetailsWidget()
0807 {
0808     QFrame *frame = new QFrame;
0809     frame->setLayout(d->profileDetailsBox->layout());
0810     return frame;
0811 }
0812 
0813 QWidget *AlkOnlineQuotesWidget::onlineQuotesWidget()
0814 {
0815     QFrame *frame = new QFrame;
0816     frame->setLayout(d->onlineQuotesGroupBox->layout());
0817     return frame;
0818 }
0819 
0820 QWidget *AlkOnlineQuotesWidget::quoteDetailsWidget()
0821 {
0822     QFrame *frame = new QFrame;
0823     frame->setLayout(d->detailsGroupBox->layout());
0824     return frame;
0825 }
0826 
0827 QWidget *AlkOnlineQuotesWidget::debugWidget()
0828 {
0829     QFrame *frame = new QFrame;
0830     frame->setLayout(d->debugGroupBox->layout());
0831     return frame;
0832 }
0833 
0834 void AlkOnlineQuotesWidget::readConfig()
0835 {
0836 }
0837 
0838 void AlkOnlineQuotesWidget::writeConfig()
0839 {
0840 }
0841 
0842 void AlkOnlineQuotesWidget::resetConfig()
0843 {
0844     QStringList::ConstIterator it;
0845     QStringList groups = d->m_profile->quoteSources();
0846 
0847     // delete all currently defined entries
0848     for (it = groups.constBegin(); it != groups.constEnd(); ++it) {
0849         AlkOnlineQuoteSource(*it, d->m_profile).remove();
0850     }
0851 
0852     // and write back the one's from the reset list
0853     QList<AlkOnlineQuoteSource>::iterator itr;
0854     for (itr = d->m_resetList.begin(); itr != d->m_resetList.end(); ++itr) {
0855         (*itr).write();
0856     }
0857 
0858     d->loadQuotesList();
0859 }
0860 
0861 QString AlkOnlineQuotesWidget::acceptLanguage() const
0862 {
0863     return d->m_acceptLanguage;
0864 }
0865 
0866 void AlkOnlineQuotesWidget::setAcceptLanguage(const QString &text)
0867 {
0868     d->m_acceptLanguage = text;
0869 }
0870 
0871 bool AlkOnlineQuotesWidget::GHNSSourceEditable()
0872 {
0873     return d->m_ghnsEditable;
0874 
0875 }
0876 
0877 void AlkOnlineQuotesWidget::setGHNSSourceEditable(bool state)
0878 {
0879     d->m_ghnsEditable = state;
0880 }
0881 
0882 #include "alkonlinequoteswidget.moc"