1 /**
2 * \file importdialog.cpp
3 * Import dialog.
4 *
5 * \b Project: Kid3
6 * \author Urs Fleisch
7 * \date 17 Sep 2003
8 *
9 * Copyright (C) 2003-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 "importdialog.h"
28 #include <QLayout>
29 #include <QPushButton>
30 #include <QToolButton>
31 #include <QLabel>
32 #include <QCheckBox>
33 #include <QSpinBox>
34 #include <QString>
35 #include <QVBoxLayout>
36 #include <QHBoxLayout>
37 #include <QComboBox>
38 #include <QLineEdit>
39 #include <QBitArray>
40 #include <QToolTip>
41 #include <QTableView>
42 #include <QHeaderView>
43 #include <QList>
44 #include <QGridLayout>
45 #include <QGroupBox>
46 #include <QDir>
47 #include <QMenu>
48 #include <QCoreApplication>
49 #include "config.h"
50 #include "importconfig.h"
51 #include "genres.h"
52 #include "serverimporter.h"
53 #include "servertrackimporter.h"
54 #include "serverimportdialog.h"
55 #include "servertrackimportdialog.h"
56 #include "textimportdialog.h"
57 #include "tagimportdialog.h"
58 #include "contexthelp.h"
59 #include "taggedfile.h"
60 #include "trackdata.h"
61 #include "trackdatamodel.h"
62 #include "frameitemdelegate.h"
63 #include "trackdatamatcher.h"
64 #include "iplatformtools.h"
65
66 namespace {
67
68 /**
69 * Get list of frame types whose visibility can be changed using a context menu.
70 * @return list of frame types of Frame::Type or
71 * TrackDataModel::TrackProperties.
72 */
checkableFrameTypes()73 QList<int> checkableFrameTypes() {
74 return QList<int>()
75 << TrackDataModel::FT_FileName << TrackDataModel::FT_FilePath;
76 }
77
78 }
79
80 /**
81 * Constructor.
82 *
83 * @param platformTools platform tools
84 * @param parent parent widget
85 * @param caption dialog title
86 * @param genreModel genre model
87 * @param trackDataModel track data to be filled with imported values,
88 * is passed with durations of files set
89 * @param importers server importers
90 * @param trackImporters server track importers
91 */
ImportDialog(IPlatformTools * platformTools,QWidget * parent,QString & caption,TrackDataModel * trackDataModel,GenreModel * genreModel,const QList<ServerImporter * > & importers,const QList<ServerTrackImporter * > & trackImporters)92 ImportDialog::ImportDialog(IPlatformTools* platformTools,
93 QWidget* parent, QString& caption,
94 TrackDataModel* trackDataModel,
95 GenreModel* genreModel,
96 const QList<ServerImporter*>& importers,
97 const QList<ServerTrackImporter*>& trackImporters)
98 : QDialog(parent), m_platformTools(platformTools),
99 m_autoStartSubDialog(-1), m_columnVisibility(0ULL),
100 m_trackDataModel(trackDataModel), m_importers(importers),
101 m_trackImporters(trackImporters)
102 {
103 setObjectName(QLatin1String("ImportDialog"));
104 setModal(false);
105 setWindowTitle(caption);
106 setSizeGripEnabled(true);
107
108 auto vlayout = new QVBoxLayout(this);
109
110 m_trackDataTable = new QTableView(this);
111 m_trackDataTable->setModel(m_trackDataModel);
112 m_trackDataTable->resizeColumnsToContents();
113 m_trackDataTable->setItemDelegateForColumn(
114 m_trackDataModel->columnForFrameType(Frame::FT_Genre),
115 new FrameItemDelegate(genreModel, this));
116 m_trackDataTable->verticalHeader()->setSectionsMovable(true);
117 m_trackDataTable->horizontalHeader()->setSectionsMovable(true);
118 m_trackDataTable->horizontalHeader()->setContextMenuPolicy(
119 Qt::CustomContextMenu);
120 connect(m_trackDataTable->verticalHeader(), &QHeaderView::sectionMoved,
121 this, &ImportDialog::moveTableRow);
122 connect(m_trackDataTable->horizontalHeader(),
123 &QWidget::customContextMenuRequested,
124 this, &ImportDialog::showTableHeaderContextMenu);
125 vlayout->addWidget(m_trackDataTable);
126
127 auto accuracyLayout = new QHBoxLayout;
128 QLabel* accuracyLabel = new QLabel(tr("Accuracy:"));
129 accuracyLayout->addWidget(accuracyLabel);
130 m_accuracyPercentLabel = new QLabel(QLatin1String("-"));
131 #if QT_VERSION >= 0x050b00
132 m_accuracyPercentLabel->setMinimumWidth(
133 m_accuracyPercentLabel->fontMetrics().horizontalAdvance(QLatin1String("100%")));
134 #else
135 m_accuracyPercentLabel->setMinimumWidth(
136 m_accuracyPercentLabel->fontMetrics().width(QLatin1String("100%")));
137 #endif
138 accuracyLayout->addWidget(m_accuracyPercentLabel);
139 QLabel* coverArtLabel = new QLabel(tr("Cover Art:"));
140 accuracyLayout->addWidget(coverArtLabel);
141 m_coverArtUrlLabel = new QLabel(QLatin1String(" -"));
142 m_coverArtUrlLabel->setSizePolicy(QSizePolicy::Ignored,
143 QSizePolicy::Preferred);
144 accuracyLayout->addWidget(m_coverArtUrlLabel, 1);
145 vlayout->addLayout(accuracyLayout);
146
147 auto butlayout = new QHBoxLayout;
148 QPushButton* fileButton = new QPushButton(tr("From F&ile/Clipboard..."));
149 fileButton->setAutoDefault(false);
150 butlayout->addWidget(fileButton);
151 QPushButton* tagsButton = new QPushButton(tr("From T&ags..."));
152 tagsButton->setAutoDefault(false);
153 butlayout->addWidget(tagsButton);
154 QPushButton* serverButton = new QPushButton(tr("&From Server..."));
155 serverButton->setAutoDefault(false);
156 butlayout->addWidget(serverButton);
157 m_serverComboBox = new QComboBox;
158 m_serverComboBox->setEditable(false);
159 const auto sis = m_importers;
160 for (const ServerImporter* si : sis) {
161 m_serverComboBox->addItem(QCoreApplication::translate("@default", si->name()));
162 }
163 const auto stis = m_trackImporters;
164 for (const ServerTrackImporter* si : stis) {
165 m_serverComboBox->addItem(QCoreApplication::translate("@default", si->name()));
166 }
167 butlayout->addWidget(m_serverComboBox);
168 if (m_serverComboBox->count() == 0) {
169 serverButton->hide();
170 m_serverComboBox->hide();
171 }
172 auto butspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding,
173 QSizePolicy::Minimum);
174 butlayout->addItem(butspacer);
175 QLabel* destLabel = new QLabel;
176 destLabel->setText(tr("D&estination:"));
177 butlayout->addWidget(destLabel);
178 m_destComboBox = new QComboBox;
179 m_destComboBox->setEditable(false);
180 const QList<QPair<Frame::TagVersion, QString> > tagVersions =
181 Frame::availableTagVersions();
182 for (auto it = tagVersions.constBegin(); it != tagVersions.constEnd(); ++it) {
183 m_destComboBox->addItem(it->second, it->first);
184 }
185 destLabel->setBuddy(m_destComboBox);
186 butlayout->addWidget(m_destComboBox);
187 auto revertButton = new QToolButton;
188 revertButton->setIcon(
189 m_platformTools->iconFromTheme(QLatin1String("document-revert")));
190 revertButton->setToolTip(tr("Revert"));
191 revertButton->setShortcut(QKeySequence::Undo);
192 connect(revertButton, &QAbstractButton::clicked,
193 this, &ImportDialog::changeTagDestination);
194 butlayout->addWidget(revertButton);
195 vlayout->addLayout(butlayout);
196
197 auto matchLayout = new QHBoxLayout;
198 m_mismatchCheckBox = new QCheckBox(
199 tr("Check maximum allowable time &difference (sec):"));
200 matchLayout->addWidget(m_mismatchCheckBox);
201 m_maxDiffSpinBox = new QSpinBox;
202 m_maxDiffSpinBox->setMaximum(9999);
203 matchLayout->addWidget(m_maxDiffSpinBox);
204 auto matchSpacer = new QSpacerItem(16, 0, QSizePolicy::Expanding,
205 QSizePolicy::Minimum);
206 matchLayout->addItem(matchSpacer);
207 QLabel* matchLabel = new QLabel(tr("Match with:"));
208 matchLayout->addWidget(matchLabel);
209 QPushButton* lengthButton = new QPushButton(tr("&Length"));
210 lengthButton->setAutoDefault(false);
211 matchLayout->addWidget(lengthButton);
212 QPushButton* trackButton = new QPushButton(tr("T&rack"));
213 trackButton->setAutoDefault(false);
214 matchLayout->addWidget(trackButton);
215 QPushButton* titleButton = new QPushButton(tr("&Title"));
216 titleButton->setAutoDefault(false);
217 matchLayout->addWidget(titleButton);
218 vlayout->addLayout(matchLayout);
219
220 connect(fileButton, &QAbstractButton::clicked,
221 this, &ImportDialog::fromText);
222 connect(tagsButton, &QAbstractButton::clicked,
223 this, &ImportDialog::fromTags);
224 connect(serverButton, &QAbstractButton::clicked,
225 this, &ImportDialog::fromServer);
226 connect(m_serverComboBox, static_cast<void (QComboBox::*)(int)>(
227 &QComboBox::activated), this, &ImportDialog::fromServer);
228 connect(lengthButton, &QAbstractButton::clicked,
229 this, &ImportDialog::matchWithLength);
230 connect(trackButton, &QAbstractButton::clicked,
231 this, &ImportDialog::matchWithTrack);
232 connect(titleButton, &QAbstractButton::clicked,
233 this, &ImportDialog::matchWithTitle);
234 connect(m_mismatchCheckBox, &QAbstractButton::toggled,
235 this, &ImportDialog::showPreview);
236 connect(m_maxDiffSpinBox, static_cast<void (QSpinBox::*)(int)>(
237 &QSpinBox::valueChanged), this, &ImportDialog::maxDiffChanged);
238 connect(this, &QDialog::finished, this, &ImportDialog::hideSubdialogs);
239
240 auto hlayout = new QHBoxLayout;
241 auto hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding,
242 QSizePolicy::Minimum);
243 QPushButton* helpButton = new QPushButton(tr("&Help"), this);
244 helpButton->setAutoDefault(false);
245 QPushButton* saveButton = new QPushButton(tr("&Save Settings"), this);
246 saveButton->setAutoDefault(false);
247 QPushButton* okButton = new QPushButton(tr("&OK"), this);
248 okButton->setAutoDefault(false);
249 QPushButton* cancelButton = new QPushButton(tr("&Cancel"), this);
250 cancelButton->setAutoDefault(false);
251 hlayout->addWidget(helpButton);
252 hlayout->addWidget(saveButton);
253 hlayout->addItem(hspacer);
254 hlayout->addWidget(okButton);
255 hlayout->addWidget(cancelButton);
256 connect(helpButton, &QAbstractButton::clicked,
257 this, &ImportDialog::showHelp);
258 connect(saveButton, &QAbstractButton::clicked,
259 this, &ImportDialog::saveConfig);
260 connect(okButton, &QAbstractButton::clicked, this, &QDialog::accept);
261 connect(cancelButton, &QAbstractButton::clicked, this, &QDialog::reject);
262 vlayout->addLayout(hlayout);
263 }
264
265 /**
266 * Destructor.
267 */
~ImportDialog()268 ImportDialog::~ImportDialog()
269 {
270 // Must not be inline because of forwared declared QScopedPointer.
271 }
272
273 /**
274 * Import from server and preview in table.
275 */
fromServer()276 void ImportDialog::fromServer()
277 {
278 if (m_serverComboBox)
279 displayServerImportDialog(m_serverComboBox->currentIndex());
280 }
281
282 /**
283 * Import from text.
284 */
fromText()285 void ImportDialog::fromText()
286 {
287 if (!m_textImportDialog) {
288 m_textImportDialog.reset(new TextImportDialog(
289 m_platformTools, this, m_trackDataModel));
290 connect(m_textImportDialog.data(), &TextImportDialog::trackDataUpdated,
291 this, &ImportDialog::showPreview);
292 }
293 m_textImportDialog->clear();
294 m_textImportDialog->show();
295 }
296
297 /**
298 * Import from tags.
299 */
fromTags()300 void ImportDialog::fromTags()
301 {
302 if (!m_tagImportDialog) {
303 m_tagImportDialog.reset(new TagImportDialog(this, m_trackDataModel));
304 connect(m_tagImportDialog.data(), &TagImportDialog::trackDataUpdated,
305 this, &ImportDialog::showPreview);
306 }
307 m_tagImportDialog->clear();
308 m_tagImportDialog->show();
309 }
310
311 /**
312 * Display server import dialog.
313 *
314 * @param importerIdx importer index, if invalid but not negative the
315 * MusicBrainz Fingerprint dialog is displayed
316 */
displayServerImportDialog(int importerIdx)317 void ImportDialog::displayServerImportDialog(int importerIdx)
318 {
319 if (importerIdx >= 0) {
320 if (importerIdx < m_importers.size()) {
321 displayServerImportDialog(m_importers.at(importerIdx));
322 } else if (importerIdx - m_importers.size() < m_trackImporters.size()) {
323 displayServerTrackImportDialog(
324 m_trackImporters.at(importerIdx - m_importers.size()));
325 }
326 }
327 }
328
329 /**
330 * Display server import dialog.
331 *
332 * @param source import source
333 */
displayServerImportDialog(ServerImporter * source)334 void ImportDialog::displayServerImportDialog(ServerImporter* source)
335 {
336 if (!m_serverImportDialog) {
337 m_serverImportDialog.reset(new ServerImportDialog(this));
338 connect(m_serverImportDialog.data(), &ServerImportDialog::trackDataUpdated,
339 this, &ImportDialog::showPreview);
340 connect(m_serverImportDialog.data(), &QDialog::accepted,
341 this, &ImportDialog::onServerImportDialogClosed);
342 }
343 m_serverImportDialog->setImportSource(source);
344 m_serverImportDialog->setArtistAlbum(
345 m_trackDataModel->trackData().getArtist(),
346 m_trackDataModel->trackData().getAlbum());
347 m_serverImportDialog->show();
348 }
349
350 /**
351 * Import from track server and preview in table.
352 *
353 * @param source import source
354 */
displayServerTrackImportDialog(ServerTrackImporter * source)355 void ImportDialog::displayServerTrackImportDialog(ServerTrackImporter* source)
356 {
357 if (!m_serverTrackImportDialog) {
358 m_serverTrackImportDialog.reset(new ServerTrackImportDialog(this, m_trackDataModel));
359 connect(m_serverTrackImportDialog.data(), &ServerTrackImportDialog::trackDataUpdated,
360 this, &ImportDialog::showPreview);
361 }
362 m_serverTrackImportDialog->setImportSource(source);
363 m_serverTrackImportDialog->initTable();
364 m_serverTrackImportDialog->exec();
365 }
366
367 /**
368 * Hide subdialogs.
369 */
hideSubdialogs()370 void ImportDialog::hideSubdialogs()
371 {
372 if (m_serverImportDialog)
373 m_serverImportDialog->hide();
374 if (m_textImportDialog)
375 m_textImportDialog->hide();
376 if (m_tagImportDialog)
377 m_tagImportDialog->hide();
378 }
379
380 /**
381 * Shows the dialog as a modeless dialog.
382 *
383 * @param importerIndex index of importer to use, -1 for none
384 */
showWithSubDialog(int importerIndex)385 void ImportDialog::showWithSubDialog(int importerIndex)
386 {
387 m_autoStartSubDialog = importerIndex;
388
389 if (importerIndex >= 0 && importerIndex < m_serverComboBox->count()) {
390 m_serverComboBox->setCurrentIndex(importerIndex);
391 }
392
393 show();
394 if (m_autoStartSubDialog >= 0) {
395 displayServerImportDialog(m_autoStartSubDialog);
396 }
397 }
398
399 /**
400 * Clear dialog data.
401 */
clear()402 void ImportDialog::clear()
403 {
404 const ImportConfig& importCfg = ImportConfig::instance();
405 m_serverComboBox->setCurrentIndex(importCfg.importServer());
406 Frame::TagVersion importDest = importCfg.importDest();
407 int index = m_destComboBox->findData(importDest);
408 m_destComboBox->setCurrentIndex(index);
409
410 if (!m_trackDataModel->trackData().isTagSupported(
411 Frame::tagNumberFromMask(importDest))) {
412 index = m_destComboBox->findData(Frame::TagV2);
413 m_destComboBox->setCurrentIndex(index);
414 changeTagDestination();
415 }
416
417 m_mismatchCheckBox->setChecked(importCfg.enableTimeDifferenceCheck());
418 m_maxDiffSpinBox->setValue(importCfg.maxTimeDifference());
419 m_columnVisibility = importCfg.importVisibleColumns();
420
421 const auto frameTypes = checkableFrameTypes();
422 for (int frameType : frameTypes) {
423 if (frameType < 64) {
424 int column = m_trackDataModel->columnForFrameType(frameType);
425 if (column != -1) {
426 m_trackDataTable->setColumnHidden(
427 column, (m_columnVisibility & (1ULL << frameType)) == 0ULL);
428 }
429 }
430 }
431
432 if (!importCfg.importWindowGeometry().isEmpty()) {
433 restoreGeometry(importCfg.importWindowGeometry());
434 }
435
436 showPreview();
437 }
438
439 /**
440 * Show fields to import in text as preview in table.
441 */
showPreview()442 void ImportDialog::showPreview()
443 {
444 // make time difference check
445 bool diffCheckEnable;
446 int maxDiff;
447 getTimeDifferenceCheck(diffCheckEnable, maxDiff);
448 m_trackDataModel->setTimeDifferenceCheck(diffCheckEnable, maxDiff);
449 m_trackDataTable->scrollToTop();
450 m_trackDataTable->resizeColumnsToContents();
451 m_trackDataTable->resizeRowsToContents();
452
453 int accuracy = m_trackDataModel->calculateAccuracy();
454 m_accuracyPercentLabel->setText(accuracy >= 0 && accuracy <= 100
455 ? QString::number(accuracy) + QLatin1Char('%')
456 : QLatin1String("-"));
457 QUrl coverArtUrl = m_trackDataModel->getTrackData().getCoverArtUrl();
458 m_coverArtUrlLabel->setText(coverArtUrl.isEmpty() ? QLatin1String("-")
459 : coverArtUrl.toString());
460 }
461
462 /**
463 * Called when server import dialog is closed.
464 */
onServerImportDialogClosed()465 void ImportDialog::onServerImportDialogClosed()
466 {
467 // This is used to prevent that the import dialog is brought behind the
468 // main window when the server import dialog is closed, which happened
469 // with Qt 5 on Mac OS X.
470 show();
471 raise();
472 activateWindow();
473 }
474
475 /**
476 * Get import destination.
477 *
478 * @return TagV1, TagV2 or TagV2V1 for ID3v1, ID3v2 or both.
479 */
getDestination() const480 Frame::TagVersion ImportDialog::getDestination() const
481 {
482 return Frame::tagVersionCast(
483 m_destComboBox->itemData(m_destComboBox->currentIndex()).toInt());
484 }
485
486 /**
487 * Show help.
488 */
showHelp()489 void ImportDialog::showHelp()
490 {
491 ContextHelp::displayHelp(QLatin1String("import"));
492 }
493
494 /**
495 * Save the local settings to the configuration.
496 */
saveConfig()497 void ImportDialog::saveConfig()
498 {
499 ImportConfig& importCfg = ImportConfig::instance();
500 importCfg.setImportDest(Frame::tagVersionCast(
501 m_destComboBox->itemData(m_destComboBox->currentIndex()).toInt()));
502
503 importCfg.setImportServer(m_serverComboBox->currentIndex());
504 bool enable;
505 int maxDiff;
506 getTimeDifferenceCheck(enable, maxDiff);
507 importCfg.setEnableTimeDifferenceCheck(enable);
508 importCfg.setMaxTimeDifference(maxDiff);
509 importCfg.setImportVisibleColumns(m_columnVisibility);
510
511 importCfg.setImportWindowGeometry(saveGeometry());
512 }
513
514 /**
515 * Get time difference check configuration.
516 *
517 * @param enable true if check is enabled
518 * @param maxDiff maximum allowed time difference
519 */
getTimeDifferenceCheck(bool & enable,int & maxDiff) const520 void ImportDialog::getTimeDifferenceCheck(bool& enable, int& maxDiff) const
521 {
522 enable = m_mismatchCheckBox->isChecked();
523 maxDiff = m_maxDiffSpinBox->value();
524 }
525
526 /**
527 * Called when the maximum time difference value is changed.
528 */
maxDiffChanged()529 void ImportDialog::maxDiffChanged() {
530 if (m_mismatchCheckBox->isChecked()) {
531 showPreview();
532 }
533 }
534
535 /**
536 * Move a table row.
537 *
538 * The first parameter @a section is not used.
539 * @param fromIndex index of position moved from
540 * @param fromIndex index of position moved to
541 */
moveTableRow(int,int fromIndex,int toIndex)542 void ImportDialog::moveTableRow(int, int fromIndex, int toIndex) {
543 auto vHeader = qobject_cast<QHeaderView*>(sender());
544 if (vHeader) {
545 // revert movement, but avoid recursion
546 disconnect(vHeader, &QHeaderView::sectionMoved, nullptr, nullptr);
547 vHeader->moveSection(toIndex, fromIndex);
548 connect(vHeader, &QHeaderView::sectionMoved,
549 this, &ImportDialog::moveTableRow);
550 }
551
552 // Allow dragging multiple rows when pressing Ctrl by adding selected rows.
553 ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData());
554 auto numTracks = static_cast<int>(trackDataVector.size());
555
556 int diff = toIndex - fromIndex;
557 QList<int> fromList;
558 if (fromIndex >= 0 && fromIndex < numTracks &&
559 toIndex >= 0 && toIndex < numTracks) {
560 fromList.append(fromIndex);
561 }
562 const QModelIndexList selectedRows =
563 m_trackDataTable->selectionModel()->selectedRows();
564 for (const QModelIndex& index : selectedRows) {
565 int from = index.row();
566 int to = from + diff;
567 if (!fromList.contains(from) &&
568 from >= 0 && from < numTracks &&
569 to >= 0 && to < numTracks) {
570 fromList.append(from);
571 }
572 }
573 std::sort(fromList.begin(), fromList.end());
574
575 for (auto it = fromList.constBegin(); it != fromList.constEnd(); ++it) {
576 fromIndex = *it;
577 toIndex = fromIndex + diff;
578 // swap elements but keep file durations and names
579 ImportTrackData fromData(trackDataVector[fromIndex]);
580 ImportTrackData toData(trackDataVector[toIndex]);
581 trackDataVector[fromIndex].setFrameCollection(toData.getFrameCollection());
582 trackDataVector[toIndex].setFrameCollection(fromData.getFrameCollection());
583 trackDataVector[fromIndex].setImportDuration(toData.getImportDuration());
584 trackDataVector[toIndex].setImportDuration(fromData.getImportDuration());
585 }
586 if (!fromList.isEmpty()) {
587 m_trackDataModel->setTrackData(trackDataVector);
588 // redisplay the table
589 showPreview();
590 }
591 }
592
593 /**
594 * Called when the destination combo box value is changed.
595 */
changeTagDestination()596 void ImportDialog::changeTagDestination()
597 {
598 ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData());
599 trackDataVector.readTags(getDestination());
600 m_trackDataModel->setTrackData(trackDataVector);
601 showPreview();
602 }
603
604 /**
605 * Match import data with length.
606 */
matchWithLength()607 void ImportDialog::matchWithLength()
608 {
609 bool diffCheckEnable;
610 int maxDiff;
611 getTimeDifferenceCheck(diffCheckEnable, maxDiff);
612 if (TrackDataMatcher::matchWithLength(m_trackDataModel, diffCheckEnable, maxDiff))
613 showPreview();
614 }
615
616 /**
617 * Match import data with track number.
618 */
matchWithTrack()619 void ImportDialog::matchWithTrack()
620 {
621 if (TrackDataMatcher::matchWithTrack(m_trackDataModel))
622 showPreview();
623 }
624
625 /**
626 * Match import data with title.
627 */
matchWithTitle()628 void ImportDialog::matchWithTitle()
629 {
630 if (TrackDataMatcher::matchWithTitle(m_trackDataModel))
631 showPreview();
632 }
633
634 /**
635 * Display custom context menu for horizontal table header.
636 *
637 * @param pos position where context menu is drawn on screen
638 */
showTableHeaderContextMenu(const QPoint & pos)639 void ImportDialog::showTableHeaderContextMenu(const QPoint& pos)
640 {
641 if (QWidget* widget = qobject_cast<QWidget*>(sender())) {
642 QMenu menu(widget);
643 const auto frameTypes = checkableFrameTypes();
644 for (int frameType : frameTypes) {
645 int column = m_trackDataModel->columnForFrameType(frameType);
646 if (column != -1) {
647 auto action = new QAction(&menu);
648 action->setText(
649 m_trackDataModel->headerData(column, Qt::Horizontal).toString());
650 action->setData(frameType);
651 action->setCheckable(true);
652 action->setChecked((m_columnVisibility & (1ULL << frameType)) != 0ULL);
653 connect(action, &QAction::triggered,
654 this, &ImportDialog::toggleTableColumnVisibility);
655 menu.addAction(action);
656 }
657 }
658 menu.setMouseTracking(true);
659 menu.exec(widget->mapToGlobal(pos));
660 }
661 }
662
663 /**
664 * Toggle visibility of table column.
665 *
666 * @param visible true to make column visible
667 */
toggleTableColumnVisibility(bool visible)668 void ImportDialog::toggleTableColumnVisibility(bool visible)
669 {
670 if (auto action = qobject_cast<QAction*>(sender())) {
671 bool ok;
672 int frameType = action->data().toInt(&ok);
673 if (ok && frameType < 64) {
674 if (visible) {
675 m_columnVisibility |= 1ULL << frameType;
676 } else {
677 m_columnVisibility &= ~(1ULL << frameType);
678 }
679 int column = m_trackDataModel->columnForFrameType(frameType);
680 if (column != -1) {
681 m_trackDataTable->setColumnHidden(column, !visible);
682 }
683 }
684 if (visible) {
685 m_trackDataTable->resizeColumnsToContents();
686 }
687 }
688 }
689