#include "./dbquerywidget.h" #include "./tagedit.h" #include "./tageditorwidget.h" #include "../application/knownfieldmodel.h" #include "../application/settings.h" #include "../dbquery/dbquery.h" #include "../misc/utility.h" #include "resources/config.h" #include "ui_dbquerywidget.h" #include #include #include #include #include #include #include #include #include #include #include #ifndef QT_NO_CLIPBOARD #include #endif #include using namespace std; using namespace std::placeholders; using namespace CppUtilities; using namespace QtUtilities; using namespace Settings; using namespace Utility; using namespace TagParser; namespace QtGui { DbQueryWidget::DbQueryWidget(TagEditorWidget *tagEditorWidget, QWidget *parent) : QWidget(parent) , m_ui(new Ui::DbQueryWidget) , m_tagEditorWidget(tagEditorWidget) , m_model(nullptr) , m_coverIndex(-1) , m_lyricsIndex(-1) , m_menu(new QMenu(parent)) , m_insertPresentDataAction(nullptr) , m_searchMusicBrainzAction(nullptr) , m_searchLyricsWikiaAction(nullptr) , m_searchMakeItPersonalAction(nullptr) , m_searchTekstowoAction(nullptr) , m_lastSearchAction(nullptr) , m_refreshAutomaticallyAction(nullptr) { m_ui->setupUi(this); updateStyleSheet(); m_ui->notificationLabel->setText(tr("Search hasn't been started")); m_ui->notificationLabel->setContext(tr("MusicBrainz/LyricsWikia notifications")); m_ui->notificationLabel->setMaxLineCount(5); m_ui->searchGroupBox->installEventFilter(this); // initialize buttons m_ui->abortPushButton->setIcon(style()->standardIcon(QStyle::SP_DialogCancelButton, nullptr, m_ui->abortPushButton)); m_ui->abortPushButton->setVisible(false); m_ui->applyPushButton->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton, nullptr, m_ui->applyPushButton)); // initialize fields model m_ui->fieldsListView->setModel(&values().dbQuery.fields); // initialize search terms form insertSearchTermsFromTagEdit(m_tagEditorWidget->activeTagEdit()); // restore settings m_ui->overrideCheckBox->setChecked(values().dbQuery.override); // setup menu const auto searchIcon = QIcon::fromTheme(QStringLiteral("search")); const auto enableLegacyProvider = qEnvironmentVariableIntValue(PROJECT_VARNAME_UPPER "_ENABLE_LEGACY_METADATA_PROVIDERS"); m_menu->setTitle(tr("New search")); m_menu->setIcon(searchIcon); m_searchMusicBrainzAction = m_lastSearchAction = m_menu->addAction(tr("Query MusicBrainz")); m_searchMusicBrainzAction->setIcon(searchIcon); m_searchMusicBrainzAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_M)); connect(m_searchMusicBrainzAction, &QAction::triggered, this, &DbQueryWidget::searchMusicBrainz); if (enableLegacyProvider) { m_searchLyricsWikiaAction = m_menu->addAction(tr("Query LyricsWikia")); m_searchLyricsWikiaAction->setIcon(searchIcon); m_searchLyricsWikiaAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_L)); connect(m_searchLyricsWikiaAction, &QAction::triggered, this, &DbQueryWidget::searchLyricsWikia); m_searchMakeItPersonalAction = m_menu->addAction(tr("Query makeitpersonal")); m_searchMakeItPersonalAction->setIcon(searchIcon); m_searchMakeItPersonalAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_K)); connect(m_searchMakeItPersonalAction, &QAction::triggered, this, &DbQueryWidget::searchMakeItPersonal); } m_searchTekstowoAction = m_menu->addAction(tr("Query Tekstowo")); m_searchTekstowoAction->setIcon(searchIcon); m_searchTekstowoAction->setShortcut(QKeySequence(Qt::CTRL, Qt::Key_T)); connect(m_searchTekstowoAction, &QAction::triggered, this, &DbQueryWidget::searchTekstowo); m_menu->addSeparator(); m_insertPresentDataAction = m_menu->addAction(tr("Use present data as search criteria")); m_insertPresentDataAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); m_insertPresentDataAction->setEnabled(m_tagEditorWidget->activeTagEdit()); connect(m_insertPresentDataAction, &QAction::triggered, this, &DbQueryWidget::insertSearchTermsFromActiveTagEdit); m_refreshAutomaticallyAction = m_menu->addAction(tr("Start new query automatically when inserting search criteria")); m_refreshAutomaticallyAction->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); m_refreshAutomaticallyAction->setCheckable(true); auto *const clearSearchCriteria = m_menu->addAction(tr("Clear search criteria")); clearSearchCriteria->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear"))); connect(clearSearchCriteria, &QAction::triggered, this, &DbQueryWidget::clearSearchCriteria); m_ui->searchPushButton->setMenu(m_menu); // ensure fieldsGroupBox takes only minimal space (initially) m_ui->splitter->setSizes({ 1, 1000 }); // connect signals and slots connect(m_ui->abortPushButton, &QPushButton::clicked, this, &DbQueryWidget::abortSearch); connect(m_ui->resultsTreeView, &QTreeView::doubleClicked, this, &DbQueryWidget::applySpecifiedResults); connect(m_ui->applyPushButton, &QPushButton::clicked, this, &DbQueryWidget::applySelectedResults); connect(m_tagEditorWidget, &TagEditorWidget::fileStatusChanged, this, &DbQueryWidget::fileStatusChanged); connect(m_ui->resultsTreeView, &QTreeView::customContextMenuRequested, this, &DbQueryWidget::showResultsContextMenu); } DbQueryWidget::~DbQueryWidget() { values().dbQuery.override = m_ui->overrideCheckBox->isChecked(); } void DbQueryWidget::insertSearchTermsFromTagEdit(TagEdit *tagEdit) { if (!tagEdit) { return; } auto somethingChanged = false; auto lyricsCentricProvider = (m_lastSearchAction == m_searchTekstowoAction) || (m_searchMakeItPersonalAction && m_lastSearchAction == m_searchMakeItPersonalAction); // set album and artist if (!lyricsCentricProvider) { const auto newAlbum = tagValueToQString(tagEdit->value(KnownField::Album)); if (m_ui->albumLineEdit->text() != newAlbum) { m_ui->albumLineEdit->setText(newAlbum); somethingChanged = true; } } const auto newArtist = tagValueToQString(tagEdit->value(KnownField::Artist)); if (m_ui->artistLineEdit->text() != newArtist) { m_ui->artistLineEdit->setText(newArtist); somethingChanged = true; } // set title and track number if (lyricsCentricProvider) { if (const auto newTitle = tagValueToQString(tagEdit->value(KnownField::Title)); m_ui->titleLineEdit->text() != newTitle) { m_ui->titleLineEdit->setText(newTitle); somethingChanged = true; } } // refresh automatically if enabled and something has changed if (somethingChanged && m_refreshAutomaticallyAction && m_refreshAutomaticallyAction->isChecked()) { m_lastSearchAction->trigger(); } } SongDescription DbQueryWidget::currentSongDescription() const { SongDescription desc; desc.title = m_ui->titleLineEdit->text(); desc.album = m_ui->albumLineEdit->text(); desc.artist = m_ui->artistLineEdit->text(); desc.track = m_ui->trackSpinBox->value(); return desc; } void DbQueryWidget::searchMusicBrainz() { m_lastSearchAction = m_searchMusicBrainzAction; // check whether enough search terms are supplied if (m_ui->titleLineEdit->text().isEmpty() && m_ui->albumLineEdit->text().isEmpty() && m_ui->artistLineEdit->text().isEmpty()) { m_ui->notificationLabel->setNotificationType(NotificationType::Critical); m_ui->notificationLabel->setText(tr("Insufficient search criteria supplied - at least title, album or artist must be specified")); return; } // delete current model m_ui->resultsTreeView->setModel(nullptr); delete m_model; // show status m_ui->notificationLabel->setNotificationType(NotificationType::Progress); m_ui->notificationLabel->setText(tr("Retrieving meta data from MusicBrainz ...")); setStatus(false); // do actual query useQueryResults(queryMusicBrainz(currentSongDescription())); } void DbQueryWidget::searchLyricsWikia() { m_lastSearchAction = m_searchLyricsWikiaAction; // check whether enough search terms are supplied if (m_ui->artistLineEdit->text().isEmpty()) { m_ui->notificationLabel->setNotificationType(NotificationType::Critical); m_ui->notificationLabel->setText(tr("Insufficient search criteria supplied - artist is mandatory")); return; } // delete current model m_ui->resultsTreeView->setModel(nullptr); delete m_model; // show status m_ui->notificationLabel->setNotificationType(NotificationType::Progress); m_ui->notificationLabel->setText(tr("Retrieving meta data from LyricsWikia ...")); setStatus(false); // do actual query useQueryResults(queryLyricsWikia(currentSongDescription())); } void DbQueryWidget::searchMakeItPersonal() { m_lastSearchAction = m_searchMakeItPersonalAction; // check whether enough search terms are supplied if (m_ui->artistLineEdit->text().isEmpty() || m_ui->titleLineEdit->text().isEmpty()) { m_ui->notificationLabel->setNotificationType(NotificationType::Critical); m_ui->notificationLabel->setText(tr("Insufficient search criteria supplied - artist and title are mandatory")); return; } // delete current model m_ui->resultsTreeView->setModel(nullptr); delete m_model; // show status m_ui->notificationLabel->setNotificationType(NotificationType::Progress); m_ui->notificationLabel->setText(tr("Retrieving lyrics from makeitpersonal ...")); setStatus(false); // do actual query useQueryResults(queryMakeItPersonal(currentSongDescription())); } void DbQueryWidget::searchTekstowo() { m_lastSearchAction = m_searchTekstowoAction; // check whether enough search terms are supplied if (m_ui->artistLineEdit->text().isEmpty() || m_ui->titleLineEdit->text().isEmpty()) { m_ui->notificationLabel->setNotificationType(NotificationType::Critical); m_ui->notificationLabel->setText(tr("Insufficient search criteria supplied - artist and title are mandatory")); return; } // delete current model m_ui->resultsTreeView->setModel(nullptr); delete m_model; // show status m_ui->notificationLabel->setNotificationType(NotificationType::Progress); m_ui->notificationLabel->setText(tr("Retrieving lyrics from Tekstowo ...")); setStatus(false); // do actual query useQueryResults(queryTekstowo(currentSongDescription())); } void DbQueryWidget::abortSearch() { if (!m_model) { return; } if (m_model->isFetchingCover()) { // call abort to abort fetching cover m_model->abort(); } else if (!m_model->areResultsAvailable()) { // delete model to abort search m_ui->resultsTreeView->setModel(nullptr); delete m_model; m_model = nullptr; // update status m_ui->notificationLabel->setNotificationType(NotificationType::Information); m_ui->notificationLabel->setText(tr("Aborted")); setStatus(true); } } void DbQueryWidget::showResults() { if (!m_model) { return; } // update notification label if (m_model->errorList().isEmpty()) { m_ui->notificationLabel->setNotificationType(NotificationType::TaskComplete); if (m_model->results().isEmpty()) { m_ui->notificationLabel->setText(tr("No results available")); } else { m_ui->notificationLabel->setText( tr("%1 result(s) available", nullptr, Utility::containerSizeToInt(m_model->results().size())).arg(m_model->results().size())); } } else { m_ui->notificationLabel->setNotificationType(NotificationType::Critical); m_ui->notificationLabel->clearText(); for (const QString &error : m_model->errorList()) { m_ui->notificationLabel->appendLine(error); } } // update push buttons if (m_model->results().isEmpty()) { m_ui->applyPushButton->setEnabled(false); } else { auto *const selectionModel(m_ui->resultsTreeView->selectionModel()); if (selectionModel->selection().isEmpty()) { selectionModel->setCurrentIndex(m_model->index(0, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } m_ui->applyPushButton->setEnabled(m_tagEditorWidget->activeTagEdit()); } // hide empty columns for (int columnIndex = 0, columnCount = m_model->columnCount(), rowCount = m_model->rowCount(); columnIndex != columnCount; ++columnIndex) { bool columnHasValues = false; for (int rowIndex = 0; rowIndex != rowCount; ++rowIndex) { const auto value = m_model->data(m_model->index(rowIndex, columnIndex)); if (value.isNull()) { continue; } if ( #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) value.metaType() == QMetaType::fromType() #else value.type() == QVariant::String #endif && value.toString().isEmpty()) { continue; } columnHasValues = true; break; } m_ui->resultsTreeView->setColumnHidden(columnIndex, !columnHasValues); } setStatus(true); } void DbQueryWidget::setStatus(bool aborted) { m_ui->abortPushButton->setVisible(!aborted); m_searchMusicBrainzAction->setEnabled(aborted); if (m_searchLyricsWikiaAction) { m_searchLyricsWikiaAction->setEnabled(aborted); } m_ui->applyPushButton->setVisible(aborted); } void DbQueryWidget::fileStatusChanged(bool, bool hasTags) { m_ui->applyPushButton->setEnabled(hasTags && m_ui->resultsTreeView->selectionModel() && m_ui->resultsTreeView->selectionModel()->hasSelection()); insertSearchTermsFromActiveTagEdit(); m_insertPresentDataAction->setEnabled(hasTags); } /*! * \brief Applies the selected results for the selected fields to the active tag edit. * \sa applyResults() */ void DbQueryWidget::applySelectedResults() { const auto *const selectionModel = m_ui->resultsTreeView->selectionModel(); if (!selectionModel) { return; } const auto selection = selectionModel->selection().indexes(); if (!selection.isEmpty()) { applySpecifiedResults(selection.front()); } } /*! * \brief Applies the results for the specified \a modelIndex. */ void DbQueryWidget::applySpecifiedResults(const QModelIndex &modelIndex) { auto *const tagEdit = m_tagEditorWidget->activeTagEdit(); if (!tagEdit) { return; } applyResults(tagEdit, modelIndex); } /*! * \brief Completes all present tag edits with the best matching result row. * \remarks * - Does nothing if no result row matches. * - Only the selected fields are applied. * \sa applyResults() */ void DbQueryWidget::applyMatchingResults() { m_tagEditorWidget->foreachTagEdit(bind(static_cast(&DbQueryWidget::applyMatchingResults), this, _1)); } /*! * \brief Completes the specified \a tagEdit with the best matching result row. * \remarks * - Does nothing if no result row matches. * - Only the selected fields are applied. * \sa applyResults() */ void DbQueryWidget::applyMatchingResults(TagEdit *tagEdit) { if (!m_model) { return; } // determine already present title, album and artist const auto givenTitle = tagEdit->value(KnownField::Title); const auto givenAlbum = tagEdit->value(KnownField::Album); const auto givenArtist = tagEdit->value(KnownField::Artist); const auto givenTrack = tagEdit->trackNumber(); const auto givenDisk = tagEdit->diskNumber(); if (givenTitle.isEmpty() && !givenTrack) { return; } // find row matching already present data for (int row = 0, rowCount = m_model->rowCount(); row != rowCount; ++row) { // skip row if title, track, album or artist do not match // -> ignore fields which aren't specified, though // -> ignore track and disk mismatch if the title matches (likely the track/disk number has been taken over incorrectly) constexpr auto options = TagValueComparisionFlags::IgnoreMetaData | TagValueComparisionFlags::CaseInsensitive; const auto rowTitle = m_model->fieldValue(row, KnownField::Title); const auto rowAlbum = m_model->fieldValue(row, KnownField::Album); const auto rowArtist = m_model->fieldValue(row, KnownField::Artist); const auto rowTrack = m_model->fieldValue(row, KnownField::PartNumber).toInteger(); const auto rowDisk = m_model->fieldValue(row, KnownField::DiskPosition).toInteger(); const auto titleMismatch = !givenTitle.compareTo(rowTitle, options); if ((!givenTitle.isEmpty() && titleMismatch) || (!givenAlbum.isEmpty() && !givenAlbum.compareTo(rowAlbum, options)) || (!givenArtist.isEmpty() && !givenArtist.compareTo(rowArtist, options)) || (givenTrack && (givenTitle.isEmpty() || titleMismatch) && givenTrack != rowTrack) || (givenDisk && rowDisk && (givenTitle.isEmpty() || titleMismatch) && givenDisk != rowDisk)) { continue; } // apply results for matching row const auto rowIndex = m_model->index(row, 0); applyResults(tagEdit, rowIndex); // select the row which has just been applied if (auto *const selectionModel = m_ui->resultsTreeView->selectionModel()) { selectionModel->select(rowIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } m_tagEditorWidget->addParsingNotificationLine( tr("Inserted search result row %1 (with title \"%2\", album \"%3\" and artist \"%4\").") .arg(row + 1) .arg(tagValueToQString(rowTitle), tagValueToQString(rowAlbum), tagValueToQString(rowArtist))); // just take the first matching row for now break; } } /*! * \brief The same as applyMatchingResults() but checks whether auto-insert is enabled before (and does nothing if not). */ void DbQueryWidget::autoInsertMatchingResults() { if (m_ui->autoInsertCheckBox->isChecked()) { applyMatchingResults(); } } /*! * \brief Applies the results at the specified \a resultIndex for the selected fields to the specified \a tagEdit. * \remarks * - Returns instantly. If cover/lyrics need to be retrieved, this is done asynchronously. * - Does nothing if no results are available. */ void DbQueryWidget::applyResults(TagEdit *tagEdit, const QModelIndex &resultIndex) { if (!m_model) { return; } // determine previous value handling const auto previousValueHandling = m_ui->overrideCheckBox->isChecked() ? PreviousValueHandling::Update : PreviousValueHandling::Keep; // loop through all fields for (const ChecklistItem &item : values().dbQuery.fields.items()) { if (!item.isChecked()) { continue; } // determine the field to be used and its value const auto field = static_cast(item.id().toInt()); const auto row = resultIndex.row(); const auto value = m_model->fieldValue(row, field); // set the value if available if (!value.isEmpty()) { tagEdit->setValue(field, value, previousValueHandling); continue; } // cover and lyrics might be fetched asynchronously switch (field) { case KnownField::Cover: if (m_model->fetchCover(resultIndex)) { // cover is available now tagEdit->setValue(KnownField::Cover, m_model->fieldValue(row, KnownField::Cover), previousValueHandling); continue; } // cover is fetched asynchronously // -> show status m_ui->notificationLabel->setNotificationType(NotificationType::Progress); m_ui->notificationLabel->appendLine(tr("Retrieving cover art to be applied ...")); setStatus(false); // -> apply cover when available connect(m_model, &QueryResultsModel::coverAvailable, [this, row, previousValueHandling](const QModelIndex &index) { if (row != index.row()) { return; } if (auto *const activeTagEdit = m_tagEditorWidget->activeTagEdit()) { activeTagEdit->setValue(KnownField::Cover, m_model->fieldValue(row, KnownField::Cover), previousValueHandling); } }); break; case KnownField::Lyrics: if (m_model->fetchLyrics(resultIndex)) { // lyrics are available now tagEdit->setValue(KnownField::Lyrics, m_model->fieldValue(row, KnownField::Lyrics), previousValueHandling); continue; } // lyrics are fetched asynchronously // -> show status m_ui->notificationLabel->setNotificationType(NotificationType::Progress); m_ui->notificationLabel->appendLine(tr("Retrieving lyrics to be applied ...")); setStatus(false); // -> apply cover when available connect(m_model, &QueryResultsModel::lyricsAvailable, [this, row, previousValueHandling](const QModelIndex &index) { if (row != index.row()) { return; } if (auto *const activeTagEdit = m_tagEditorWidget->activeTagEdit()) { activeTagEdit->setValue(KnownField::Lyrics, m_model->fieldValue(row, KnownField::Lyrics), previousValueHandling); } }); break; default:; } } } void DbQueryWidget::insertSearchTermsFromActiveTagEdit() { insertSearchTermsFromTagEdit(m_tagEditorWidget->activeTagEdit()); } void DbQueryWidget::showResultsContextMenu(const QPoint &pos) { const auto *const selectionModel = m_ui->resultsTreeView->selectionModel(); const auto hasSelection = selectionModel && !selectionModel->selection().isEmpty(); QMenu contextMenu; if (hasSelection && m_ui->applyPushButton->isEnabled()) { contextMenu.addAction(m_ui->applyPushButton->icon(), tr("Use selected row"), this, static_cast(&DbQueryWidget::applySelectedResults)); } #ifndef QT_NO_CLIPBOARD if (hasSelection) { contextMenu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), tr("Copy value"), this, &DbQueryWidget::copySelectedResult); } #endif contextMenu.addAction(QIcon::fromTheme(QStringLiteral("view-refresh")), tr("Refresh results"), m_lastSearchAction, &QAction::trigger); if (hasSelection) { contextMenu.addSeparator(); contextMenu.addAction( QIcon::fromTheme(QStringLiteral("view-preview")), tr("Show cover"), this, &DbQueryWidget::fetchAndShowCoverForSelection); contextMenu.addAction( QIcon::fromTheme(QStringLiteral("view-media-lyrics")), tr("Show lyrics"), this, &DbQueryWidget::fetchAndShowLyricsForSelection); contextMenu.addAction( QIcon::fromTheme(QStringLiteral("internet-web-browser")), tr("Show in browser"), this, &DbQueryWidget::openSelectionInBrowser); } if (!contextMenu.isEmpty()) { contextMenu.addSeparator(); } contextMenu.addMenu(m_menu); m_contextMenuPos = m_ui->resultsTreeView->viewport()->mapToGlobal(pos); contextMenu.exec(m_contextMenuPos); } #ifndef QT_NO_CLIPBOARD void DbQueryWidget::copySelectedResult() { auto *const clipboard = QGuiApplication::clipboard(); if (!clipboard) { return; } const auto selectedIndex = this->selectedIndex(); if (!selectedIndex.isValid()) { return; } const auto clickedColumnIndex = m_ui->resultsTreeView->indexAt(m_ui->resultsTreeView->mapFromGlobal(m_contextMenuPos)); if (!clickedColumnIndex.isValid()) { return; } const auto value = m_model->data(m_model->index(selectedIndex.row(), clickedColumnIndex.column())); if (value.isValid()) { clipboard->setText(value.toString()); } } #endif void DbQueryWidget::fetchAndShowCoverForSelection() { const auto selectedIndex = this->selectedIndex(); if (!selectedIndex.isValid()) { return; } if (m_model->fetchCover(selectedIndex)) { if (const auto *const cover = m_model->cover(selectedIndex)) { showCover(*cover); } else { // cover couldn't be fetched, error tracked via resultsAvailable() signal so nothing to do } } else { // cover is fetched asynchronously // -> memorize index to be shown m_coverIndex = selectedIndex.row(); // -> show status m_ui->notificationLabel->setNotificationType(NotificationType::Progress); m_ui->notificationLabel->setText(tr("Retrieving cover art ...")); setStatus(false); } } void DbQueryWidget::fetchAndShowLyricsForSelection() { const auto selectedIndex = this->selectedIndex(); if (!selectedIndex.isValid()) { return; } if (m_model->fetchLyrics(selectedIndex)) { if (const auto *const lyrics = m_model->lyrics(selectedIndex)) { showLyrics(*lyrics); } else { // lyrics couldn't be fetched, error tracked via resultsAvailable() signal so nothing to do } } else { // lyrics are fetched asynchronously // -> memorize index to be shown m_lyricsIndex = selectedIndex.row(); // -> show status m_ui->notificationLabel->setNotificationType(NotificationType::Progress); m_ui->notificationLabel->setText(tr("Retrieving lyrics ...")); setStatus(false); } } void DbQueryWidget::openSelectionInBrowser() { const auto selectedIndex = this->selectedIndex(); if (!selectedIndex.isValid()) { return; } const auto url = m_model->webUrl(selectedIndex); if (url.isEmpty()) { m_ui->notificationLabel->appendLine(tr("No web URL available")); return; } if (!QDesktopServices::openUrl(url)) { m_ui->notificationLabel->appendLine(tr("Unable to open URL: ") + url.toString()); } } void DbQueryWidget::showCover(const QByteArray &data) { QDialog dlg; dlg.setWindowFlags(Qt::Tool); dlg.setWindowTitle(tr("Cover - %1").arg(QApplication::applicationName())); QBoxLayout layout(QBoxLayout::Up); layout.setContentsMargins(0, 0, 0, 0); QGraphicsView view(&dlg); QGraphicsScene scene; layout.addWidget(&view); scene.addItem(new QGraphicsPixmapItem(QPixmap::fromImage(QImage::fromData(data)))); view.setScene(&scene); view.show(); dlg.setLayout(&layout); dlg.exec(); } void DbQueryWidget::showCoverFromIndex(const QModelIndex &index) { if (m_coverIndex == index.row()) { m_coverIndex = -1; showCover(*m_model->cover(index)); } } void DbQueryWidget::showLyrics(const QString &data) { QDialog dlg; dlg.setWindowFlags(Qt::Tool); dlg.setWindowTitle(tr("Lyrics - %1").arg(QApplication::applicationName())); QBoxLayout layout(QBoxLayout::Up); layout.setContentsMargins(0, 0, 0, 0); QTextBrowser textBrowser; layout.addWidget(&textBrowser); textBrowser.setText(data); dlg.setLayout(&layout); dlg.resize(400, 400); dlg.exec(); } void DbQueryWidget::showLyricsFromIndex(const QModelIndex &index) { if (m_lyricsIndex == index.row()) { m_lyricsIndex = -1; showLyrics(*m_model->lyrics(index)); } } bool DbQueryWidget::event(QEvent *event) { const auto res = QWidget::event(event); switch (event->type()) { case QEvent::PaletteChange: updateStyleSheet(); break; default:; } return res; } void DbQueryWidget::clearSearchCriteria() { m_ui->titleLineEdit->clear(); m_ui->albumLineEdit->clear(); m_ui->artistLineEdit->clear(); m_ui->trackSpinBox->setValue(0); } void DbQueryWidget::updateStyleSheet() { #ifdef Q_OS_WINDOWS setStyleSheet(dialogStyleForPalette(palette())); #else setStyleSheet(QStringLiteral("QGroupBox { color: palette(text); background-color: palette(base); }")); #endif } bool DbQueryWidget::eventFilter(QObject *obj, QEvent *event) { if (obj == m_ui->searchGroupBox) { switch (event->type()) { case QEvent::KeyRelease: switch (static_cast(event)->key()) { case Qt::Key_Return: m_lastSearchAction->trigger(); break; default:; } break; default:; } } return QWidget::eventFilter(obj, event); } void DbQueryWidget::useQueryResults(QueryResultsModel *queryResults) { m_ui->resultsTreeView->setModel(m_model = queryResults); connect(queryResults, &QueryResultsModel::resultsAvailable, this, &DbQueryWidget::showResults); connect(queryResults, &QueryResultsModel::lyricsAvailable, this, &DbQueryWidget::showLyricsFromIndex); connect(queryResults, &QueryResultsModel::coverAvailable, this, &DbQueryWidget::showCoverFromIndex); connect(queryResults, &QAbstractItemModel::modelReset, this, &DbQueryWidget::autoInsertMatchingResults); } QModelIndex DbQueryWidget::selectedIndex() const { if (!m_model) { return QModelIndex(); } const auto *const selectionModel = m_ui->resultsTreeView->selectionModel(); if (!selectionModel) { return QModelIndex(); } const auto selection = selectionModel->selectedRows(); if (selection.size() != 1) { return QModelIndex(); } return selection.at(0); } } // namespace QtGui