File indexing completed on 2025-02-23 04:34:19

0001 /**
0002  * \file servertrackimportdialog.cpp
0003  * Generic dialog for track based import from a server.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 13 Sep 2005
0008  *
0009  * Copyright (C) 2005-2024  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #include "servertrackimportdialog.h"
0028 #include <QLayout>
0029 #include <QPushButton>
0030 #include <QLineEdit>
0031 #include <QComboBox>
0032 #include <QCheckBox>
0033 #include <QLabel>
0034 #include <QStatusBar>
0035 #include <QTableView>
0036 #include <QHeaderView>
0037 #include <QVBoxLayout>
0038 #include <QHBoxLayout>
0039 #include <QCoreApplication>
0040 #include "serverimporterconfig.h"
0041 #include "contexthelp.h"
0042 #include "servertrackimporter.h"
0043 #include "comboboxdelegate.h"
0044 #include "trackdatamodel.h"
0045 #include "standardtablemodel.h"
0046 
0047 namespace {
0048 
0049 class AlbumTableModel : public StandardTableModel {
0050 public:
0051   using StandardTableModel::StandardTableModel;
0052 
0053   Qt::ItemFlags flags(const QModelIndex& index) const override {
0054     Qt::ItemFlags itemFlags =
0055         QAbstractItemModel::flags(index) | Qt::ItemIsDropEnabled;
0056     if (index.isValid())
0057       itemFlags |= Qt::ItemIsDragEnabled;
0058     if (index.column() != 1)
0059       itemFlags |= Qt::ItemIsEditable;
0060     return itemFlags;
0061   }
0062 };
0063 
0064 }
0065 
0066 /**
0067  * Constructor.
0068  *
0069  * @param parent          parent widget
0070  * @param trackDataModel track data to be filled with imported values,
0071  *                        is passed with filenames set
0072  */
0073 ServerTrackImportDialog::ServerTrackImportDialog(QWidget* parent,
0074                                                  TrackDataModel* trackDataModel)
0075   : QDialog(parent), m_statusBar(nullptr),
0076     m_client(nullptr), m_trackDataModel(trackDataModel)
0077 {
0078   setObjectName(QLatin1String("ServerTrackImportDialog"));
0079   setModal(true);
0080 
0081   auto vlayout = new QVBoxLayout(this);
0082   auto serverLayout = new QHBoxLayout;
0083   m_serverLabel = new QLabel(tr("&Server:"), this);
0084   m_serverComboBox = new QComboBox(this);
0085   m_serverComboBox->setEditable(true);
0086   m_serverComboBox->setSizePolicy(
0087     QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
0088   m_serverLabel->setBuddy(m_serverComboBox);
0089   serverLayout->addWidget(m_serverLabel);
0090   serverLayout->addWidget(m_serverComboBox);
0091   vlayout->addLayout(serverLayout);
0092 
0093   m_albumTableModel = new AlbumTableModel(this);
0094   m_albumTableModel->setColumnCount(2);
0095   m_albumTableModel->setHorizontalHeaderLabels({
0096     QLatin1String("08 A Not So Short Title/Medium Sized Artist - And The Album Title [2005]"),
0097     QLatin1String("A Not So Short State")
0098   });
0099   m_albumTable = new QTableView(this);
0100   m_albumTable->setModel(m_albumTableModel);
0101   m_albumTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
0102   m_albumTable->setSelectionMode(QAbstractItemView::NoSelection);
0103   m_albumTable->resizeColumnsToContents();
0104   m_albumTable->setItemDelegateForColumn(0, new ComboBoxDelegate(this));
0105   m_albumTableModel->setHorizontalHeaderLabels({
0106     tr("Track Title/Artist - Album"),
0107     tr("State")
0108   });
0109   initTable();
0110   vlayout->addWidget(m_albumTable);
0111 
0112   auto hlayout = new QHBoxLayout;
0113   auto hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding,
0114                                          QSizePolicy::Minimum);
0115   m_helpButton = new QPushButton(tr("&Help"), this);
0116   m_helpButton->setAutoDefault(false);
0117   m_saveButton = new QPushButton(tr("&Save Settings"), this);
0118   m_saveButton->setAutoDefault(false);
0119   auto okButton = new QPushButton(tr("&OK"), this);
0120   auto applyButton = new QPushButton(tr("&Apply"), this);
0121   auto cancelButton = new QPushButton(tr("&Cancel"), this);
0122   hlayout->addWidget(m_helpButton);
0123   hlayout->addWidget(m_saveButton);
0124   hlayout->addItem(hspacer);
0125   hlayout->addWidget(okButton);
0126   hlayout->addWidget(applyButton);
0127   hlayout->addWidget(cancelButton);
0128   // auto default is switched off to use the return key to set the server
0129   // configuration
0130   okButton->setAutoDefault(false);
0131   okButton->setDefault(true);
0132   cancelButton->setAutoDefault(false);
0133   applyButton->setAutoDefault(false);
0134   connect(m_helpButton, &QAbstractButton::clicked, this, &ServerTrackImportDialog::showHelp);
0135   connect(m_saveButton, &QAbstractButton::clicked, this, &ServerTrackImportDialog::saveConfig);
0136   connect(okButton, &QAbstractButton::clicked, this, &ServerTrackImportDialog::accept);
0137   connect(cancelButton, &QAbstractButton::clicked, this, &ServerTrackImportDialog::reject);
0138   connect(applyButton, &QAbstractButton::clicked, this, &ServerTrackImportDialog::apply);
0139   vlayout->addLayout(hlayout);
0140 
0141   m_statusBar = new QStatusBar(this);
0142   vlayout->addWidget(m_statusBar);
0143   connect(m_albumTable->selectionModel(),
0144           &QItemSelectionModel::currentRowChanged,
0145           this, &ServerTrackImportDialog::showFilenameInStatusBar);
0146 }
0147 
0148 /**
0149  * Destructor.
0150  */
0151 ServerTrackImportDialog::~ServerTrackImportDialog()
0152 {
0153   stopClient();
0154 }
0155 
0156 /**
0157  * Set importer to be used.
0158  *
0159  * @param source  import source to use
0160  */
0161 void ServerTrackImportDialog::setImportSource(ServerTrackImporter* source)
0162 {
0163   if (m_client) {
0164     disconnect(m_client, &ServerTrackImporter::statusChanged,
0165                this, &ServerTrackImportDialog::setFileStatus);
0166     disconnect(m_client, &ServerTrackImporter::resultsReceived,
0167                this, &ServerTrackImportDialog::setResults);
0168   }
0169   m_client = source;
0170 
0171   if (m_client) {
0172     connect(m_client, &ServerTrackImporter::statusChanged,
0173             this, &ServerTrackImportDialog::setFileStatus);
0174     connect(m_client, &ServerTrackImporter::resultsReceived,
0175             this, &ServerTrackImportDialog::setResults);
0176 
0177     setWindowTitle(QCoreApplication::translate("@default", m_client->name()));
0178     if (m_client->defaultServer()) {
0179       m_serverLabel->show();
0180       m_serverComboBox->show();
0181       if (m_client->serverList()) {
0182         QStringList strList;
0183         for (const char** sl = m_client->serverList(); *sl != nullptr; ++sl) {
0184           strList += QString::fromLatin1(*sl); // clazy:exclude=reserve-candidates
0185         }
0186         m_serverComboBox->clear();
0187         m_serverComboBox->addItems(strList);
0188       }
0189     } else {
0190       m_serverLabel->hide();
0191       m_serverComboBox->hide();
0192     }
0193     if (m_client->helpAnchor()) {
0194       m_helpButton->show();
0195     } else {
0196       m_helpButton->hide();
0197     }
0198     if (m_client->config()) {
0199       m_saveButton->show();
0200     } else {
0201       m_saveButton->hide();
0202     }
0203   }
0204 }
0205 
0206 /**
0207  * Initialize the table model.
0208  * Has to be called before reusing the dialog with new track data.
0209  */
0210 void ServerTrackImportDialog::initTable()
0211 {
0212   if (m_client && m_client->config()) {
0213     setServer(m_client->config()->server());
0214   }
0215 
0216   int numRows = 0;
0217   const ImportTrackDataVector& trackDataVector(m_trackDataModel->trackData());
0218   for (auto it = trackDataVector.constBegin(); it != trackDataVector.constEnd(); ++it) {
0219     if (it->isEnabled()) {
0220       ++numRows;
0221     }
0222   }
0223 
0224   m_trackResults.resize(numRows);
0225   m_albumTableModel->clear();
0226   m_albumTableModel->insertRows(0, numRows);
0227   for (int i = 0; i < numRows; ++i) {
0228     QStringList cbItems;
0229     cbItems << tr("No result") << tr("Unknown");
0230     QModelIndex idx = m_albumTableModel->index(i, 0);
0231     m_albumTableModel->setData(idx, cbItems.first(), Qt::EditRole);
0232     m_albumTableModel->setData(idx, cbItems, Qt::UserRole);
0233     idx = m_albumTableModel->index(i, 1);
0234     m_albumTableModel->setData(idx, tr("Unknown"));
0235   }
0236   showFilenameInStatusBar(m_albumTable->currentIndex());
0237 }
0238 
0239 /**
0240  * Clear all results.
0241  */
0242 void ServerTrackImportDialog::clearResults()
0243 {
0244   const int numRows = m_trackResults.size();
0245   for (int i = 0; i < numRows; ++i) {
0246     m_trackResults[i].clear();
0247     setFileStatus(i, tr("Unknown"));
0248     updateFileTrackData(i);
0249   }
0250 }
0251 
0252 /**
0253  * Create and start the track import client.
0254  */
0255 void ServerTrackImportDialog::startClient()
0256 {
0257   if (m_client) {
0258     clearResults();
0259     ServerImporterConfig cfg;
0260     cfg.setServer(getServer());
0261     m_client->setConfig(&cfg);
0262     m_client->start();
0263   }
0264 }
0265 
0266 /**
0267  * Stop and destroy the track import client.
0268  */
0269 void ServerTrackImportDialog::stopClient()
0270 {
0271   if (m_client) {
0272     m_client->stop();
0273   }
0274 }
0275 
0276 /**
0277  * Hides the dialog and sets the result to QDialog::Accepted.
0278  */
0279 void ServerTrackImportDialog::accept()
0280 {
0281   apply();
0282   stopClient();
0283   QDialog::accept();
0284 }
0285 
0286 /**
0287  * Hides the dialog and sets the result to QDialog::Rejected.
0288  */
0289 void ServerTrackImportDialog::reject()
0290 {
0291   stopClient();
0292   QDialog::reject();
0293 }
0294 
0295 /**
0296  * Apply imported data.
0297  */
0298 void ServerTrackImportDialog::apply()
0299 {
0300   ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData());
0301   trackDataVector.setCoverArtUrl(QUrl());
0302   auto it = trackDataVector.begin();
0303   bool newTrackData = false;
0304   int numRows = m_albumTableModel->rowCount();
0305   for (int index = 0; index < numRows; ++index) {
0306     while (it != trackDataVector.end() && !it->isEnabled()) {
0307       ++it;
0308     }
0309     if (it == trackDataVector.end()) {
0310       break;
0311     }
0312     QModelIndex idx(m_albumTableModel->index(index, 0));
0313     if (idx.isValid()) {
0314       if (int selectedItem = idx.data(Qt::UserRole).toStringList().indexOf(
0315             idx.data(Qt::EditRole).toString());
0316           selectedItem > 0) {
0317         const ImportTrackData& selectedData =
0318           m_trackResults[index][selectedItem - 1];
0319         it->setTitle(selectedData.getTitle());
0320         it->setArtist(selectedData.getArtist());
0321         it->setAlbum(selectedData.getAlbum());
0322         it->setTrack(selectedData.getTrack());
0323         it->setYear(selectedData.getYear());
0324         it->setImportDuration(selectedData.getImportDuration());
0325         newTrackData = true;
0326       }
0327     }
0328     ++it;
0329   }
0330   if (newTrackData) {
0331     m_trackDataModel->setTrackData(trackDataVector);
0332     emit trackDataUpdated();
0333   }
0334 }
0335 
0336 /**
0337  * Shows the dialog as a modal dialog.
0338  */
0339 int ServerTrackImportDialog::exec()
0340 {
0341   startClient();
0342   return QDialog::exec();
0343 }
0344 
0345 /**
0346  * Set the status of a file.
0347  *
0348  * @param index  index of file
0349  * @param status status string
0350  */
0351 void ServerTrackImportDialog::setFileStatus(int index, const QString& status)
0352 {
0353   m_albumTableModel->setData(m_albumTableModel->index(index, 1),
0354                              status);
0355 }
0356 
0357 /**
0358  * Update the track data combo box of a file.
0359  *
0360  * @param index  index of file
0361  */
0362 void ServerTrackImportDialog::updateFileTrackData(int index)
0363 {
0364   QStringList stringList;
0365   const ImportTrackDataVector& trackData = m_trackResults.at(index);
0366   const int numResults = trackData.size();
0367   QString str(numResults == 0 ?
0368               tr("No result") : tr("No result selected"));
0369   stringList.push_back(str);
0370   for (auto it = trackData.constBegin(); it != trackData.constEnd(); ++it) {
0371     str = QString(QLatin1String("%1 "))
0372         .arg(it->getTrack(), 2, 10, QLatin1Char('0'));
0373     str += it->getTitle();
0374     str += QLatin1Char('/');
0375     str += it->getArtist();
0376     str += QLatin1String(" - ");
0377     str += it->getAlbum();
0378     if (it->getYear() > 0) {
0379       str += QString(QLatin1String(" [%1]")).arg(it->getYear());
0380     }
0381     stringList.push_back(str);
0382   }
0383   m_albumTableModel->setData(m_albumTableModel->index(index, 0),
0384                              stringList, Qt::UserRole);
0385   m_albumTableModel->setData(m_albumTableModel->index(index, 0),
0386                              stringList.at(numResults == 1 ? 1 : 0),
0387                              Qt::EditRole);
0388 }
0389 
0390 /**
0391  * Set result list for a file.
0392  *
0393  * @param index           index of file
0394  * @param trackDataVector result list
0395  */
0396 void ServerTrackImportDialog::setResults(
0397   int index, const ImportTrackDataVector& trackDataVector)
0398 {
0399   m_trackResults[index] = trackDataVector;
0400   updateFileTrackData(index);
0401 }
0402 
0403 /**
0404  * Get string with server and port.
0405  *
0406  * @return "servername:port".
0407  */
0408 QString ServerTrackImportDialog::getServer() const
0409 {
0410   QString server(m_serverComboBox->currentText());
0411   if (server.isEmpty() && m_client && m_client->defaultServer()) {
0412     server = QString::fromLatin1(m_client->defaultServer());
0413   }
0414   return server;
0415 }
0416 
0417 /**
0418  * Set string with server and port.
0419  *
0420  * @param srv "servername:port"
0421  */
0422 void ServerTrackImportDialog::setServer(const QString& srv)
0423 {
0424   if (int idx = m_serverComboBox->findText(srv); idx >= 0) {
0425     m_serverComboBox->setCurrentIndex(idx);
0426   } else {
0427     m_serverComboBox->addItem(srv);
0428     m_serverComboBox->setCurrentIndex(m_serverComboBox->count() - 1);
0429   }
0430 }
0431 
0432 /**
0433  * Save the local settings to the configuration.
0434  */
0435 void ServerTrackImportDialog::saveConfig()
0436 {
0437   if (m_client && m_client->config()) {
0438     m_client->config()->setServer(getServer());
0439   }
0440 }
0441 
0442 /**
0443  * Show help.
0444  */
0445 void ServerTrackImportDialog::showHelp()
0446 {
0447   if (m_client && m_client->helpAnchor()) {
0448     ContextHelp::displayHelp(QString::fromLatin1(m_client->helpAnchor()));
0449   }
0450 }
0451 
0452 /**
0453  * Show the name of the current track in the status bar.
0454  *
0455  * @param index model index
0456  */
0457 void ServerTrackImportDialog::showFilenameInStatusBar(const QModelIndex& index)
0458 {
0459   if (m_statusBar) {
0460     int row = index.row();
0461 
0462     int rowNr = 0;
0463     const ImportTrackDataVector& trackDataVector(m_trackDataModel->trackData());
0464     for (auto it = trackDataVector.constBegin();
0465          it != trackDataVector.constEnd();
0466          ++it) {
0467       if (it->isEnabled()) {
0468         if (rowNr == row) {
0469           m_statusBar->showMessage(it->getFilename());
0470           return;
0471         }
0472         ++rowNr;
0473       }
0474     }
0475     m_statusBar->clearMessage();
0476   }
0477 }