/* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "archivebrowser.h" #include "archivebrowsermodel.h" #include "copyworker.h" #include "smtreeview.h" #include "smglobals.h" #include "pictureviewer2.h" #include "delegates.h" #include "helper.h" ArchiveBrowser::ArchiveBrowser(QWidget *parent) : QWidget(parent), mSelectedSize(0), mSelectedItems(0){ //prep mModel = static_cast(SmGlobals::instance()->model("BrowserModel")); mProxy = new ArchiveBrowserModelProxy; mProxy->setSourceModel(mModel); mTree = new SmTreeView("ui/archivebrowserheaders"); mTree->setModel(mProxy); mTree->setColumnHidden(ArchiveBrowserModel::GenericId, true); mTree->setColumnHidden(ArchiveBrowserModel::NodeType, true); mTree->setItemDelegateForColumn(ArchiveBrowserModel::TotalSize, new SizeDelegate(this)); mTree->setItemDelegateForColumn(ArchiveBrowserModel::FileType, new FileTypeDelegate(this)); mTree->setSelectionMode(QAbstractItemView::ExtendedSelection); QToolBar *toolBar = new QToolBar; QWidget *spacer1 = new QWidget; toolBar->addWidget(spacer1); spacer1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); QLabel *qualityL = new QLabel(tr("Quality")); toolBar->addWidget(qualityL); mQualityFilter = new QComboBox; toolBar->addWidget(mQualityFilter); setupQualityFilter(); connect(mQualityFilter, QOverload::of(&QComboBox::currentIndexChanged), mProxy, &ArchiveBrowserModelProxy::setQualityFilter); toolBar->addSeparator(); mSizeFilter = new QCheckBox(tr("Filter by size")); connect(mSizeFilter, &QCheckBox::stateChanged, mProxy, &ArchiveBrowserModelProxy::setSizeFilter); toolBar->addWidget(mSizeFilter); toolBar->addSeparator(); QAction *refreshA = new QAction(QIcon(":/refresh.png"), tr("Refresh"), this); connect(refreshA, &QAction::triggered, this, &ArchiveBrowser::refresh); toolBar->addAction(refreshA); mTree->addAction(refreshA); toolBar->addSeparator(); mTree->addAction(Helper::createSeparator(this)); QAction *playSelectedA = new QAction(QIcon::fromTheme("media-playback-start"), tr("Play selected..."), this); connect(playSelectedA, &QAction::triggered, this, &ArchiveBrowser::playSelected); toolBar->addAction(playSelectedA); mTree->addAction(playSelectedA); QAction *moveToUSBA = new QAction(QIcon(":/usb-32.png"), tr("Move to USB..."), this); connect(moveToUSBA, &QAction::triggered, this, &ArchiveBrowser::moveToUSB); toolBar->addAction(moveToUSBA); mTree->addAction(moveToUSBA); QAction *moveToBurnA = new QAction(QIcon(":/fire.png"), tr("Move to burn..."), this); connect(moveToBurnA, &QAction::triggered, this, &ArchiveBrowser::moveToBurn); toolBar->addAction(moveToBurnA); mTree->addAction(moveToBurnA); toolBar->addSeparator(); mTree->addAction(Helper::createSeparator(this)); QIcon downArrowIcon = Helper::icon(Qt::transparent, qApp->palette().color(QPalette::Text), QChar(0x2b07), true, false); QAction *expandAllA = new QAction(downArrowIcon, tr("Expand all"), this); connect(expandAllA, &QAction::triggered, archiveTree(), &SmTreeView::expandAll); toolBar->addAction(expandAllA); mTree->addAction(expandAllA); QIcon upArrowIcon = Helper::icon(Qt::transparent, qApp->palette().color(QPalette::Text), QChar(0x2b06), true, false); QAction *collapseAllA = new QAction(upArrowIcon, tr("Collapse all"), this); connect(collapseAllA, &QAction::triggered, archiveTree(), &SmTreeView::collapseAll); toolBar->addAction(collapseAllA); mTree->addAction(collapseAllA); toolBar->addSeparator(); mTree->readHeaderConfig(); QHash hData = mModel->headerData(); QStringList hDataSorted = hData.keys(); std::sort(hDataSorted.begin(), hDataSorted.end()); QActionGroup *hDataAG = new QActionGroup(this); hDataAG->setExclusive(false); for(const QString &h : hDataSorted){ QAction *a = new QAction(h, this); a->setCheckable(true); a->setData(hData.value(h)); hDataAG->addAction(a); if(!mTree->header()->isSectionHidden(hData.value(h))){ a->setChecked(true); } connect(a, &QAction::triggered, [=] { mTree->toggleHeader(a); }); } QIcon headerIcon = Helper::icon(Qt::transparent, qApp->palette().color(QPalette::Text), 'H', true, false); QAction *headerA = new QAction(headerIcon, tr("Show headers"), this); QMenu *headerMenu = new QMenu; headerMenu->addActions(hDataAG->actions()); headerA->setMenu(headerMenu); toolBar->addAction(headerA); toolBar->addSeparator(); toolBar->addAction(SmGlobals::instance()->globalAction()); QWidget *spacer2 = new QWidget; spacer2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); toolBar->addWidget(spacer2); //connect connect(mTree->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ArchiveBrowser::browserSelectionChanged); connect(mTree, &SmTreeView::doubleClicked, this, &ArchiveBrowser::itemDoubleClicked); connect(mModel, &ArchiveBrowserModel::populated, this, &ArchiveBrowser::resetAll); //make widget QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(toolBar); mainLayout->addWidget(mTree); setLayout(mainLayout); mTree->setSortingEnabled(true); //copyworker mUSBProgress = nullptr; mCopyWorker = new CopyWorker(this); connect(mCopyWorker, &CopyWorker::error, this, &ArchiveBrowser::copyError); connect(mCopyWorker, &CopyWorker::success, this, &ArchiveBrowser::copySuccess); connect(mCopyWorker, &CopyWorker::file, this, &ArchiveBrowser::setCopyFile); } void ArchiveBrowser::browserSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { QModelIndexList selectedIdx = selectedRows(selected); QModelIndexList deselectedIdx = selectedRows(deselected); for(const QModelIndex &sel : selectedIdx){ mSelectedSize += sel.data(ArchiveBrowserModel::TotalSizeRole).toDouble(); ++mSelectedItems; mModel->setData(sel, true, ArchiveBrowserModel::SelectedRole); } for(const QModelIndex &desel : deselectedIdx){ mSelectedSize -= desel.data(ArchiveBrowserModel::TotalSizeRole).toDouble(); --mSelectedItems; mModel->setData(desel, false, ArchiveBrowserModel::SelectedRole); } emit sizeChanged(mSelectedSize); emit itemCountChanged(mSelectedItems); qint64 remaining = DVDSIZE - mSelectedSize; mProxy->setBytesRemaining(remaining); } void ArchiveBrowser::readConfig(){ QSettings s; QString qualFilter = s.value("ui/browserquality", tr("(none)")).toString(); mQualityFilter->setCurrentText(qualFilter); } void ArchiveBrowser::writeSettings(){ mTree->writeHeaderConfig(); QSettings s; s.setValue("ui/browserquality", mQualityFilter->currentText()); } void ArchiveBrowser::moveToBurn() { QModelIndexList sel = mTree->selectionModel()->selectedRows(); if(sel.isEmpty()){ return; } QSettings s; QString destDirS = s.value("paths/burn").toString(); QDir burnDir(destDirS); if(!burnDir.exists()){ QString msg = QString(tr("Destination directory %1 does not exist!\nBailing out!")).arg(destDirS); QMessageBox::critical(this, tr("Error"), msg); return; } QString msg = QString(tr("

This will do the following:

  • Move %1 file(s) to %2
  • Update the DVD no. for %1 files

Continue?

")).arg(sel.size()).arg(destDirS); int retval = QMessageBox::question(this, tr("Question"), msg, QMessageBox::Yes | QMessageBox::No); if(retval == QMessageBox::Yes){ QList filesToUpdate; for(const QModelIndex &idx : sel){ QString dirName = idx.data(ArchiveBrowserModel::NameRole).toString(); dirName.replace(' ', '.'); burnDir.mkdir(dirName); QString burnDirS = QString("%1/%2").arg(destDirS).arg(dirName); QModelIndex real = mProxy->mapToSource(idx); QModelIndexList children = mModel->children(real); for(const QModelIndex &child : children){ QFileInfo current(child.data(ArchiveBrowserModel::FullPathRole).toString()); int type = child.data(ArchiveBrowserModel::FileTypeRole).toInt(); QString destination = QString("%1/%2").arg(burnDirS).arg(current.fileName()); if(type == FT_MOVIE){ QFile::rename(current.absoluteFilePath(), destination); filesToUpdate << child.data(ArchiveBrowserModel::GenericIdRole).toInt(); }else{ QFile::copy(current.absoluteFilePath(), destination); } } } mModel->updateDVDNo(filesToUpdate); mModel->refresh(); mProxy->setBytesRemaining(0); } } void ArchiveBrowser::moveToUSB(){ QModelIndexList sel = mTree->selectionModel()->selectedRows(); if(sel.isEmpty()){ return; } QSettings s; QString destDirS = s.value("paths/usb").toString(); QFileInfo destFi(destDirS); if(!destFi.exists() || !destFi.isDir()){ QString msg = QString(tr("%1 does not exist or isn't a directory!")).arg(destFi.absoluteFilePath()); QMessageBox::critical(this, tr("Error"), msg); return; } int nextDVDNo = mModel->nextDVDNo(); QString dvdDirS = QString("DVD_%1").arg(QString::number(nextDVDNo)); QDir dest(destDirS); if(dest.exists(dvdDirS)){ QString msg = QString(tr("Something fishy is going on: %1 already exists in %2!")).arg(dvdDirS).arg(destDirS); QMessageBox::critical(this, tr("Error"), msg); return; } bool mkdir = dest.mkdir(dvdDirS); if(!mkdir){ QString msg = QString(tr("Failed to create %1 in %2!")).arg(dvdDirS).arg(destDirS); QMessageBox::critical(this, tr("Error"), msg); return; } // this one is .../DVD_123 QString finalDir = QString("%1/%2").arg(destDirS).arg(dvdDirS); QString msg = QString(tr("

This will do the following:

  • Move %1 file(s) to %2
  • Update the DVD no. for %1 files

Continue?

")).arg(sel.size()).arg(finalDir); int retval = QMessageBox::question(this, tr("Question"), msg, QMessageBox::Yes | QMessageBox::No); if(retval == QMessageBox::Yes){ mCopyWorker->clear(); QDir finalD(finalDir); for(const QModelIndex &idx : sel){ QString dirName = idx.data(ArchiveBrowserModel::NameRole).toString(); dirName.replace(' ', '.'); // this one .../DVD_123/series_name // don't check for success, it may already exist! finalD.mkdir(dirName); QModelIndex real = mProxy->mapToSource(idx); QModelIndexList children = mModel->children(real); for(const QModelIndex &child : children){ QString source = child.data(ArchiveBrowserModel::FullPathRole).toString(); QFileInfo sFi(source); QString destination = QString("%1/%2/%3").arg(finalDir).arg(dirName).arg(sFi.fileName()); mCopyWorker->enqueue(source, destination); mCopyWorker->appendData(source, child.data(ArchiveBrowserModel::FileTypeRole)); mCopyWorker->appendData(source, child.data(ArchiveBrowserModel::GenericIdRole)); } } mUSBProgress = new QProgressDialog(this); connect(mCopyWorker, &CopyWorker::bytesRead, mUSBProgress, &QProgressDialog::setValue); mUSBProgress->setLabelText(tr("Copying files...")); mUSBProgress->setMinimum(0); mUSBProgress->setMaximum(mCopyWorker->max()); mUSBProgress->show(); mCopyWorker->start(); } } void ArchiveBrowser::refresh() { mModel->refresh(); } void ArchiveBrowser::playSelected(){ QPair pgdata = Helper::programData("movieviewer"); QModelIndexList sel = mTree->selectionModel()->selectedRows(); QStringList movieFiles; for(const QModelIndex &idx : sel){ QModelIndex real = mProxy->mapToSource(idx); QModelIndexList children = mModel->children(real); for(const QModelIndex &ci : children){ if(ci.data(ArchiveBrowserModel::FileTypeRole).toInt() == 1){ movieFiles << ci.data(ArchiveBrowserModel::FullPathRole).toString(); } } } if(movieFiles.isEmpty()){ return; } QString prg = pgdata.first; QStringList args = pgdata.second; args.append(movieFiles); QProcess::startDetached(prg, args); } void ArchiveBrowser::itemDoubleClicked(QModelIndex cur){ if(cur.data(ArchiveBrowserModel::NodeTypeRole).toInt() == ArchiveBrowserModel::SeriesPartNode){ return; } if(cur.data(ArchiveBrowserModel::NodeTypeRole).toInt() == ArchiveBrowserModel::FileNode){ int fileType = cur.data(ArchiveBrowserModel::FileTypeRole).toInt(); PictureViewer2 *picView = SmGlobals::instance()->pictureViewer(); if(fileType == FT_MOVIE){ QPixmap pm = Helper::preview(cur.data(ArchiveBrowserModel::FullPathRole).toString()); picView->setPixmap(pm); }else{ picView->setFile(cur.data(ArchiveBrowserModel::FullPathRole).toString()); } picView->show(); } } void ArchiveBrowser::setupQualityFilter(){ mQualityFilter->clear(); QList qualities = mModel->availableQualities(); std::sort(qualities.begin(), qualities.end()); QStringList qualityList = QStringList() << tr("(none)"); for(int q : qualities){ qualityList << QString::number(q); } mQualityFilter->addItems(qualityList); } void ArchiveBrowser::resetAll() { mTree->selectionModel()->clear(); mSelectedItems = 0; mSelectedSize = 0; emit sizeChanged(0); emit itemCountChanged(0); } void ArchiveBrowser::copyError(QString error){ mUSBProgress->hide(); QMessageBox::critical(this, tr("Copy Error"), error); mCopyWorker->disconnect(mUSBProgress); QProgressDialog *old = mUSBProgress; old->deleteLater(); mUSBProgress = nullptr; } void ArchiveBrowser::copySuccess(QString success){ mUSBProgress->hide(); QMessageBox::information(this, tr("Copy Success"), success); mCopyWorker->disconnect(mUSBProgress); QProgressDialog *old = mUSBProgress; old->deleteLater(); mUSBProgress = nullptr; QString msg = QString(tr("Delete source files and update database?")); int q = QMessageBox::question(this, tr("Question"), msg); if(q == QMessageBox::Yes){ QHash > data = mCopyWorker->data(); QList filesToUpdate; for(const QString &source : data.keys()){ int ft = data.value(source).at(0).toInt(); if(ft == FT_MOVIE){ QFile::remove(source); filesToUpdate << data.value(source).at(1).toInt(); } } mModel->updateDVDNo(filesToUpdate); mModel->refresh(); mProxy->setBytesRemaining(0); emit needFSFreeUpdate(); mSizeFilter->setChecked(false); } } void ArchiveBrowser::setCopyFile(QString file){ QString msg = QString(tr("Copying %1")).arg(file); mUSBProgress->setLabelText(msg); } QModelIndexList ArchiveBrowser::selectedRows(const QItemSelection &sel){ QModelIndexList retval; QModelIndexList selIdx = sel.indexes(); for(const QModelIndex &idx : selIdx){ QModelIndex real = mProxy->mapToSource(idx); if(real.column() == 0){ retval << real; } } return retval; }