1 /**
2 * \file serverimportdialog.cpp
3 * Generic dialog to import from a server.
4 *
5 * \b Project: Kid3
6 * \author Urs Fleisch
7 * \date 09 Oct 2006
8 *
9 * Copyright (C) 2006-2018 Urs Fleisch
10 *
11 * This file is part of Kid3.
12 *
13 * Kid3 is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * Kid3 is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 */
26
27 #include "serverimportdialog.h"
28 #include <QLayout>
29 #include <QPushButton>
30 #include <QLineEdit>
31 #include <QComboBox>
32 #include <QCheckBox>
33 #include <QLabel>
34 #include <QStatusBar>
35 #include <QVBoxLayout>
36 #include <QHBoxLayout>
37 #include <QListView>
38 #include <QCoreApplication>
39 #include "serverimporter.h"
40 #include "serverimporterconfig.h"
41 #include "contexthelp.h"
42 #include "trackdata.h"
43
44 /**
45 * Constructor.
46 *
47 * @param parent parent widget
48 */
ServerImportDialog(QWidget * parent)49 ServerImportDialog::ServerImportDialog(QWidget* parent) : QDialog(parent),
50 m_serverComboBox(nullptr), m_cgiLineEdit(nullptr), m_tokenLineEdit(nullptr),
51 m_standardTagsCheckBox(nullptr), m_additionalTagsCheckBox(nullptr),
52 m_coverArtCheckBox(nullptr), m_source(nullptr)
53 {
54 setObjectName(QLatin1String("ServerImportDialog"));
55
56 auto vlayout = new QVBoxLayout(this);
57
58 auto findLayout = new QHBoxLayout;
59 m_artistLineEdit = new QComboBox(this);
60 m_albumLineEdit = new QComboBox(this);
61 m_findButton = new QPushButton(tr("&Find"), this);
62 m_findButton->setAutoDefault(false);
63 m_artistLineEdit->setEditable(true);
64 m_artistLineEdit->setDuplicatesEnabled(false);
65 m_albumLineEdit->setEditable(true);
66 m_albumLineEdit->setDuplicatesEnabled(false);
67 m_artistLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
68 m_albumLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
69 findLayout->addWidget(m_artistLineEdit);
70 findLayout->addWidget(m_albumLineEdit);
71 findLayout->addWidget(m_findButton);
72 connect(m_findButton, &QAbstractButton::clicked, this, &ServerImportDialog::slotFind);
73 vlayout->addLayout(findLayout);
74
75 auto serverLayout = new QHBoxLayout;
76 m_serverLabel = new QLabel(tr("&Server:"), this);
77 m_serverComboBox = new QComboBox(this);
78 m_serverComboBox->setEditable(true);
79 m_cgiLabel = new QLabel(tr("C&GI Path:"), this);
80 m_cgiLineEdit = new QLineEdit(this);
81 serverLayout->addWidget(m_serverLabel);
82 serverLayout->addWidget(m_serverComboBox);
83 m_serverLabel->setBuddy(m_serverComboBox);
84 serverLayout->addWidget(m_cgiLabel);
85 serverLayout->addWidget(m_cgiLineEdit);
86 m_cgiLabel->setBuddy(m_cgiLineEdit);
87 vlayout->addLayout(serverLayout);
88
89 auto tokenLayout = new QHBoxLayout;
90 m_tokenLabel = new QLabel(tr("&Token:"), this);
91 m_tokenLineEdit = new QLineEdit(this);
92 tokenLayout->addWidget(m_tokenLabel);
93 tokenLayout->addWidget(m_tokenLineEdit);
94 m_tokenLabel->setBuddy(m_tokenLineEdit);
95 vlayout->addLayout(tokenLayout);
96
97 auto hlayout = new QHBoxLayout;
98 m_standardTagsCheckBox = new QCheckBox(tr("&Standard Tags"), this);
99 m_additionalTagsCheckBox = new QCheckBox(tr("&Additional Tags"), this);
100 m_coverArtCheckBox = new QCheckBox(tr("C&over Art"), this);
101 hlayout->addWidget(m_standardTagsCheckBox);
102 hlayout->addWidget(m_additionalTagsCheckBox);
103 hlayout->addWidget(m_coverArtCheckBox);
104 vlayout->addLayout(hlayout);
105
106 m_albumListBox = new QListView(this);
107 m_albumListBox->setEditTriggers(QAbstractItemView::NoEditTriggers);
108 vlayout->addWidget(m_albumListBox);
109 connect(m_albumListBox, &QAbstractItemView::activated,
110 this, static_cast<void (ServerImportDialog::*)(const QModelIndex&)>(
111 &ServerImportDialog::requestTrackList));
112
113 auto buttonLayout = new QHBoxLayout;
114 m_helpButton = new QPushButton(tr("&Help"), this);
115 m_helpButton->setAutoDefault(false);
116 m_saveButton = new QPushButton(tr("&Save Settings"), this);
117 m_saveButton->setAutoDefault(false);
118 QPushButton* closeButton = new QPushButton(tr("&Close"), this);
119 closeButton->setAutoDefault(false);
120 buttonLayout->addWidget(m_helpButton);
121 connect(m_helpButton, &QAbstractButton::clicked, this, &ServerImportDialog::showHelp);
122 buttonLayout->addWidget(m_saveButton);
123 connect(m_saveButton, &QAbstractButton::clicked, this, &ServerImportDialog::saveConfig);
124 auto hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding,
125 QSizePolicy::Minimum);
126 buttonLayout->addItem(hspacer);
127 buttonLayout->addWidget(closeButton);
128 connect(closeButton, &QAbstractButton::clicked, this, &QDialog::accept);
129 vlayout->addLayout(buttonLayout);
130
131 m_statusBar = new QStatusBar(this);
132 vlayout->addWidget(m_statusBar);
133 showStatusMessage(tr("Ready."));
134 }
135
136 /**
137 * Set importer to be used.
138 *
139 * @param source import source to use
140 */
setImportSource(ServerImporter * source)141 void ServerImportDialog::setImportSource(ServerImporter* source)
142 {
143 if (m_source) {
144 disconnect(m_source, &HttpClient::progress,
145 this, &ServerImportDialog::showStatusMessage);
146 disconnect(m_source, &ImportClient::findFinished,
147 this, &ServerImportDialog::slotFindFinished);
148 disconnect(m_source, &ImportClient::albumFinished,
149 this, &ServerImportDialog::slotAlbumFinished);
150 }
151 m_source = source;
152
153 if (m_source) {
154 connect(m_source, &HttpClient::progress,
155 this, &ServerImportDialog::showStatusMessage);
156 connect(m_source, &ImportClient::findFinished,
157 this, &ServerImportDialog::slotFindFinished);
158 connect(m_source, &ImportClient::albumFinished,
159 this, &ServerImportDialog::slotAlbumFinished);
160
161 setWindowTitle(QCoreApplication::translate("@default", m_source->name()));
162 if (m_source->defaultServer()) {
163 m_serverLabel->show();
164 m_serverComboBox->show();
165 if (m_source->defaultCgiPath()) {
166 m_cgiLabel->show();
167 m_cgiLineEdit->show();
168 } else {
169 m_cgiLabel->hide();
170 m_cgiLineEdit->hide();
171 }
172 if (m_source->serverList()) {
173 QStringList strList;
174 for (const char** sl = m_source->serverList(); *sl != nullptr; ++sl) {
175 strList += QString::fromLatin1(*sl); // clazy:exclude=reserve-candidates
176 }
177 m_serverComboBox->clear();
178 m_serverComboBox->addItems(strList);
179 }
180 } else {
181 m_serverLabel->hide();
182 m_serverComboBox->hide();
183 m_cgiLabel->hide();
184 m_cgiLineEdit->hide();
185 }
186 if (qstrcmp(m_source->name(), "Discogs") == 0) {
187 m_tokenLabel->show();
188 m_tokenLineEdit->show();
189 } else {
190 m_tokenLabel->hide();
191 m_tokenLineEdit->hide();
192 }
193 if (m_source->additionalTags()) {
194 m_standardTagsCheckBox->show();
195 m_additionalTagsCheckBox->show();
196 m_coverArtCheckBox->show();
197 } else {
198 m_standardTagsCheckBox->hide();
199 m_additionalTagsCheckBox->hide();
200 m_coverArtCheckBox->hide();
201 }
202
203 m_albumListBox->setModel(m_source->getAlbumListModel());
204
205 if (m_source->helpAnchor()) {
206 m_helpButton->show();
207 } else {
208 m_helpButton->hide();
209 }
210 if (m_source->config()) {
211 m_saveButton->show();
212 } else {
213 m_saveButton->hide();
214 }
215 }
216 }
217
218 /**
219 * Display message in status bar.
220 *
221 * @param msg status message
222 */
showStatusMessage(const QString & msg)223 void ServerImportDialog::showStatusMessage(const QString& msg)
224 {
225 m_statusBar->showMessage(msg);
226 }
227
228 /**
229 * Get string with server and port.
230 *
231 * @return "servername:port".
232 */
getServer() const233 QString ServerImportDialog::getServer() const
234 {
235 if (m_serverComboBox) {
236 QString server(m_serverComboBox->currentText());
237 if (server.isEmpty() && m_source) {
238 server = QString::fromLatin1(m_source->defaultServer());
239 }
240 return server;
241 } else {
242 return QString();
243 }
244 }
245
246 /**
247 * Set string with server and port.
248 *
249 * @param srv "servername:port"
250 */
setServer(const QString & srv)251 void ServerImportDialog::setServer(const QString& srv)
252 {
253 if (m_serverComboBox) {
254 int idx = m_serverComboBox->findText(srv);
255 if (idx >= 0) {
256 m_serverComboBox->setCurrentIndex(idx);
257 } else {
258 m_serverComboBox->addItem(srv);
259 m_serverComboBox->setCurrentIndex(m_serverComboBox->count() - 1);
260 }
261 }
262 }
263
264 /**
265 * Get string with CGI path.
266 *
267 * @return CGI path, e.g. "/~cddb/cddb.cgi".
268 */
getCgiPath() const269 QString ServerImportDialog::getCgiPath() const
270 {
271 if (m_cgiLineEdit) {
272 QString cgi(m_cgiLineEdit->text());
273 if (cgi.isEmpty() && m_source) {
274 cgi = QString::fromLatin1(m_source->defaultCgiPath());
275 }
276 return cgi;
277 } else {
278 return QString();
279 }
280 }
281
282 /**
283 * Set string with CGI path.
284 *
285 * @param cgi CGI path, e.g. "/~cddb/cddb.cgi".
286 */
setCgiPath(const QString & cgi)287 void ServerImportDialog::setCgiPath(const QString& cgi)
288 {
289 if (m_cgiLineEdit) {
290 m_cgiLineEdit->setText(cgi);
291 }
292 }
293
294 /**
295 * Get token to access API server.
296 * @return token.
297 */
getToken() const298 QString ServerImportDialog::getToken() const
299 {
300 return m_tokenLineEdit ? m_tokenLineEdit->text() : QString();
301 }
302
303 /**
304 * Set token to access API server.
305 * @param token access token
306 */
setToken(const QString & token)307 void ServerImportDialog::setToken(const QString& token)
308 {
309 if (m_tokenLineEdit) {
310 m_tokenLineEdit->setText(token);
311 }
312 }
313
314 /**
315 * Get standard tags option.
316 *
317 * @return true if standard tags are enabled.
318 */
getStandardTags() const319 bool ServerImportDialog::getStandardTags() const
320 {
321 return m_standardTagsCheckBox ?
322 m_standardTagsCheckBox->checkState() == Qt::Checked
323 : false;
324 }
325
326 /**
327 * Set standard tags option.
328 *
329 * @param enable true if standard tags are enabled
330 */
setStandardTags(bool enable)331 void ServerImportDialog::setStandardTags(bool enable)
332 {
333 if (m_standardTagsCheckBox) {
334 m_standardTagsCheckBox->setCheckState(
335 enable ? Qt::Checked : Qt::Unchecked);
336 }
337 }
338
339 /**
340 * Get additional tags option.
341 *
342 * @return true if additional tags are enabled.
343 */
getAdditionalTags() const344 bool ServerImportDialog::getAdditionalTags() const
345 {
346 return m_additionalTagsCheckBox ?
347 m_additionalTagsCheckBox->checkState() == Qt::Checked
348 : false;
349 }
350
351 /**
352 * Set additional tags option.
353 *
354 * @param enable true if additional tags are enabled
355 */
setAdditionalTags(bool enable)356 void ServerImportDialog::setAdditionalTags(bool enable)
357 {
358 if (m_additionalTagsCheckBox) {
359 m_additionalTagsCheckBox->setCheckState(
360 enable ? Qt::Checked : Qt::Unchecked);
361 }
362 }
363
364 /**
365 * Get cover art option.
366 *
367 * @return true if cover art are enabled.
368 */
getCoverArt() const369 bool ServerImportDialog::getCoverArt() const
370 {
371 return m_coverArtCheckBox ?
372 m_coverArtCheckBox->checkState() == Qt::Checked
373 : false;
374 }
375
376 /**
377 * Set cover art option.
378 *
379 * @param enable true if cover art are enabled
380 */
setCoverArt(bool enable)381 void ServerImportDialog::setCoverArt(bool enable)
382 {
383 if (m_coverArtCheckBox) {
384 m_coverArtCheckBox->setCheckState(
385 enable ? Qt::Checked : Qt::Unchecked);
386 }
387 }
388
389 /**
390 * Get the local configuration.
391 *
392 * @param cfg configuration
393 */
getImportSourceConfig(ServerImporterConfig * cfg) const394 void ServerImportDialog::getImportSourceConfig(ServerImporterConfig* cfg) const
395 {
396 cfg->setServer(getServer());
397 cfg->setCgiPath(getCgiPath());
398 cfg->setStandardTags(getStandardTags());
399 cfg->setAdditionalTags(getAdditionalTags());
400 cfg->setCoverArt(getCoverArt());
401 cfg->setWindowGeometry(saveGeometry());
402
403 QString token = getToken();
404 if (!token.isEmpty() || cfg->property("token").isValid()) {
405 cfg->setProperty("token", token);
406 }
407 }
408
409 /**
410 * Save the local settings to the configuration.
411 */
saveConfig()412 void ServerImportDialog::saveConfig()
413 {
414 if (m_source && m_source->config()) {
415 getImportSourceConfig(m_source->config());
416 }
417 }
418
419 /**
420 * Set a find string from artist and album information.
421 *
422 * @param artist artist
423 * @param album album
424 */
setArtistAlbum(const QString & artist,const QString & album)425 void ServerImportDialog::setArtistAlbum(const QString& artist, const QString& album)
426 {
427 if (m_source && m_source->config()) {
428 ServerImporterConfig* cf = m_source->config();
429 setServer(cf->server());
430 setCgiPath(cf->cgiPath());
431 setStandardTags(cf->standardTags());
432 setAdditionalTags(cf->additionalTags());
433 setCoverArt(cf->coverArt());
434 if (!cf->windowGeometry().isEmpty()) {
435 restoreGeometry(cf->windowGeometry());
436 }
437
438 setToken(cf->property("token").toString());
439 }
440
441 if (!(artist.isEmpty() && album.isEmpty())) {
442 int idx = m_artistLineEdit->findText(artist);
443 if (idx >= 0) {
444 m_artistLineEdit->setCurrentIndex(idx);
445 } else {
446 m_artistLineEdit->addItem(artist);
447 m_artistLineEdit->setCurrentIndex(m_artistLineEdit->count() - 1);
448 }
449 idx = m_albumLineEdit->findText(album);
450 if (idx >= 0) {
451 m_albumLineEdit->setCurrentIndex(idx);
452 } else {
453 m_albumLineEdit->addItem(album);
454 m_albumLineEdit->setCurrentIndex(m_albumLineEdit->count() - 1);
455 }
456 QLineEdit* lineEdit = m_artistLineEdit->lineEdit();
457 if (lineEdit) {
458 lineEdit->selectAll();
459 }
460 m_artistLineEdit->setFocus();
461 }
462 }
463
464 /**
465 * Query a search for a keyword from the server.
466 */
slotFind()467 void ServerImportDialog::slotFind()
468 {
469 ServerImporterConfig cfg;
470 getImportSourceConfig(&cfg);
471 if (m_source)
472 m_source->find(&cfg, m_artistLineEdit->currentText(),
473 m_albumLineEdit->currentText());
474 }
475
476 /**
477 * Process finished find request.
478 *
479 * @param searchStr search data received
480 */
slotFindFinished(const QByteArray & searchStr)481 void ServerImportDialog::slotFindFinished(const QByteArray& searchStr)
482 {
483 if (m_source)
484 m_source->parseFindResults(searchStr);
485 m_albumListBox->setFocus();
486 if (QItemSelectionModel* selModel = m_albumListBox->selectionModel()) {
487 if (QAbstractItemModel* model = m_albumListBox->model()) {
488 if (model->rowCount() > 0) {
489 selModel->select(model->index(0, 0),
490 QItemSelectionModel::Select | QItemSelectionModel::Rows);
491 }
492 }
493 }
494 }
495
496 /**
497 * Process finished album data.
498 *
499 * @param albumStr album track data received
500 */
slotAlbumFinished(const QByteArray & albumStr)501 void ServerImportDialog::slotAlbumFinished(const QByteArray& albumStr)
502 {
503 if (m_source) {
504 m_source->setStandardTags(getStandardTags());
505 m_source->setAdditionalTags(getAdditionalTags());
506 m_source->setCoverArt(getCoverArt());
507 m_source->parseAlbumResults(albumStr);
508 }
509 emit trackDataUpdated();
510 }
511
512 /**
513 * Request track list from server.
514 *
515 * @param category category, e.g. "release"
516 * @param id internal ID
517 */
requestTrackList(const QString & category,const QString & id)518 void ServerImportDialog::requestTrackList(const QString& category,
519 const QString& id)
520 {
521 ServerImporterConfig cfg;
522 getImportSourceConfig(&cfg);
523 if (m_source)
524 m_source->getTrackList(&cfg, category, id);
525 }
526
527 /**
528 * Request track list from server.
529 *
530 * @param index model index of list containing an AlbumListItem
531 */
requestTrackList(const QModelIndex & index)532 void ServerImportDialog::requestTrackList(const QModelIndex& index)
533 {
534 if (m_source) {
535 QString text, category, id;
536 m_source->getAlbumListModel()->getItem(index.row(), text, category, id);
537 if (!id.isEmpty()) {
538 requestTrackList(category, id);
539 }
540 }
541 }
542
543 /**
544 * Show help.
545 */
showHelp()546 void ServerImportDialog::showHelp()
547 {
548 if (m_source && m_source->helpAnchor()) {
549 ContextHelp::displayHelp(QString::fromLatin1(m_source->helpAnchor()));
550 }
551 }
552