File indexing completed on 2024-03-24 16:20:38

0001 /* AUDEX CDDA EXTRACTOR
0002  * SPDX-FileCopyrightText: Copyright (C) 2007 Marco Nelles
0003  * <https://userbase.kde.org/Audex>
0004  *
0005  * SPDX-License-Identifier: GPL-3.0-or-later
0006  */
0007 
0008 #include "mainwindow.h"
0009 #include "widgets/devicewidget.h"
0010 
0011 #include <QMenu>
0012 #include <QWidgetAction>
0013 
0014 class CDDATreeView : public QTreeView
0015 {
0016 public:
0017     CDDATreeView(QWidget *parent = nullptr)
0018         : QTreeView(parent)
0019     {
0020     }
0021 
0022 protected:
0023     void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint) override
0024     {
0025         QTreeView::closeEditor(editor, hint);
0026         if ((this->currentIndex().row() < this->model()->rowCount() - 1) && (hint == QAbstractItemDelegate::SubmitModelCache)) {
0027             QTreeView::closeEditor(nullptr, QAbstractItemDelegate::EditNextItem);
0028         }
0029     }
0030 };
0031 
0032 MainWindow::MainWindow(QWidget *parent)
0033     : KXmlGuiWindow(parent)
0034 {
0035     profile_model = new ProfileModel(this);
0036     if (!profile_model) {
0037         qDebug() << "Unable to create ProfileModel object. Low mem?";
0038         ErrorDialog::show(this,
0039                           i18n("Unable to create ProfileModel object."),
0040                           i18n("Internal error. Check your hardware. If all okay please make bug report."));
0041         return;
0042     }
0043     if (profile_model->lastError().isValid()) {
0044         ErrorDialog::show(this, profile_model->lastError().message(), profile_model->lastError().details());
0045         return;
0046     }
0047 
0048     bool updated = firstStart();
0049 
0050     cdda_model = new CDDAModel(this);
0051     if (!cdda_model) {
0052         qDebug() << "Unable to create CDDAModel object. Low mem?";
0053         ErrorDialog::show(this, i18n("Unable to create CDDAModel object."), i18n("Internal error. Check your hardware. If all okay please make bug report."));
0054         return;
0055     }
0056     if (cdda_model->lastError().isValid()) {
0057         ErrorDialog::show(this, cdda_model->lastError().message(), cdda_model->lastError().details());
0058         return;
0059     }
0060 
0061     connect(cdda_model, SIGNAL(audioDiscDetected()), this, SLOT(new_audio_disc_detected()));
0062     connect(cdda_model, SIGNAL(audioDiscRemoved()), this, SLOT(audio_disc_removed()));
0063 
0064     connect(cdda_model, SIGNAL(cddbLookupStarted()), this, SLOT(cddb_lookup_start()));
0065     connect(cdda_model, SIGNAL(cddbLookupDone(const bool)), this, SLOT(cddb_lookup_done(const bool)));
0066     connect(cdda_model, SIGNAL(cddbDataModified()), this, SLOT(enable_cddb_submit()));
0067     connect(cdda_model, SIGNAL(cddbDataModified()), this, SLOT(update_layout()));
0068     connect(cdda_model, SIGNAL(cddbDataSubmited(bool)), this, SLOT(enable_cddb_submit(bool)));
0069 
0070     connect(profile_model, SIGNAL(profilesRemovedOrInserted()), this, SLOT(update_profile_action()));
0071     connect(profile_model, SIGNAL(currentProfileIndexChanged(int)), this, SLOT(update_profile_action(int)));
0072 
0073     setup_actions();
0074     setup_layout();
0075     setupGUI();
0076 
0077     enable_layout(false);
0078 
0079     if (updated) {
0080         update();
0081         resize(650, 500);
0082     }
0083 
0084     device_widget = nullptr;
0085 }
0086 
0087 bool MainWindow::firstStart()
0088 {
0089     if (Preferences::firstStart()) {
0090         profile_model->autoCreate();
0091         Preferences::setFirstStart(false);
0092         Preferences::self()->save();
0093         return true;
0094     }
0095 
0096     return false;
0097 }
0098 
0099 MainWindow::~MainWindow()
0100 {
0101     delete profile_model;
0102     delete cdda_model;
0103 }
0104 
0105 void MainWindow::eject()
0106 {
0107     qDebug() << "eject requested";
0108     cdda_model->eject();
0109 }
0110 
0111 void MainWindow::cddb_lookup()
0112 {
0113     cdda_model->lookupCDDB();
0114 }
0115 
0116 void MainWindow::cddb_submit()
0117 {
0118     QStringList toc = cdda_model->cdio()->prettyTOC();
0119     for (int i = 0; i < toc.size(); ++i)
0120         qDebug() << toc.at(i);
0121     return;
0122     if (!cdda_model->submitCDDB()) {
0123         ErrorDialog::show(this, cdda_model->lastError().message(), cdda_model->lastError().details());
0124     }
0125 }
0126 
0127 void MainWindow::rip()
0128 {
0129     if (cdda_model->empty()) {
0130         if (KMessageBox::warningTwoActions(this,
0131                                            i18n("No disc information set. Do you really want to continue?"),
0132                                            i18n("Disc information not found"),
0133                                            KStandardGuiItem::cont(),
0134                                            KStandardGuiItem::cancel(),
0135                                            "no_disc_info_warn")
0136             == KMessageBox::SecondaryAction)
0137             return;
0138     }
0139 
0140     if ((profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_SF_INDEX)).toBool())
0141         && (cdda_model->numOfAudioTracksInSelection() < cdda_model->numOfAudioTracks())) {
0142         if (KMessageBox::warningTwoActions(this,
0143                                            i18n("Single file rip selected but not all audio tracks to rip selected. Do you really want to continue?"),
0144                                            i18n("Not all audio tracks selected for single file rip"),
0145                                            KStandardGuiItem::cont(),
0146                                            KStandardGuiItem::cancel(),
0147                                            "singlefile_selection_warn")
0148             == KMessageBox::SecondaryAction)
0149             return;
0150     }
0151 
0152     if ((profile_model->isSelectedEncoderWithEmbedCover()
0153          || (profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_SC_INDEX)).toBool()))
0154         && cdda_model->isCoverEmpty()) {
0155         if (KMessageBox::warningTwoActions(this,
0156                                            i18n("No cover was set. Do you really want to continue?"),
0157                                            i18n("Cover is empty"),
0158                                            KStandardGuiItem::cont(),
0159                                            KStandardGuiItem::cancel(),
0160                                            "empty_cover_warn")
0161             == KMessageBox::SecondaryAction)
0162             return;
0163     }
0164 
0165     auto *dialog = new ExtractingProgressDialog(profile_model, cdda_model, this);
0166 
0167     dialog->setWindowModality(Qt::ApplicationModal);
0168 
0169     dialog->exec();
0170 
0171     delete dialog;
0172 }
0173 
0174 void MainWindow::configure()
0175 {
0176     if (KConfigDialog::showDialog("settings"))
0177         return;
0178 
0179     KConfigDialog *dialog = new KConfigDialog(this, "settings", Preferences::self());
0180 
0181     KPageWidgetItem *generalPage = dialog->addPage(new generalSettingsWidget(), i18n("General settings"));
0182     generalPage->setIcon(QIcon(QApplication::windowIcon()));
0183 
0184     device_widget = new deviceWidget();
0185     KPageWidgetItem *devicePage = dialog->addPage(device_widget, i18n("Device settings"));
0186     devicePage->setIcon(QIcon::fromTheme("drive-optical"));
0187 
0188     if (cdda_model && cdda_model->cdio())
0189         device_widget->setDeviceInfo(cdda_model->cdio()->getVendor(),
0190                                      cdda_model->cdio()->getModel(),
0191                                      cdda_model->cdio()->getRevision(),
0192                                      cdda_model->cdio()->getDriveCapabilities().contains(READ_MCN),
0193                                      cdda_model->cdio()->getDriveCapabilities().contains(READ_ISRC),
0194                                      cdda_model->cdio()->getDriveCapabilities().contains(C2_ERRS));
0195 
0196     KPageWidgetItem *profilePage = dialog->addPage(new profileWidget(profile_model), i18n("Profiles"));
0197     profilePage->setIcon(QIcon::fromTheme("document-multiple"));
0198 
0199     KPluginMetaData info(QStringLiteral("plasma/kcms/systemsettings_qwidgets/kcm_cddb"));
0200     KCModule *m = KCModuleLoader::loadModule(info);
0201     if (m) {
0202         m->load();
0203         auto *cfg = new KCDDB::Config();
0204         cfg->load();
0205         dialog->addPage(m->widget(), cfg, i18n("CDDB settings"), QStringLiteral("text-xmcd"));
0206     }
0207 
0208     KPageWidgetItem *remoteServerPage = dialog->addPage(new remoteServerSettingsWidget(), i18n("Remote Server"));
0209     remoteServerPage->setIcon(QIcon::fromTheme("network-server"));
0210 
0211     connect(dialog, SIGNAL(settingsChanged(const QString &)), this, SLOT(configuration_updated(const QString &)));
0212 
0213     dialog->exec();
0214 }
0215 
0216 void MainWindow::edit()
0217 {
0218     cdda_header_widget->edit_data();
0219 }
0220 
0221 void MainWindow::new_audio_disc_detected()
0222 {
0223     enable_layout(true);
0224     resizeColumns();
0225     if (Preferences::cddbLookupAuto()) {
0226         qDebug() << "Performing CDDB auto lookup";
0227         QTimer::singleShot(0, this, SLOT(cddb_lookup()));
0228     }
0229 
0230     update_layout();
0231 
0232     if (device_widget) {
0233         if (cdda_model && cdda_model->cdio()) {
0234             device_widget->setDeviceInfo(cdda_model->cdio()->getVendor(),
0235                                          cdda_model->cdio()->getModel(),
0236                                          cdda_model->cdio()->getRevision(),
0237                                          cdda_model->cdio()->getDriveCapabilities().contains(READ_MCN),
0238                                          cdda_model->cdio()->getDriveCapabilities().contains(READ_ISRC),
0239                                          cdda_model->cdio()->getDriveCapabilities().contains(C2_ERRS));
0240         } else {
0241             device_widget->clearDeviceInfo();
0242         }
0243     }
0244 }
0245 
0246 void MainWindow::audio_disc_removed()
0247 {
0248     enable_layout(false);
0249 
0250     update_layout();
0251 
0252     if (device_widget)
0253         device_widget->clearDeviceInfo();
0254 }
0255 
0256 void MainWindow::cddb_lookup_start()
0257 {
0258 }
0259 
0260 void MainWindow::cddb_lookup_done(const bool successful)
0261 {
0262     if (!successful) {
0263         ErrorDialog::show(this,
0264                           i18n("CDDB lookup failed, with the following error:\n%1", cdda_model->lastError().message()),
0265                           cdda_model->lastError().details(),
0266                           i18n("CDD Lookup Failure"));
0267     }
0268     update_layout();
0269     disable_cddb_submit();
0270     // if (Preferences::coverLookupAuto())
0271     //     cdda_header_widget->fetchCover();
0272 }
0273 
0274 void MainWindow::update_layout()
0275 {
0276     if (!cdda_model->isVarious()) {
0277         cdda_tree_view->hideColumn(CDDA_MODEL_COLUMN_ARTIST_INDEX);
0278     } else {
0279         cdda_tree_view->showColumn(CDDA_MODEL_COLUMN_ARTIST_INDEX);
0280     }
0281     resizeColumns();
0282     actionCollection()->action("selectall")->setEnabled(cdda_model->selectedTracks().count() < cdda_model->numOfAudioTracks());
0283     actionCollection()->action("selectnone")->setEnabled(cdda_model->selectedTracks().count() > 0);
0284 }
0285 
0286 void MainWindow::enable_layout(bool enabled)
0287 {
0288     layout_enabled = enabled;
0289     cdda_tree_view->setEnabled(enabled);
0290     cdda_header_dock->setEnabled(enabled);
0291     cdda_header_widget->setEnabled(enabled);
0292     actionCollection()->action("profile_label")->setEnabled((profile_model->rowCount() > 0) && (enabled));
0293     profile_combobox->setEnabled((profile_model->rowCount() > 0) && (enabled));
0294     actionCollection()->action("profile")->setEnabled((profile_model->rowCount() > 0) && (enabled));
0295     actionCollection()->action("cddbfetch")->setEnabled(enabled);
0296     if (cdda_model->isModified())
0297         actionCollection()->action("cddbsubmit")->setEnabled(enabled);
0298     else
0299         actionCollection()->action("cddbsubmit")->setEnabled(false);
0300     actionCollection()->action("edit")->setEnabled(enabled);
0301     actionCollection()->action("eject")->setEnabled(enabled);
0302     actionCollection()->action("rip")->setEnabled(enabled);
0303     actionCollection()->action("splittitles")->setEnabled(enabled);
0304     actionCollection()->action("swapartistsandtitles")->setEnabled(enabled);
0305     actionCollection()->action("capitalize")->setEnabled(enabled);
0306     actionCollection()->action("autofillartists")->setEnabled(enabled);
0307     actionCollection()->action("selectall")->setEnabled(enabled);
0308     actionCollection()->action("selectnone")->setEnabled(enabled);
0309     actionCollection()->action("invertselection")->setEnabled(enabled);
0310 }
0311 
0312 void MainWindow::enable_cddb_submit(bool enabled)
0313 {
0314     actionCollection()->action("cddbsubmit")->setEnabled(enabled);
0315 }
0316 
0317 void MainWindow::disable_cddb_submit()
0318 {
0319     actionCollection()->action("cddbsubmit")->setEnabled(false);
0320 }
0321 
0322 void MainWindow::configuration_updated(const QString &dialog_name)
0323 {
0324     Q_UNUSED(dialog_name);
0325     Preferences::self()->save();
0326 }
0327 
0328 void MainWindow::current_profile_updated_from_ui(int row)
0329 {
0330     if (row >= 0) {
0331         profile_model->blockSignals(true);
0332         profile_model->setRowAsCurrentProfileIndex(row);
0333         profile_model->blockSignals(false);
0334     }
0335 }
0336 
0337 void MainWindow::update_profile_action(int index)
0338 {
0339     if (index == -1) {
0340         if (layout_enabled) {
0341             actionCollection()->action("profile_label")->setEnabled(false);
0342             actionCollection()->action("profile")->setEnabled(false);
0343         }
0344     } else {
0345         if (layout_enabled) {
0346             actionCollection()->action("profile_label")->setEnabled(true);
0347             actionCollection()->action("profile")->setEnabled(true);
0348         }
0349         profile_combobox->setCurrentIndex(profile_model->getRowByIndex(index));
0350     }
0351 }
0352 
0353 void MainWindow::update_profile_action()
0354 {
0355     // When the Profile model emits 'reset' the profile combo clears its current settings.
0356     // Therefore, we need to try and reset these...
0357     if (profile_combobox->currentText().isEmpty()) {
0358         profile_combobox->setCurrentIndex(profile_model->currentProfileRow());
0359     }
0360 
0361     if (layout_enabled) {
0362         actionCollection()->action("profile_label")->setEnabled(profile_model->rowCount() > 0);
0363         actionCollection()->action("profile")->setEnabled(profile_model->rowCount() > 0);
0364     }
0365 }
0366 
0367 void MainWindow::split_titles()
0368 {
0369     bool ok;
0370     QString divider = QInputDialog::getText(this,
0371                                             i18n("Split titles"),
0372                                             i18n("Please set a divider string. Be aware of empty spaces.\n\nDivider:"),
0373                                             QLineEdit::Normal,
0374                                             " - ",
0375                                             &ok);
0376     if (ok && !divider.isEmpty()) {
0377         cdda_model->splitTitleOfTracks(divider);
0378     }
0379 }
0380 
0381 void MainWindow::swap_artists_and_titles()
0382 {
0383     if (KMessageBox::warningTwoActions(this,
0384                                        i18n("Do you really want to swap all artists and titles?"),
0385                                        i18n("Swap artists and titles"),
0386                                        KStandardGuiItem::ok(),
0387                                        KStandardGuiItem::cancel(),
0388                                        "no_swap_artists_and_titles_warn")
0389         == KMessageBox::SecondaryAction)
0390         return;
0391 
0392     cdda_model->swapArtistAndTitle();
0393     cdda_model->swapArtistAndTitleOfTracks();
0394 }
0395 
0396 void MainWindow::capitalize()
0397 {
0398     if (KMessageBox::warningTwoActions(this,
0399                                        i18n("Do you really want to capitalize all artists and titles?"),
0400                                        i18n("Capitalize artists and titles"),
0401                                        KStandardGuiItem::ok(),
0402                                        KStandardGuiItem::cancel(),
0403                                        "no_capitalize_warn")
0404         == KMessageBox::SecondaryAction)
0405         return;
0406 
0407     cdda_model->capitalizeHeader();
0408     cdda_model->capitalizeTracks();
0409 }
0410 
0411 void MainWindow::auto_fill_artists()
0412 {
0413     if (KMessageBox::warningTwoActions(this,
0414                                        i18n("Do you really want to autofill track artists?"),
0415                                        i18n("Autofill artists"),
0416                                        KStandardGuiItem::ok(),
0417                                        KStandardGuiItem::cancel(),
0418                                        "no_autofill_warn")
0419         == KMessageBox::SecondaryAction)
0420         return;
0421 
0422     cdda_model->setTitleArtistsFromHeader();
0423 }
0424 
0425 void MainWindow::toggle(const QModelIndex &idx)
0426 {
0427     if (idx.isValid() && (idx.column() == CDDA_MODEL_COLUMN_RIP_INDEX)) {
0428         cdda_model->toggle(idx.row());
0429         cdda_tree_view->update(idx);
0430     }
0431 }
0432 
0433 void MainWindow::resizeColumns()
0434 {
0435     for (int i = 0; i < CDDA_MODEL_COLUMN_COUNT; ++i)
0436         cdda_tree_view->resizeColumnToContents(i);
0437 }
0438 
0439 void MainWindow::setup_actions()
0440 {
0441     auto *ejectAction = new QAction(this);
0442     ejectAction->setText(i18n("Eject"));
0443     ejectAction->setIcon(QIcon::fromTheme("media-eject"));
0444     actionCollection()->addAction("eject", ejectAction);
0445     actionCollection()->setDefaultShortcut(ejectAction, Qt::CTRL + Qt::Key_E);
0446     connect(ejectAction, SIGNAL(triggered(bool)), this, SLOT(eject()));
0447 
0448     profile_label = new QLabel(this);
0449     profile_label->setText(i18n("Profile:"));
0450     profile_combobox = new KComboBox(this);
0451     profile_combobox->setModel(profile_model);
0452     profile_combobox->setModelColumn(1);
0453     profile_combobox->setMinimumWidth(80);
0454     profile_combobox->setMaximumWidth(220);
0455     profile_combobox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
0456     profile_combobox->resize(QSize(220, profile_combobox->height()));
0457     profile_combobox->setCurrentIndex(profile_model->currentProfileRow());
0458     connect(profile_combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(current_profile_updated_from_ui(int)));
0459 
0460     auto *plabelAction = new QWidgetAction(this);
0461     plabelAction->setText(i18n("&Profile:"));
0462     plabelAction->setDefaultWidget(profile_label);
0463     profile_label->setBuddy(profile_combobox);
0464     actionCollection()->addAction("profile_label", plabelAction);
0465 
0466     auto *profileAction = new QWidgetAction(this);
0467     profileAction->setText(i18n("Profile"));
0468     profileAction->setDefaultWidget(profile_combobox);
0469     actionCollection()->addAction("profile", profileAction);
0470     actionCollection()->setDefaultShortcut(profileAction, Qt::Key_F6);
0471     actionCollection()->setShortcutsConfigurable(profileAction, false);
0472     update_profile_action();
0473 
0474     auto *cddbLookupAction = new QAction(this);
0475     cddbLookupAction->setText(i18n("Fetch"));
0476     cddbLookupAction->setIcon(QIcon::fromTheme("view-list-text"));
0477     actionCollection()->addAction("cddbfetch", cddbLookupAction);
0478     actionCollection()->setDefaultShortcut(cddbLookupAction, Qt::CTRL + Qt::Key_F);
0479     connect(cddbLookupAction, SIGNAL(triggered(bool)), this, SLOT(cddb_lookup()));
0480 
0481     auto *cddbSubmitAction = new QAction(this);
0482     cddbSubmitAction->setText(i18n("Submit"));
0483     actionCollection()->addAction("cddbsubmit", cddbSubmitAction);
0484     actionCollection()->setDefaultShortcut(cddbSubmitAction, Qt::CTRL + Qt::Key_S);
0485     connect(cddbSubmitAction, SIGNAL(triggered(bool)), this, SLOT(cddb_submit()));
0486 
0487     auto *editAction = new QAction(this);
0488     editAction->setText(i18n("Edit"));
0489     editAction->setIcon(QIcon::fromTheme("document-edit"));
0490     actionCollection()->addAction("edit", editAction);
0491     actionCollection()->setDefaultShortcut(editAction, Qt::CTRL + Qt::Key_D);
0492     connect(editAction, SIGNAL(triggered(bool)), this, SLOT(edit()));
0493 
0494     auto *extractAction = new QAction(this);
0495     extractAction->setText(i18n("Rip..."));
0496     extractAction->setIcon(QIcon::fromTheme("media-optical-audio"));
0497     actionCollection()->addAction("rip", extractAction);
0498     actionCollection()->setDefaultShortcut(extractAction, Qt::CTRL + Qt::Key_X);
0499     connect(extractAction, SIGNAL(triggered(bool)), this, SLOT(rip()));
0500 
0501     actionCollection()->addAction("preferences", KStandardAction::preferences(this, SLOT(configure()), this));
0502 
0503     auto *splitTitlesAction = new QAction(this);
0504     splitTitlesAction->setText(i18n("Split Titles..."));
0505     actionCollection()->addAction("splittitles", splitTitlesAction);
0506     connect(splitTitlesAction, SIGNAL(triggered(bool)), this, SLOT(split_titles()));
0507 
0508     auto *swapArtistsAndTitlesAction = new QAction(this);
0509     swapArtistsAndTitlesAction->setText(i18n("Swap Artists And Titles"));
0510     actionCollection()->addAction("swapartistsandtitles", swapArtistsAndTitlesAction);
0511     connect(swapArtistsAndTitlesAction, SIGNAL(triggered(bool)), this, SLOT(swap_artists_and_titles()));
0512 
0513     auto *capitalizeAction = new QAction(this);
0514     capitalizeAction->setText(i18n("Capitalize"));
0515     actionCollection()->addAction("capitalize", capitalizeAction);
0516     connect(capitalizeAction, SIGNAL(triggered(bool)), this, SLOT(capitalize()));
0517 
0518     auto *autoFillArtistsAction = new QAction(this);
0519     autoFillArtistsAction->setText(i18n("Auto Fill Artists"));
0520     actionCollection()->addAction("autofillartists", autoFillArtistsAction);
0521     connect(autoFillArtistsAction, SIGNAL(triggered(bool)), this, SLOT(auto_fill_artists()));
0522 
0523     auto *selectAllAction = new QAction(this);
0524     selectAllAction->setText(i18n("Select All Tracks"));
0525     actionCollection()->addAction("selectall", selectAllAction);
0526     connect(selectAllAction, SIGNAL(triggered(bool)), this, SLOT(select_all()));
0527 
0528     auto *selectNoneAction = new QAction(this);
0529     selectNoneAction->setText(i18n("Deselect All Tracks"));
0530     actionCollection()->addAction("selectnone", selectNoneAction);
0531     connect(selectNoneAction, SIGNAL(triggered(bool)), this, SLOT(select_none()));
0532 
0533     auto *invertSelectionAction = new QAction(this);
0534     invertSelectionAction->setText(i18n("Invert Selection"));
0535     actionCollection()->addAction("invertselection", invertSelectionAction);
0536     connect(invertSelectionAction, SIGNAL(triggered(bool)), this, SLOT(invert_selection()));
0537 
0538     KStandardAction::quit(qApp, SLOT(quit()), actionCollection());
0539 }
0540 
0541 void MainWindow::setup_layout()
0542 {
0543     cdda_tree_view = new CDDATreeView(this);
0544     cdda_tree_view->setModel(cdda_model);
0545     cdda_tree_view->setAlternatingRowColors(true);
0546     cdda_tree_view->setSelectionBehavior(QAbstractItemView::SelectRows);
0547     cdda_tree_view->setEditTriggers(QAbstractItemView::EditKeyPressed | QAbstractItemView::DoubleClicked);
0548     cdda_tree_view->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
0549     cdda_tree_view->setIndentation(0);
0550     cdda_tree_view->setAllColumnsShowFocus(true);
0551     cdda_tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
0552     connect(cdda_tree_view, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(cdda_context_menu(const QPoint &)));
0553     connect(cdda_tree_view, SIGNAL(clicked(const QModelIndex &)), SLOT(toggle(const QModelIndex &)));
0554     connect(cdda_model, SIGNAL(selectionChanged(const int)), this, SLOT(selection_changed(const int)));
0555 
0556     cdda_header_dock = new QDockWidget(this);
0557     cdda_header_dock->setObjectName("cdda_header_dock");
0558     cdda_header_dock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
0559     cdda_header_dock->setAllowedAreas(Qt::AllDockWidgetAreas);
0560 
0561     setCentralWidget(cdda_tree_view);
0562     cdda_header_widget = new CDDAHeaderWidget(cdda_model, cdda_header_dock);
0563     connect(cdda_header_widget, SIGNAL(headerDataChanged()), this, SLOT(update_layout()));
0564     cdda_header_dock->setWidget(cdda_header_widget);
0565     addDockWidget(Qt::LeftDockWidgetArea, cdda_header_dock);
0566 
0567     statusBar()->hide();
0568     statusBar()->setMaximumHeight(0);
0569 }
0570 
0571 void MainWindow::select_all()
0572 {
0573     cdda_model->selectAll();
0574 }
0575 
0576 void MainWindow::select_none()
0577 {
0578     cdda_model->selectNone();
0579 }
0580 
0581 void MainWindow::invert_selection()
0582 {
0583     cdda_model->invertSelection();
0584 }
0585 
0586 void MainWindow::cdda_context_menu(const QPoint &pos)
0587 {
0588     Q_UNUSED(pos);
0589     QMenu menu(this);
0590     menu.addAction(actionCollection()->action("selectall"));
0591     menu.addAction(actionCollection()->action("selectnone"));
0592     menu.addSeparator();
0593     menu.addAction(actionCollection()->action("invertselection"));
0594     menu.exec(QCursor::pos());
0595 }
0596 
0597 void MainWindow::selection_changed(const int num_selected)
0598 {
0599     actionCollection()->action("rip")->setEnabled(num_selected > 0);
0600     actionCollection()->action("selectall")->setEnabled(num_selected < cdda_model->numOfAudioTracks());
0601     actionCollection()->action("selectnone")->setEnabled(num_selected > 0);
0602 }