diff options
author | Arno <am@disconnect.de> | 2010-11-27 12:28:34 +0100 |
---|---|---|
committer | Arno <am@disconnect.de> | 2010-11-27 12:28:34 +0100 |
commit | da30a02976792a07c72e8be01aebde019a6a09d5 (patch) | |
tree | 7643cddb956a258eea9aeda2990313fd6fa92d32 | |
parent | 0f3f7598e4a3ee58c330cc7424cf89ea3e692da0 (diff) | |
download | SheMov-da30a02976792a07c72e8be01aebde019a6a09d5.tar.gz SheMov-da30a02976792a07c72e8be01aebde019a6a09d5.tar.bz2 SheMov-da30a02976792a07c72e8be01aebde019a6a09d5.zip |
Implemented dialog for showing movies without covers
Implemented a new dialog to show movies without covers. The view is a
QTreeView with another model. While working on the model several
shortcomings of SmTreeModel were resolved. findValue() now takes another
argument to indicate the column the returned QModelIndex() should
represent. Also, itemAt() was promoted from private to protected. It's
quite useful for derived classes.
-rw-r--r-- | archivetreeview.cpp | 190 | ||||
-rw-r--r-- | archivetreeview.h | 68 | ||||
-rw-r--r-- | seriestreemodel.cpp | 10 | ||||
-rw-r--r-- | seriestreemodel.h | 2 | ||||
-rw-r--r-- | shemov.cpp | 6 | ||||
-rw-r--r-- | shemov.h | 1 | ||||
-rw-r--r-- | shemov.qrc | 1 | ||||
-rw-r--r-- | smtreemodel.h | 3 |
8 files changed, 276 insertions, 5 deletions
diff --git a/archivetreeview.cpp b/archivetreeview.cpp index b00737e..3529b01 100644 --- a/archivetreeview.cpp +++ b/archivetreeview.cpp @@ -17,10 +17,25 @@ #include <QHash> #include <QProcess> #include <QMessageBox> +#include <QSqlDatabase> +#include <QSqlQuery> +#include <QMenu> +#include <QAction> +#include <QContextMenuEvent> +#include <QLabel> +#include <QPushButton> +#include <QHBoxLayout> +#include <QVBoxLayout> +#include <QProcess> +#include <QUrl> +#include <QMessageBox> +#include <QClipboard> +#include <QApplication> #include "archivetreeview.h" #include "smglobals.h" #include "smtreemodel.h" +#include "smtreeitem.h" #include "seriestreewidget.h" #include "filestreewidget.h" #include "filestreemodel.h" @@ -67,6 +82,11 @@ ArchiveTreeView::ArchiveTreeView(QWidget *parent) : QWidget(parent){ 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; @@ -132,6 +152,28 @@ void ArchiveTreeView::cleanDatabase(const QString &table){ } } +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); @@ -224,3 +266,151 @@ void ArchiveTreeView::constructWindowTitle(){ 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(); +} + +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<QVariant> 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; +} diff --git a/archivetreeview.h b/archivetreeview.h index ddfad7e..2789298 100644 --- a/archivetreeview.h +++ b/archivetreeview.h @@ -11,6 +11,11 @@ #include <QWidget> #include <QModelIndexList> #include <QList> +#include <QSqlDatabase> +#include <QTreeView> +#include <QDialog> + +#include "smtreemodel.h" class SeriesTreeWidget; class FilesTreeWidget; @@ -18,7 +23,12 @@ class FilesTreeModel; class SeriesTreeModel; class MappingTableWidget; class MappingTableModel; +class NoCoverMovieDialog; class QItemSelection; +class QSqlQuery; +class QPushButton; +class QContextMenuEvent; +class QLabel; class ArchiveTreeView : public QWidget { @@ -35,6 +45,8 @@ class ArchiveTreeView : public QWidget public slots: void setFileViewMode(int mode); void cleanDatabase(const QString &table); + void showNoCoverDialog(); + void selectMovie(const QModelIndex &idx); private slots: void currentChanged(const QItemSelection &selected, const QItemSelection &deselected); @@ -59,6 +71,62 @@ class ArchiveTreeView : public QWidget //misc QString mWindowTitle; + NoCoverMovieDialog *mNoCoverDialog; +}; + +class NoCoverMovieModel : public SmTreeModel { + Q_OBJECT + public: + enum CustomRoles { SeriesNameRole = Qt::UserRole + 1, SeriesPartRole = Qt::UserRole + 2, SeriesIdRole = Qt::UserRole + 3 }; + enum Fields { Display = 0, SeriesName = 1, SeriesPart = 2, SeriesId = 3 }; + explicit NoCoverMovieModel(const QStringList &headers, QObject *parent = 0); + virtual ~NoCoverMovieModel() {} + virtual QVariant data(const QModelIndex &index, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + public slots: + void refresh(); + + signals: + void refreshed(); + + private: + QSqlDatabase mDb; + QSqlQuery *mDataQuery; +}; + +class NoCoverMovieView : public QTreeView { + Q_OBJECT + public: + explicit NoCoverMovieView(QWidget *parent = 0); + virtual ~NoCoverMovieView() {} + + protected: + void contextMenuEvent(QContextMenuEvent *e); +}; + +class NoCoverMovieDialog : public QDialog { + Q_OBJECT + public: + explicit NoCoverMovieDialog(QWidget *parent = 0, Qt::WindowFlags flags = 0); + virtual ~NoCoverMovieDialog() {} + NoCoverMovieView *view() { return mView; } + + private slots: + void openInBrowser(); + void copyToClipboard(); + void updateLabel(); + + private: + const QString selectedText() const; + const QString labelText() const; + QPushButton *mClose; + QPushButton *mRefresh; + NoCoverMovieView *mView; + NoCoverMovieModel *mModel; + QLabel *mMovieLabel; + QAction *mCopyA; + QAction *mBrowserA; }; #endif diff --git a/seriestreemodel.cpp b/seriestreemodel.cpp index ab3fe8c..a2bde55 100644 --- a/seriestreemodel.cpp +++ b/seriestreemodel.cpp @@ -164,15 +164,19 @@ QList<QVariant> SeriesTreeModel::childrenColumnList(const QModelIndex &parent, i return retval; } -QModelIndex SeriesTreeModel::findValue(const QVariant &value, const QModelIndex &parent, int column) const{ +QModelIndex SeriesTreeModel::findValue(const QVariant &value, const QModelIndex &parent, int searchColumn, int indexColumn) const{ SmTreeItem *parentItem = root(); if(parent != QModelIndex()){ parentItem = static_cast<SmTreeItem*>(parent.internalPointer()); } for(int i = 0; i < parentItem->childCount(); ++i){ SmTreeItem *childItem = parentItem->child(i); - if(value == childItem->data(column)){ - return createIndex(i, column, childItem); + if(value == childItem->data(searchColumn)){ + if(indexColumn == -1){ + return createIndex(i, searchColumn, childItem); + }else{ + return createIndex(i, indexColumn, childItem); + } } } return QModelIndex(); diff --git a/seriestreemodel.h b/seriestreemodel.h index 63820ec..e26e49b 100644 --- a/seriestreemodel.h +++ b/seriestreemodel.h @@ -35,7 +35,7 @@ class SeriesTreeModel : public SmTreeModel { QList<QVariant> childrenColumnList(const QModelIndex &parent, int column) const; //find - QModelIndex findValue(const QVariant &value, const QModelIndex &parent = QModelIndex(), int column = 0) const; + QModelIndex findValue(const QVariant &value, const QModelIndex &parent = QModelIndex(), int searchColumn = 0, int indexColumn = -1) const; QFileInfoList findFiles(const QModelIndex &where) const; QFileInfoList findMovies(const QModelIndexList &from) const; QFileInfoList findSortedMovies(const QModelIndex &from) const; @@ -165,6 +165,7 @@ void SheMov::tabChanged(int newTab){ mShowNormalA->setEnabled(false); mShowArchivedA->setEnabled(false); mShowLocalA->setEnabled(false); + mShowNoCoverDialogA->setEnabled(false); } if(newTab == 1){ setWindowTitle(mATree->windowTitle()); @@ -173,6 +174,7 @@ void SheMov::tabChanged(int newTab){ mShowNormalA->setEnabled(true); mShowArchivedA->setEnabled(true); mShowLocalA->setEnabled(true); + mShowNoCoverDialogA->setEnabled(true); } updateSelectionCount(QItemSelection(), QItemSelection()); } @@ -413,6 +415,8 @@ void SheMov::createActions(){ connect(mShowNormalA, SIGNAL(triggered()), viewMapper, SLOT(map())); mShowNormalA->setCheckable(true); mShowNormalA->setChecked(true); + mShowNoCoverDialogA = new QAction(QIcon(":/higheels.png"), tr("List movies without cover..."), this); + connect(mShowNoCoverDialogA, SIGNAL(triggered()), mATree, SLOT(showNoCoverDialog())); //Tree FileWidget actions mMoveToBurnA = new QAction(tr("Move to burn directory"), this); @@ -457,6 +461,7 @@ void SheMov::createMenus(){ cleanupMenu->addActions(mCleanupGroup->actions()); fileMenu->addMenu(cleanupMenu); fileMenu->addAction(mConsistencyA); + fileMenu->addAction(mShowNoCoverDialogA); fileMenu->addSeparator(); fileMenu->addAction(mQuitA); menuBar()->addMenu(fileMenu); @@ -638,6 +643,7 @@ void SheMov::createToolBar(){ toolBar->addAction(mCdupA); toolBar->addSeparator(); toolBar->addAction(mConfigA); + toolBar->addAction(mShowNoCoverDialogA); toolBar->addSeparator(); toolBar->addAction(mShowNormalA); toolBar->addAction(mShowArchivedA); @@ -115,6 +115,7 @@ class SheMov : public QMainWindow { QAction *mShowLocalA; QAction *mShowArchivedA; QAction *mShowNormalA; + QAction *mShowNoCoverDialogA; QActionGroup *mViewTreeGroup; //TreeView FileWidget Actions @@ -16,5 +16,6 @@ <file>bald_pussy.png</file> <file>prince_albert.png</file> <file>diaper.png</file> + <file>higheels.png</file> </qresource> </RCC> diff --git a/smtreemodel.h b/smtreemodel.h index 6d89fb2..3eae432 100644 --- a/smtreemodel.h +++ b/smtreemodel.h @@ -46,9 +46,10 @@ class SmTreeModel : public QAbstractItemModel { bool removeRows(int row, int count, const QModelIndex &parent); bool addRow(const QList<QVariant> &data, const QModelIndex &parent); + protected: + SmTreeItem *itemAt(const QModelIndex &index) const; private: - SmTreeItem *itemAt(const QModelIndex &index) const; QStringList mHeaders; SmTreeItem *mRootItem; }; |