#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "moviewidget.h" #include "smview.h" #include "moviepropertiesdialog.h" #include "editfiledialog.h" #include "helper.h" #include "smglobals.h" MovieWidget::MovieWidget(QWidget *parent) : QWidget(parent){ mPropDlg = new MoviePropertiesDialog(this); mEditFileDlg = new EditFileDialog(this); connect(mPropDlg, &MoviePropertiesDialog::accepted, this, &MovieWidget::refresh); connect(mEditFileDlg, &EditFileDialog::accepted, this, &MovieWidget::topSelectionChanged); setPalette(qApp->palette()); setupWidget(); } MovieWidget::~MovieWidget(){ writeSettings(); } void MovieWidget::setupWidget(){ QToolBar *toolbar = new QToolBar; QPixmap buttplug(":/butt_plug_light.png"); QMatrix rotatematrix; rotatematrix.rotate(90); QIcon buttplugRight(buttplug.transformed(rotatematrix)); rotatematrix.rotate(-180); QIcon buttplugLeft(buttplug.transformed(rotatematrix)); QAction *backA = new QAction(buttplugLeft, tr("Prev. archive selection"), this); connect(backA, &QAction::triggered, [=] { forwardSelection(-1) ;}); toolbar->addAction(backA); QAction *forwardA = new QAction(buttplugRight, tr("Next archive selection"), this); connect(forwardA, &QAction::triggered, [=] { forwardSelection(1); }); toolbar->addAction(forwardA); toolbar->addSeparator(); QAction *deleteA = new QAction(QIcon(":/delete.png"), tr("Delete"), this); connect(deleteA, &QAction::triggered, this, &MovieWidget::deleteSeries); toolbar->addAction(deleteA); QAction *refreshA = new QAction(QIcon(":/refresh.png"), tr("Refresh"), this); connect(refreshA, &QAction::triggered, this, &MovieWidget::refresh); toolbar->addSeparator(); toolbar->addAction(refreshA); toolbar->addSeparator(); toolbar->addAction(SmGlobals::instance()->globalAction()); QLabel *filterL = new QLabel(tr("Filter")); mFilterLE = new QLineEdit; connect(mFilterLE, &QLineEdit::returnPressed, [=] { mTopProxy->setFilterRegExp(QRegExp(mFilterLE->text())); }); QToolBar *filterTB = new QToolBar; QAction *doFilter = new QAction(Helper::icon(Qt::transparent, qApp->palette().color(QPalette::Text), QChar(0x2245), true, false), tr("Filter"), this); connect(doFilter, &QAction::triggered, [=] { mTopProxy->setFilterRegExp(QRegExp(mFilterLE->text())); }); QAction *clearFilter = new QAction(Helper::icon(Qt::transparent, qApp->palette().color(QPalette::Text), QChar(0x2694), true, false), tr("Clear"), this); connect(clearFilter, &QAction::triggered, [=] { mFilterLE->clear(); mTopProxy->setFilterRegExp(QString()); }); filterTB->addAction(doFilter); filterTB->addAction(clearFilter); QLabel *selectionL = new QLabel(tr("Selection")); mSelectionCB = new QComboBox; QStringList selections = QStringList() << tr("Actors") << tr("Genres") << tr("Series"); mSelectionCB->addItems(selections); connect(mSelectionCB, &QComboBox::currentTextChanged, [=] { selectBy(mSelectionCB->currentText()); }); QHBoxLayout *topWL = new QHBoxLayout; topWL->addWidget(filterL); topWL->addWidget(mFilterLE); topWL->addWidget(filterTB); topWL->addStretch(); topWL->addWidget(toolbar); topWL->addStretch(); topWL->addWidget(selectionL); topWL->addWidget(mSelectionCB); mTopView = new QTreeView; mTopView->setAlternatingRowColors(true); mTopView->setSortingEnabled(true); mTopView->setUniformRowHeights(true); mTopView->setSelectionBehavior(QAbstractItemView::SelectRows); mTopView->setSelectionMode(QAbstractItemView::SingleSelection); mTopView->setRootIsDecorated(false); mTopModel = new QStandardItemModel; mTopProxy = new QSortFilterProxyModel; mTopProxy->setSourceModel(mTopModel); mTopProxy->setFilterKeyColumn(1); mTopView->setModel(mTopProxy); connect(mTopView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MovieWidget::topSelectionChanged); connect(mTopView, &QTreeView::doubleClicked, this, &MovieWidget::topDoubleClicked); mBottomView = new SmView; mBottomView->setAlternatingRowColors(true); mBottomView->setSortingEnabled(true); mBottomView->setUniformRowHeights(true); mBottomView->setSelectionBehavior(QAbstractItemView::SelectRows); mBottomView->setSelectionMode(QAbstractItemView::ExtendedSelection); mBottomModel = new QStandardItemModel; mBottomProxy = new QSortFilterProxyModel; mBottomProxy->setSourceModel(mBottomModel); mBottomView->setModel(mBottomProxy); connect(mBottomView, &SmView::doubleClicked, this, &MovieWidget::bottomDoubleClicked); QAction *playSelectedA = new QAction(QIcon(":/spreadingpants.png"), tr("Play selected..."), this); connect(playSelectedA, &QAction::triggered, this, &MovieWidget::playSelected); QAction *editFileA = new QAction(QIcon(":/huge_bra_light.png"), tr("Edit file..."), this); connect(editFileA, &QAction::triggered, this, &MovieWidget::editFile); QAction *filenameToClipA = new QAction(tr("Filename to clip"), this); connect(filenameToClipA, &QAction::triggered, [=] { copyToClipboard(FilenameRole); }); QAction *fullpathToClipA = new QAction(tr("Full path to clip"), this); connect(fullpathToClipA, &QAction::triggered, [=] { copyToClipboard(FullPathRole); }); QAction *md5ToClipA = new QAction(tr("MD5 to clip"), this); connect(md5ToClipA, &QAction::triggered, [=] { copyToClipboard(MD5SumRole); }); mBottomView->addActions(QList() << playSelectedA << Helper::createSeparator(this) << editFileA << Helper::createSeparator(this) << filenameToClipA << fullpathToClipA << md5ToClipA); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addLayout(topWL); QSplitter *splitter = new QSplitter(Qt::Vertical); splitter->addWidget(mTopView); splitter->addWidget(mBottomView); splitter->setStretchFactor(0, 3); mainLayout->addWidget(splitter); setLayout(mainLayout); readSettings(); } void MovieWidget::readSettings(){ QSettings s; QString sel = s.value("mw/selection").toString(); QString curSel = mSelectionCB->currentText(); if(curSel != sel){ mSelectionCB->setCurrentText(sel); }else{ selectBy(curSel); } QString filter = s.value("mw/filter").toString(); mFilterLE->setText(filter); if(!filter.isEmpty()){ mTopProxy->setFilterRegExp(QRegExp(mFilterLE->text())); } } void MovieWidget::writeSettings(){ QSettings s; s.setValue("mw/selection", mSelectionCB->currentText()); s.setValue("mw/filter", mFilterLE->text()); } void MovieWidget::refresh(){ selectBy(mSelectionCB->currentText()); } void MovieWidget::deleteSeries(){ QModelIndexList cur = mTopView->selectionModel()->selectedRows(); if(cur.isEmpty()){ return; } int partId = cur.first().data(SeriesPartIdRole).toInt(); QStringList fullPaths; for(int i = 0; i < mBottomModel->rowCount(); ++i){ QStandardItem *item = mBottomModel->item(i); if(item){ fullPaths << item->data(FullPathRole).toString(); } } QString msg = QString(tr("Really delete %1 file(s) (SeriesId %2)?")).arg(fullPaths.count()).arg(partId); int retval = QMessageBox::question(this, tr("Delete files..."), msg); if(retval == QMessageBox::Yes){ QSqlDatabase db = QSqlDatabase::database("treedb"); db.transaction(); QSqlQuery deleteId(db); deleteId.prepare("DELETE FROM seriesparts WHERE iseriesparts_id = :id"); deleteId.bindValue(":id", partId); if(deleteId.exec()){ for(QString f : fullPaths){ if(!QFile::remove(f)){ db.rollback(); return; } } } mTopModel->removeRow(mTopProxy->mapToSource(cur.first()).row()); db.commit(); } } void MovieWidget::selectBy(const QString &selection){ if(selection == "Series"){ populateBySeries(); }else if(selection == "Actors"){ populateByActors(); }else if(selection == "Genres"){ populateByGenres(); } wTitle = QString("MovieWidget - [%1]").arg(selection); emit needTitleChange(wTitle); } void MovieWidget::populateBySeries(){ qApp->setOverrideCursor(Qt::BusyCursor); mTopView->setSortingEnabled(false); mTopView->setUpdatesEnabled(false); mTopModel->clear(); mTopModel->setHorizontalHeaderLabels(QStringList() << tr("Ct.") << tr("Series")); QFont f("Monospace"); QBrush defaultBrush(qApp->palette().brush(QPalette::Text)); QBrush greenBrush(Qt::green); QStandardItem *root = mTopModel->invisibleRootItem(); QSqlDatabase db = QSqlDatabase::database("treedb"); QSqlQuery seriesQ("SELECT series.tseries_name, seriesparts.iseriesparts_id, seriesparts.iseriespart, seriesparts.bfavorite, seriesparts.tsubtitle, count(files.*) FROM series, seriesparts, files WHERE series.iseries_id = seriesparts.iseries_id and seriesparts.iseriesparts_id = files.iseriespart_id GROUP BY series.tseries_name, seriesparts.iseriesparts_id, seriesparts.iseriespart, seriesparts.bfavorite, seriesparts.tsubtitle ORDER BY series.tseries_name, seriesparts.iseriespart, seriesparts.tsubtitle", db); while(seriesQ.next()){ QBrush currentBrush = defaultBrush; QList items; QString series(seriesQ.value(0).toString()); if(seriesQ.value(2).toInt() > 0){ series.append(QString(" %1").arg(QString::number(seriesQ.value(2).toInt()))); } if(!seriesQ.value(4).toString().isEmpty()){ series.append(QString(" - %1").arg(seriesQ.value(4).toString())); } if(seriesQ.value(3).toBool()){ currentBrush = greenBrush; } for(int i = 0; i < 2; ++i){ QStandardItem *item = new QStandardItem; item->setForeground(currentBrush); item->setEditable(false); item->setData(seriesQ.value(1), SeriesPartIdRole); item->setData(seriesQ.value(5), CountRole); item->setData(seriesQ.value(0), SeriesNameRole); item->setData(seriesQ.value(4), SubtitleRole); item->setData(seriesQ.value(2), SeriesPartRole); items << item; } QString countStr = QString("%1").arg(seriesQ.value(5).toInt(), 2, 10, QChar('0')); items[0]->setText(countStr); items[0]->setIcon(QIcon(":/squirting_nipple_light.png")); items[0]->setFont(f); items[1]->setText(series); root->appendRow(items); } mTopView->resizeColumnToContents(0); mTopView->resizeColumnToContents(1); mTopView->setUpdatesEnabled(true); mTopView->setSortingEnabled(true); qApp->restoreOverrideCursor(); } void MovieWidget::populateByActors(){ qApp->setOverrideCursor(Qt::BusyCursor); mTopView->setSortingEnabled(false); mTopView->setUpdatesEnabled(false); mTopModel->clear(); mTopModel->setHorizontalHeaderLabels(QStringList() << tr("Ct.") << tr("Series")); QFont f("Monospace"); QBrush defaultBrush(qApp->palette().brush(QPalette::Text)); QBrush greenBrush(Qt::green); QStandardItem *root = mTopModel->invisibleRootItem(); QSqlDatabase db = QSqlDatabase::database("treedb"); QSqlQuery actorsQ("SELECT actors.tactorname, seriesparts_actormap.iseriesparts_id, series.tseries_name, seriesparts.iseriesparts_id, seriesparts.iseriespart, seriesparts.bfavorite, seriesparts.tsubtitle, COUNT(files.*) FROM actors, seriesparts_actormap, series, seriesparts, files WHERE actors.iactors_id = seriesparts_actormap.iactors_id AND seriesparts_actormap.iseriesparts_id = seriesparts.iseriesparts_id AND seriesparts.iseries_id = series.iseries_id AND seriesparts.iseriesparts_id = files.iseriespart_id GROUP BY actors.tactorname, seriesparts_actormap.iseriesparts_id, series.tseries_name, seriesparts.iseriesparts_id, seriesparts.iseriespart, seriesparts.bfavorite, seriesparts.tsubtitle ORDER BY actors.tactorname, series.tseries_name, seriesparts.iseriespart, seriesparts.tsubtitle", db); while(actorsQ.next()){ QBrush currentBrush = defaultBrush; QList items; QString actor(actorsQ.value(0).toString()); actor.append(QString(" - %1").arg(actorsQ.value(2).toString())); if(actorsQ.value(4).toInt() > 0){ actor.append(QString(" %1").arg(QString::number(actorsQ.value(4).toInt()))); } if(!actorsQ.value(6).toString().isEmpty()){ actor.append(QString(" - %1").arg(actorsQ.value(6).toString())); } if(actorsQ.value(5).toBool()){ currentBrush = greenBrush; } for(int i = 0; i < 2; ++i){ QStandardItem *item = new QStandardItem; item->setForeground(currentBrush); item->setEditable(false); item->setData(actorsQ.value(1), SeriesPartIdRole); item->setData(actorsQ.value(7), CountRole); item->setData(actorsQ.value(2), SeriesNameRole); item->setData(actorsQ.value(6), SubtitleRole); item->setData(actorsQ.value(4), SeriesPartRole); items << item; } QString countStr = QString("%1").arg(actorsQ.value(7).toInt(), 2, 10, QChar('0')); items[0]->setText(countStr); items[0]->setIcon(QIcon(":/squirting_nipple_light.png")); items[0]->setFont(f); items[1]->setText(actor); root->appendRow(items); } mTopView->resizeColumnToContents(0); mTopView->resizeColumnToContents(1); mTopView->setUpdatesEnabled(true); mTopView->setSortingEnabled(true); qApp->restoreOverrideCursor(); } void MovieWidget::populateByGenres(){ qApp->setOverrideCursor(Qt::BusyCursor); mTopView->setSortingEnabled(false); mTopView->setUpdatesEnabled(false); mTopModel->clear(); mTopModel->setHorizontalHeaderLabels(QStringList() << tr("Ct.") << tr("Series")); QFont f("Monospace"); QBrush defaultBrush(qApp->palette().brush(QPalette::Text)); QBrush greenBrush(Qt::green); QStandardItem *root = mTopModel->invisibleRootItem(); QSqlDatabase db = QSqlDatabase::database("treedb"); QSqlQuery genresQ("SELECT genres.tgenrename, seriesparts_genremap.iseriesparts_id, series.tseries_name, seriesparts.iseriesparts_id, seriesparts.iseriespart, seriesparts.bfavorite, seriesparts.tsubtitle, COUNT(files.*) FROM genres, seriesparts_genremap, series, seriesparts, files WHERE genres.igenres_id = seriesparts_genremap.igenres_id AND seriesparts_genremap.iseriesparts_id = seriesparts.iseriesparts_id AND seriesparts.iseries_id = series.iseries_id AND seriesparts.iseriesparts_id = files.iseriespart_id GROUP BY genres.tgenrename, seriesparts_genremap.iseriesparts_id, series.tseries_name, seriesparts.iseriesparts_id, seriesparts.iseriespart, seriesparts.bfavorite, seriesparts.tsubtitle ORDER BY genres.tgenrename, series.tseries_name, seriesparts.iseriespart, seriesparts.tsubtitle", db); while(genresQ.next()){ QBrush currentBrush = defaultBrush; QList items; QString genre(genresQ.value(0).toString()); genre.append(QString(" - %1").arg(genresQ.value(2).toString())); if(genresQ.value(4).toInt() > 0){ genre.append(QString(" %1").arg(QString::number(genresQ.value(4).toInt()))); } if(!genresQ.value(6).toString().isEmpty()){ genre.append(QString(" - %1").arg(genresQ.value(6).toString())); } if(genresQ.value(5).toBool()){ currentBrush = greenBrush; } for(int i = 0; i < 2; ++i){ QStandardItem *item = new QStandardItem; item->setForeground(currentBrush); item->setEditable(false); item->setData(genresQ.value(1), SeriesPartIdRole); item->setData(genresQ.value(7), CountRole); item->setData(genresQ.value(2), SeriesNameRole); item->setData(genresQ.value(6), SubtitleRole); item->setData(genresQ.value(4), SeriesPartRole); items << item; } QString countStr = QString("%1").arg(genresQ.value(7).toInt(), 2, 10, QChar('0')); items[0]->setText(countStr); items[0]->setIcon(QIcon(":/squirting_nipple_light.png")); items[0]->setFont(f); items[1]->setText(genre); root->appendRow(items); } mTopView->resizeColumnToContents(0); mTopView->resizeColumnToContents(1); mTopView->setUpdatesEnabled(true); mTopView->setSortingEnabled(true); qApp->restoreOverrideCursor(); } void MovieWidget::topSelectionChanged(){ mBottomView->setUpdatesEnabled(false); mBottomView->setSortingEnabled(false); mBottomView->setRootIsDecorated(false); mBottomModel->clear(); QBrush defaultBrush(qApp->palette().brush(QPalette::Text)); QBrush redBrush(Qt::red); QBrush greenBrush(Qt::green); QBrush currentBrush = defaultBrush; QSqlDatabase db = QSqlDatabase::database("treedb"); QSqlQuery selectFilesQ(db); selectFilesQ.prepare("SELECT tfilename, cmd5sum, bisize, siquality, iduration, sifileno, idvd FROM files WHERE iseriespart_id = :arg ORDER BY sifileno"); QModelIndexList selected = mTopView->selectionModel()->selectedRows(); if(selected.isEmpty()){ return; } if(selected.first().isValid()){ QFont f("Monospace"); QStandardItem *root = mBottomModel->invisibleRootItem(); mBottomModel->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("No.") << tr("Q.") << tr("Size") << tr("Dur.") << tr("MD5") << tr("Where")); selectFilesQ.bindValue(":arg", selected.first().data(SeriesPartIdRole)); selectFilesQ.exec(); QLocale l; while(selectFilesQ.next()){ QList items; QString whereStr; QString fileName = selectFilesQ.value(0).toString(); QString md5 = selectFilesQ.value(1).toString(); QString fullPath; int dvdno = selectFilesQ.value(6).toInt(); if(dvdno == -1){ QString fp = Helper::createArchivePath(fileName, md5); QFileInfo fi(fp); if(!fi.exists()){ whereStr = QString("Missing: %1").arg(fp); currentBrush = redBrush; }else{ currentBrush = greenBrush; fullPath = fp; whereStr = fp; } } if(dvdno > 0){ QString fp = Helper::createUSBPath(fileName, selected.first().data(SeriesNameRole).toString(), selected.first().data(SubtitleRole).toString(), dvdno, selected.first().data(SeriesPartRole).toInt()); QFileInfo fi(fp); if(fi.exists()){ whereStr = fp; currentBrush = greenBrush; fullPath = fp; }else{ whereStr = QString("DVD #%1").arg(QString::number(dvdno)); } } for(int i = 0; i < 7; ++i){ QStandardItem *item = new QStandardItem; item->setForeground(currentBrush); item->setEditable(false); item->setData(fullPath, FullPathRole); item->setData(md5, MD5SumRole); item->setData(fileName, FilenameRole); items << item; } items[0]->setText(selectFilesQ.value(0).toString()); items[0]->setIcon(QIcon(":/up_dick.png")); items[0]->setFont(f); if(selectFilesQ.value(5).toInt() > 0){ items[1]->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); items[1]->setText(QString::number(selectFilesQ.value(5).toInt())); }else{ items[1]->setText(QString()); } if(selectFilesQ.value(3).toInt() > 0){ items[2]->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); items[2]->setText(QString::number(selectFilesQ.value(3).toInt())); }else{ items[2]->setText(QString()); } items[3]->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); items[3]->setText(l.toString(selectFilesQ.value(2).toLongLong())); items[4]->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); items[4]->setText(Helper::durationFromSecs(selectFilesQ.value(4).toLongLong())); items[5]->setText(selectFilesQ.value(1).toString()); items[6]->setText(whereStr); root->appendRow(items); } } mBottomView->setUpdatesEnabled(true); mBottomView->setSortingEnabled(true); for(int i = 0; i < 6; ++i){ mBottomView->resizeColumnToContents(i); } } void MovieWidget::bottomDoubleClicked(const QModelIndex &idx){ QString fullPath = idx.data(FullPathRole).toString(); if(fullPath.startsWith("/")){ QPair playerData = Helper::programData("movieviewer"); QStringList args = playerData.second; args << fullPath; QProcess::startDetached(playerData.first, args); } } void MovieWidget::topDoubleClicked(const QModelIndex &idx){ int seriesPartsId = idx.data(SeriesPartIdRole).toInt(); mPropDlg->init(seriesPartsId); mPropDlg->show(); } void MovieWidget::playSelected(){ QModelIndexList selected = mBottomView->selectionModel()->selectedRows(); if(selected.isEmpty()){ return; } QStringList files; for(QModelIndex &idx : selected){ files << idx.data(FullPathRole).toString(); } QPair playerData = Helper::programData("movieviewer"); QStringList args = playerData.second; args << files; QProcess::startDetached(playerData.first, args); } void MovieWidget::copyToClipboard(int role){ QModelIndexList selected = mBottomView->selectionModel()->selectedRows(); if(selected.isEmpty()){ return; } QClipboard *clip = qApp->clipboard(); clip->setText(selected.first().data(role).toString()); } void MovieWidget::editFile(){ QModelIndexList selected = mBottomView->selectionModel()->selectedRows(); if(selected.isEmpty()){ return; } mEditFileDlg->initDlg(selected.first().data(MD5SumRole).toString(), selected.first().data(FullPathRole).toString()); mEditFileDlg->exec(); } void MovieWidget::forwardSelection(int by){ int cur = mSelectionCB->currentIndex(); if(cur + by >= mSelectionCB->count()){ mSelectionCB->setCurrentIndex(0); }else if(cur + by < 0){ mSelectionCB->setCurrentIndex(mSelectionCB->count() - 1); }else{ mSelectionCB->setCurrentIndex(cur + by); } }