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("e, SIGNAL(status(QString)), this, SLOT(slotLogStatus(QString))); 0633 connect("e, SIGNAL(error(QString)), this, SLOT(slotLogError(QString))); 0634 connect("e, SIGNAL(failed(QString,QString)), this, SLOT(slotLogFailed(QString,QString))); 0635 connect("e, SIGNAL(quote(QString,QString,QDate,double)), this, 0636 SLOT(slotLogQuote(QString,QString,QDate,double))); 0637 connect("e, 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"