/* 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 #include #include #include #include #include #include #include #include #include "archivetreeview.h" #include "smglobals.h" #include "smtreemodel.h" #include "smtreeitem.h" #include "seriestreewidget.h" #include "filestreewidget.h" #include "filestreemodel.h" #include "seriestreemodel.h" #include "mappingtablewidget.h" #include "mappingtablemodel.h" #include "helper.h" ArchiveTreeView::ArchiveTreeView(QWidget *parent) : QWidget(parent){ // models mActorsModel = static_cast(SmGlobals::instance()->model("actors")); mGenresModel = static_cast(SmGlobals::instance()->model("genres")); // series view mSeriesWidget = new SeriesTreeWidget; QItemSelectionModel *selModel = mSeriesWidget->seriesTree()->selectionModel(); connect(selModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(currentChanged(QItemSelection,QItemSelection))); connect(mSeriesWidget->seriesTree(), SIGNAL(doubleClicked(QModelIndex)), this, SLOT(playSelected())); mSeriesModel = static_cast(SmGlobals::instance()->model("SeriesModel")); // files view mFilesWidget = new FilesTreeWidget; QAbstractItemModel *filesModel = SmGlobals::instance()->model("FilesModel"); mFilesModel = static_cast(filesModel); for(int i = 5; i < 12; ++i){ mFilesWidget->filesTree()->setColumnHidden(i, true); } mFilesWidget->filesTree()->setColumnHidden(14, true); connect(mSeriesWidget, SIGNAL(filesReload()), mFilesWidget->filesTree(), SLOT(expandAll())); // actors and genre QWidget *genreActorWidget = new QWidget; QHBoxLayout *genreActorLayout = new QHBoxLayout; mActorsWidget = new MappingTableWidget("actors"); mGenresWidget = new MappingTableWidget("genres"); genreActorLayout->addWidget(mActorsWidget); genreActorLayout->addWidget(mGenresWidget); genreActorWidget->setLayout(genreActorLayout); // fileview, actors and genres QSplitter *verticalSplitter = new QSplitter(Qt::Vertical); verticalSplitter->addWidget(mFilesWidget); verticalSplitter->addWidget(genreActorWidget); verticalSplitter->setStretchFactor(0, 3); verticalSplitter->setStretchFactor(1, 1); // misc mNoCoverDialog = new NoCoverMovieDialog(this); mNoCoverDialog->setHidden(true); connect(mNoCoverDialog->view(), SIGNAL(doubleClicked(QModelIndex)), this, SLOT(selectMovie(QModelIndex))); // layout QHBoxLayout *mainLayout = new QHBoxLayout; QSplitter *splitter = new QSplitter; splitter->addWidget(mSeriesWidget); splitter->addWidget(verticalSplitter); splitter->setStretchFactor(0, 2); splitter->setStretchFactor(1, 3); mainLayout->addWidget(splitter); setLayout(mainLayout); constructWindowTitle(); } void ArchiveTreeView::setFileViewMode(int mode){ switch(mode){ case FilesTreeModel::Archived: case FilesTreeModel::Local: mSeriesWidget->setEnabled(false); break; case FilesTreeModel::Normal: mSeriesWidget->setEnabled(true); break; default: ; } mFilesWidget->resetSize(); mFilesModel->setMode(mode); if(mode == FilesTreeModel::Normal){ currentChanged(QItemSelection(), QItemSelection()); } mFilesWidget->filesTree()->expandAll(); mFilesWidget->filesTree()->resizeColumnToContents(0); mFilesWidget->filesTree()->resizeColumnToContents(1); mFilesWidget->filesTree()->resizeColumnToContents(2); constructWindowTitle(); } void ArchiveTreeView::cleanDatabase(const QString &table){ MappingTableModel *model = 0; if(table == "genres"){ model = mGenresModel; }else if(table == "actors"){ model = mActorsModel; } Q_ASSERT(model); QHash zeroUsers = model->zeroUsers(); if(zeroUsers.isEmpty()){ QMessageBox::information(this, tr("Message"), tr("Nothing to clean up!")); return; } QString message = QString(tr("

Really delete these items from %1?

")).arg(table); message.append("

    "); QStringList userNames = zeroUsers.keys(); qSort(userNames); foreach(QString s, userNames){ message.append(QString("
  • %1
  • ").arg(s)); } message.append("
"); int answer = QMessageBox::question(this, tr("Question"), message, QMessageBox::Yes | QMessageBox::No); if(answer == QMessageBox::Yes){ QList ids = zeroUsers.values(); model->deleteItems(ids); } } void ArchiveTreeView::showNoCoverDialog(){ mNoCoverDialog->show(); } void ArchiveTreeView::selectMovie(const QModelIndex &idx){ if(!idx.isValid()){ return; } QVariant movieName = idx.data(NoCoverMovieModel::SeriesNameRole); QVariant part = idx.data(NoCoverMovieModel::SeriesPartRole); QModelIndex movieIdx = mSeriesModel->findValue(movieName, QModelIndex(), SeriesTreeModel::Name); if(!movieIdx.isValid()){ return; } QModelIndex partIdx = mSeriesModel->findValue(part, movieIdx, SeriesTreeModel::SeriesPart, 0); QModelIndex toSelect = partIdx.isValid() ? mSeriesWidget->seriesProxy()->mapFromSource(partIdx) : mSeriesWidget->seriesProxy()->mapFromSource(movieIdx); mSeriesWidget->seriesTree()->expand(mSeriesWidget->seriesProxy()->mapFromSource(movieIdx)); mSeriesWidget->seriesTree()->selectionModel()->select(toSelect, QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); mSeriesWidget->seriesTree()->scrollTo(toSelect, QAbstractItemView::PositionAtCenter); mNoCoverDialog->hide(); } void ArchiveTreeView::currentChanged(const QItemSelection &selected, const QItemSelection &deselected){ Q_UNUSED(selected); Q_UNUSED(deselected); QModelIndexList selectedSeries = mSeriesWidget->seriesTree()->selectionModel()->selectedRows(); mFilesWidget->resetSize(); if(selectedSeries.isEmpty()){ return; } QList selectedPartIds; SeriesTreeSortModel *proxy = mSeriesWidget->seriesProxy(); foreach(QModelIndex i, selectedSeries){ QModelIndex real = proxy->mapToSource(i); if(real.isValid()){ int type = real.data(SeriesTreeModel::TypeRole).toInt(); if(type == SeriesTreeModel::Series){ QList childIds = mSeriesModel->childrenColumnList(real, SeriesTreeModel::SeriesPartId); foreach(QVariant id, childIds){ int realId = id.toInt(); if(!selectedPartIds.contains(realId)){ selectedPartIds << realId; } } }else if(type == SeriesTreeModel::Part){ int realId = i.data(SeriesTreeModel::SeriesPartIdRole).toInt(); if(!selectedPartIds.contains(realId)){ selectedPartIds << realId; } } } } mFilesModel->setIds(selectedPartIds); mFilesWidget->filesTree()->expandAll(); mFilesWidget->filesTree()->resizeColumnToContents(FilesTreeModel::FileName); mFilesWidget->filesTree()->resizeColumnToContents(FilesTreeModel::PartNo); mFilesWidget->filesTree()->resizeColumnToContents(FilesTreeModel::Quality); mFilesWidget->filesTree()->resizeColumnToContents(FilesTreeModel::DvdNo); mFilesWidget->filesTree()->resizeColumnToContents(FilesTreeModel::DisplayName); mFilesWidget->filesTree()->resizeColumnToContents(FilesTreeModel::SizeDuration); setMappingItems(selectedPartIds, mActorsModel, mActorsWidget); setMappingItems(selectedPartIds, mGenresModel, mGenresWidget); } void ArchiveTreeView::setMappingItems(QListseriesPartIds, MappingTableModel *model, MappingTableWidget *widget){ QStringList items; foreach(int id, seriesPartIds){ QList actorNames = model->mappings(id); foreach(QVariant v, actorNames){ if(!items.contains(v.toString())){ items << v.toString(); } } } qSort(items); widget->setStringList(items); widget->setEditEnabled(seriesPartIds.count() == 1); if(seriesPartIds.count() == 1){ widget->setCurrentId(seriesPartIds.at(0)); }else{ widget->setCurrentId(-1); } } void ArchiveTreeView::playSelected(const QString &preferred){ QModelIndexList selected = mSeriesWidget->seriesTree()->selectionModel()->selectedRows(); if(selected.isEmpty()){ return; } QStringList files; foreach(QModelIndex idx, selected){ QModelIndex real = mSeriesWidget->seriesProxy()->mapToSource(idx); QFileInfoList movies = mSeriesModel->findSortedMovies(real); foreach(QFileInfo fi, movies){ if(!files.contains(fi.absoluteFilePath())){ files << fi.absoluteFilePath(); } } } QPair progData = Helper::programData("movieviewer", preferred); QString program = progData.first; QStringList args = progData.second; args << files; QProcess::startDetached(program, args); } void ArchiveTreeView::constructWindowTitle(){ QString modeString = mFilesModel->modeName(); mWindowTitle = QString("Movie Archive - [%1]").arg(modeString); emit needWindowTitleChange(mWindowTitle); } //NoCoverMovieModel NoCoverMovieModel::NoCoverMovieModel(const QStringList &headers, QObject *parent) : SmTreeModel(headers, parent){ mDb = QSqlDatabase::database("treedb"); mDataQuery = new QSqlQuery(mDb); mDataQuery->prepare("SELECT files.iseriespart_id, series.tseries_name, seriesparts.iseriespart FROM files, series, seriesparts WHERE files.iseriespart_id = seriesparts.iseriesparts_id AND seriesparts.iseries_id = series.iseries_id GROUP BY files.iseriespart_id,series.tseries_name,seriesparts.iseriespart HAVING COUNT(iseriespart_id) = 1 ORDER BY series.tseries_name"); refresh(); } NoCoverMovieModel::~NoCoverMovieModel(){ delete mDataQuery; mDb = QSqlDatabase(); } QVariant NoCoverMovieModel::data(const QModelIndex &index, int role) const{ if(!index.isValid()){ return QVariant(); } SmTreeItem *item = itemAt(index); if(role == SeriesNameRole){ return item->data(SeriesName); } if(role == SeriesPartRole){ return item->data(SeriesPart); } if(role == SeriesIdRole){ return item->data(SeriesId); } return SmTreeModel::data(index, role); } void populate(); void NoCoverMovieModel::refresh(){ SmTreeItem *rootItem = new SmTreeItem(4); mDataQuery->exec(); while(mDataQuery->next()){ QString displayName = QString("%1 %2").arg(mDataQuery->value(1).toString()).arg(QString::number(mDataQuery->value(2).toInt())); QList data; data << displayName << mDataQuery->value(1) << mDataQuery->value(2) << mDataQuery->value(0); SmTreeItem *child = new SmTreeItem(data, rootItem); rootItem->appendChild(child); } setRoot(rootItem); emit refreshed(); } Qt::ItemFlags NoCoverMovieModel::flags(const QModelIndex &index) const{ if(!index.isValid()){ return 0; } return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } //NoCoverMovieView NoCoverMovieView::NoCoverMovieView(QWidget *parent) : QTreeView(parent) {} void NoCoverMovieView::contextMenuEvent(QContextMenuEvent *e){ QMenu ctxMenu; foreach(QAction *a, actions()){ ctxMenu.addAction(a); } ctxMenu.exec(e->globalPos()); } //NoCoverMovieDialog NoCoverMovieDialog::NoCoverMovieDialog(QWidget *parent, Qt::WindowFlags flags) : QDialog(parent, flags){ //model + view mView = new NoCoverMovieView; QStringList headers = QStringList() << "Movie" << "Name" << "Part" << "Id"; mModel = new NoCoverMovieModel(headers); mView->setModel(mModel); connect(mModel, SIGNAL(refreshed()), this, SLOT(updateLabel())); mView->setColumnHidden(1, true); mView->setColumnHidden(2, true); mView->setColumnHidden(3, true); mView->resizeColumnToContents(0); //actions mBrowserA = new QAction(tr("Search with google..."), this); connect(mBrowserA, SIGNAL(triggered()), this, SLOT(openInBrowser())); mView->addAction(mBrowserA); mCopyA = new QAction(tr("Copy name"), this); connect(mCopyA, SIGNAL(triggered()), this, SLOT(copyToClipboard())); mView->addAction(mCopyA); //buttons QHBoxLayout *buttonLayout = new QHBoxLayout; mClose = new QPushButton(tr("Close")); connect(mClose, SIGNAL(clicked()), this, SLOT(hide())); mRefresh = new QPushButton(tr("Refresh")); connect(mRefresh, SIGNAL(clicked()), mModel, SLOT(refresh())); buttonLayout->addWidget(mRefresh); buttonLayout->addStretch(); buttonLayout->addWidget(mClose); //label mMovieLabel = new QLabel(labelText()); //main QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(mMovieLabel); mainLayout->addWidget(mView); mainLayout->addLayout(buttonLayout); setLayout(mainLayout); } void NoCoverMovieDialog::openInBrowser(){ QString selText = selectedText(); if(selText.isEmpty()){ return; } selText.append(" covers"); QByteArray searchString = QUrl::toPercentEncoding(selText); QString url = QString("http://www.google.com/search?hl=en&q=%1").arg(QString(searchString)); QStringList args = QStringList() << "openURL" << url; bool success = QProcess::startDetached("kfmclient", args); if(!success){ QMessageBox::critical(this, tr("Error"), tr("Launching browser failed. Most likely kfmclient is not installed")); } } void NoCoverMovieDialog::copyToClipboard(){ QString selText = selectedText(); if(selText.isEmpty()){ return; } QClipboard *clip = QApplication::clipboard(); clip->setText(selText); } void NoCoverMovieDialog::updateLabel(){ mMovieLabel->setText(labelText()); } const QString NoCoverMovieDialog::selectedText() const{ QModelIndexList selected = mView->selectionModel()->selectedRows(); if(selected.isEmpty()){ return QString(); } QModelIndex real = mModel->index(selected.at(0).row(), 0, selected.at(0).parent()); if(!real.isValid()){ return QString(); } return real.data().toString(); } const QString NoCoverMovieDialog::labelText() const{ QString retval = QString(tr("Movies without covers (%1)")).arg(QString::number(mModel->rowCount(QModelIndex()))); return retval; }