diff options
author | Arno <am@disconnect.de> | 2010-12-11 13:06:20 +0100 |
---|---|---|
committer | Arno <am@disconnect.de> | 2010-12-11 13:06:20 +0100 |
commit | c3b7144f5aef2906d85339d3b9c5bf8eaa3a6356 (patch) | |
tree | cb5c3d8d6ad572148f36cfc8b75307fdea386b9f | |
parent | a87e4d8c3c2102e9728dd5df303acca7ae08b343 (diff) | |
download | SheMov-c3b7144f5aef2906d85339d3b9c5bf8eaa3a6356.tar.gz SheMov-c3b7144f5aef2906d85339d3b9c5bf8eaa3a6356.tar.bz2 SheMov-c3b7144f5aef2906d85339d3b9c5bf8eaa3a6356.zip |
Implement hover over movies
What started as an attempt to show a frame from a movie when hovering
over it, ended in a huge bugfix commit for hover related stuff. This
commit is definitely not atomic.
When hovering over a movie present on the filesytem a frame is shown.
The time frame is configurable. While digging into the code I noticed
some bugs.
Bugfixes:
* fix label for hove archive action. It was labeled for hovering over
directories in FSWidget.
* Hovering over directories didn't have an action. Also read the
appropriate value from QSettings.
Other:
* add icons for hovering over directories and hovering over movies
* replace SheMov::toggleHover(pics|some other) with a QSignalMapper
-rw-r--r-- | configurationdialog.cpp | 52 | ||||
-rw-r--r-- | configurationdialog.h | 6 | ||||
-rw-r--r-- | filestreewidget.cpp | 58 | ||||
-rw-r--r-- | filestreewidget.h | 4 | ||||
-rw-r--r-- | fileview.cpp | 2 | ||||
-rw-r--r-- | helper.cpp | 26 | ||||
-rw-r--r-- | helper.h | 4 | ||||
-rw-r--r-- | shemov.cpp | 54 | ||||
-rw-r--r-- | shemov.h | 5 | ||||
-rw-r--r-- | shemov.qrc | 2 |
10 files changed, 159 insertions, 54 deletions
diff --git a/configurationdialog.cpp b/configurationdialog.cpp index c8e7197..08186e9 100644 --- a/configurationdialog.cpp +++ b/configurationdialog.cpp @@ -59,10 +59,15 @@ ConfigurationDialog::ConfigurationDialog(QWidget *parent, Qt::WindowFlags f) : Q mFfProbePath->setCompleter(fsCompleter); pathGrid->addWidget(pathl3, 2, 0); pathGrid->addWidget(mFfProbePath, 2, 1); - QLabel *pathl4 = new QLabel(tr("DVD mount directory")); - mDvdMountPath = new QLineEdit; + QLabel *pathl4 = new QLabel(tr("Path to ffmpeg")); + mFfMpegPath = new QLineEdit; + mFfMpegPath->setCompleter(fsCompleter); pathGrid->addWidget(pathl4, 3, 0); - pathGrid->addWidget(mDvdMountPath, 3, 1); + pathGrid->addWidget(mFfMpegPath, 3, 1); + QLabel *pathl5 = new QLabel(tr("DVD mount directory")); + mDvdMountPath = new QLineEdit; + pathGrid->addWidget(pathl5, 4, 0); + pathGrid->addWidget(mDvdMountPath, 4, 1); pathGrid->setAlignment(Qt::AlignTop); pathBox->setLayout(pathGrid); QVBoxLayout *pathLayout = new QVBoxLayout; @@ -72,17 +77,17 @@ ConfigurationDialog::ConfigurationDialog(QWidget *parent, Qt::WindowFlags f) : Q //copy path options QGroupBox *copyBox = new QGroupBox(tr("Copy path options")); QGridLayout *copyGrid = new QGridLayout; - QLabel *pathl5 = new QLabel(tr("Windows drive")); + QLabel *pathl6 = new QLabel(tr("Windows drive")); mWindowsDrive = new QLineEdit; QRegExp pathValid("[a-z]{1}:"); pathValid.setCaseSensitivity(Qt::CaseInsensitive); QRegExpValidator *pathValidator = new QRegExpValidator(pathValid, this); mWindowsDrive->setValidator(pathValidator); - copyGrid->addWidget(pathl5, 0, 0); + copyGrid->addWidget(pathl6, 0, 0); copyGrid->addWidget(mWindowsDrive, 0, 1); - QLabel *pathl6 = new QLabel(tr("Strip from path")); + QLabel *pathl7 = new QLabel(tr("Strip from path")); mStripPath = new QLineEdit; - copyGrid->addWidget(pathl6, 1, 0); + copyGrid->addWidget(pathl7, 1, 0); copyGrid->addWidget(mStripPath, 1, 1); copyGrid->setAlignment(Qt::AlignTop); copyBox->setLayout(copyGrid); @@ -113,11 +118,21 @@ ConfigurationDialog::ConfigurationDialog(QWidget *parent, Qt::WindowFlags f) : Q mHoverOpacity = new QSpinBox; mHoverOpacity->setMinimum(1); mHoverOpacity->setMaximum(10); + mHoverMovies = new QCheckBox(tr("Show frame when hovering over movie")); + connect(mHoverMovies, SIGNAL(clicked(bool)), this, SLOT(setGrabFrameEnabled(bool))); + hoverLayout->addWidget(mHoverMovies); + QGridLayout *miscInputGrid = new QGridLayout; QLabel *miscl6 = new QLabel(tr("Opacity of hover window")); - QHBoxLayout *opacityLayout = new QHBoxLayout; - opacityLayout->addWidget(miscl6); - opacityLayout->addWidget(mHoverOpacity); - hoverLayout->addLayout(opacityLayout); + miscInputGrid->addWidget(miscl6, 0, 0); + miscInputGrid->addWidget(mHoverOpacity, 0, 1); + QLabel *miscl7 = new QLabel(tr("Grab frame at ([hh:mm:ss])")); + mGrabFrameFrom = new QLineEdit; + QRegExp frameValid("\\d{2}:\\d{2}:\\d{2}"); + QRegExpValidator *frameValidator = new QRegExpValidator(frameValid, this); + mGrabFrameFrom->setValidator(frameValidator); + miscInputGrid->addWidget(miscl7, 1, 0); + miscInputGrid->addWidget(mGrabFrameFrom, 1, 1); + hoverLayout->addLayout(miscInputGrid); hoverBox->setLayout(hoverLayout); //misc - assemble @@ -185,6 +200,10 @@ void ConfigurationDialog::accept(){ QDialog::accept(); } +void ConfigurationDialog::setGrabFrameEnabled(bool enabled){ + mGrabFrameFrom->setEnabled(enabled); +} + void ConfigurationDialog::readSettings(){ QSettings s; @@ -196,12 +215,16 @@ void ConfigurationDialog::readSettings(){ } mHoverPics->setChecked(s.value("ui/hoverpics", false).toBool()); mHoverArchive->setChecked(s.value("ui/hoverarchive", false).toBool()); + mHoverMovies->setChecked(s.value("ui/hovermovies", false).toBool()); mHoverOpacity->setValue(s.value("ui/hoveropacity", 10).toInt()); + mGrabFrameFrom->setText(s.value("ui/grabframe", "00:00:00").toString()); + mGrabFrameFrom->setEnabled(s.value("ui/hovermovies", false).toBool()); //read paths mArchiveDir->setText(s.value("paths/archivedir").toString()); mBurnDir->setText(s.value("paths/burn").toString()); mFfProbePath->setText(s.value("paths/ffprobe").toString()); + mFfMpegPath->setText(s.value("paths/ffmpeg").toString()); mDvdMountPath->setText(s.value("paths/dvdmount").toString()); mWindowsDrive->setText(s.value("paths/windowsdrive").toString()); mStripPath->setText(s.value("paths/strippath").toString()); @@ -225,6 +248,11 @@ void ConfigurationDialog::writeSettings(){ if(ffProbeInfo.exists() && ffProbeInfo.isExecutable()){ s.setValue("paths/ffprobe", ffprobe); } + QString ffmpeg = mFfMpegPath->text(); + QFileInfo ffMpegInfo(ffmpeg); + if(ffMpegInfo.exists() && ffMpegInfo.isExecutable()){ + s.setValue("paths/ffmpeg", ffmpeg); + } if(checkDvdPath()){ s.setValue("paths/dvdmount", mDvdMountPath->text()); } @@ -235,6 +263,8 @@ void ConfigurationDialog::writeSettings(){ s.setValue("ui/hoverpics", (mHoverPics->checkState() == Qt::Checked)); s.setValue("ui/hoverarchive", (mHoverArchive->checkState() == Qt::Checked)); s.setValue("ui/hoveropacity", mHoverOpacity->value()); + s.setValue("ui/hovermovies", (mHoverMovies->checkState() == Qt::Checked)); + s.setValue("ui/grabframe", mGrabFrameFrom->text()); //write database s.setValue("database/hostname", mDatabaseHost->text()); diff --git a/configurationdialog.h b/configurationdialog.h index edbe219..dd8e62b 100644 --- a/configurationdialog.h +++ b/configurationdialog.h @@ -27,6 +27,9 @@ class ConfigurationDialog : public QDialog { public slots: void accept(); + + private slots: + void setGrabFrameEnabled(bool enabled); private: void readSettings(); @@ -41,6 +44,7 @@ class ConfigurationDialog : public QDialog { QLineEdit *mArchiveDir; QLineEdit *mBurnDir; QLineEdit *mFfProbePath; + QLineEdit *mFfMpegPath; QLineEdit *mDvdMountPath; QLineEdit *mDatabaseHost; QLineEdit *mDatabaseName; @@ -48,9 +52,11 @@ class ConfigurationDialog : public QDialog { QLineEdit *mDatabasePassword; QLineEdit *mWindowsDrive; QLineEdit *mStripPath; + QLineEdit *mGrabFrameFrom; QComboBox *mIconForFolder; QCheckBox *mHoverPics; QCheckBox *mHoverArchive; + QCheckBox *mHoverMovies; QSpinBox *mHoverOpacity; }; diff --git a/filestreewidget.cpp b/filestreewidget.cpp index 941c7ac..4dd5308 100644 --- a/filestreewidget.cpp +++ b/filestreewidget.cpp @@ -288,9 +288,8 @@ void FilesTreeWidget::fileSelectionChanged(const QItemSelection &selected, const emit sizeChanged(mSelectedSize); } -FilesTreeView::FilesTreeView(QWidget *parent) : QTreeView(parent), mHoverWin(new HoverWindow), mHover(false){ +FilesTreeView::FilesTreeView(QWidget *parent) : QTreeView(parent), mHoverWin(new HoverWindow), mHoverPics(false){ setAttribute(Qt::WA_Hover); - } void FilesTreeView::setModel(QAbstractItemModel *model){ @@ -303,8 +302,9 @@ void FilesTreeView::setModel(QAbstractItemModel *model){ void FilesTreeView::readConfig(){ QSettings s; - mHover = s.value("ui/hoverpics", true).toBool(); + mHoverPics = s.value("ui/hoverpics", true).toBool(); mHoverWin->setWindowOpacity(s.value("ui/hoveropacity", 10).toFloat() / 10.0); + mHoverMovies = s.value("ui/hovermovies", true).toBool(); } void FilesTreeView::readHeaderConfig(){ @@ -341,39 +341,39 @@ bool FilesTreeView::event(QEvent *e){ if(!hEvent){ return QTreeView::event(e); } - if(!mHover){ - return true; - } QPoint hotSpot(hEvent->pos().x(), hEvent->pos().y() - SmGlobals::instance()->cursorSize().height()); QModelIndex curIdx = indexAt(hotSpot); + /* + The whole point of this if/then/else mess is to exit as early as possible + for performance reasons. We don't want the cpu to spin at 100% only + because something _could_ happen... + */ if((e->type() == QEvent::HoverEnter) || (e->type() == QEvent::HoverMove)){ if(!curIdx.isValid() || (!curIdx.column() == 0)){ return exitHover(); } bool toInt; int fileType = curIdx.data(FilesTreeModel::FileTypeRole).toInt(&toInt); - bool validFt = false; - if(toInt){ - validFt = (fileType == FilesTreeModel::FrontCover) || (fileType == FilesTreeModel::BackCover) || (fileType == FilesTreeModel::GeneralCover); - } - if(!toInt || !validFt){ + if(!toInt){ return exitHover(); } + if(fileType == FilesTreeModel::Movie){ + if(!mHoverMovies){ + return exitHover(); + } + }else{ + if(!mHoverPics){ + return exitHover(); + } + } } if(e->type() == QEvent::HoverEnter){ - mCurHover = curIdx; - QPixmap pm = QPixmap(curIdx.data(FilesTreeModel::FullPathRole).toString()); - mHoverWin->setPixmap(pm); - mHoverWin->setPos(); - mHoverWin->setVisible(true); + doHover(curIdx); return true; } if(e->type() == QEvent::HoverMove){ if(curIdx != mCurHover){ - mCurHover = curIdx; - mHoverWin->setPixmap(QPixmap(curIdx.data(FilesTreeModel::FullPathRole).toString())); - mHoverWin->setPos(); - mHoverWin->setVisible(true); + doHover(curIdx); return true; }else{ mHoverWin->setPos(); @@ -392,6 +392,24 @@ bool FilesTreeView::exitHover(bool exitVal){ return exitVal; } +void FilesTreeView::doHover(const QModelIndex &idx){ + QPixmap pm; + mCurHover = idx; + if(idx.data(FilesTreeModel::FileTypeRole).toInt() == FilesTreeModel::Movie){ + pm = Helper::grabFrame(idx.data(FilesTreeModel::FullPathRole).toString()); + if(pm.isNull()){ + return; + } + }else{ + if(!pm.load(idx.data(FilesTreeModel::FullPathRole).toString())){ + return; + } + } + mHoverWin->setPixmap(pm); + mHoverWin->setPos(); + mHoverWin->setVisible(true); +} + FilesTreeSortModel::FilesTreeSortModel(QObject *parent) : QSortFilterProxyModel(parent) {} // left + right are from the sourceModel() !!!! diff --git a/filestreewidget.h b/filestreewidget.h index a0c9c2f..7353a52 100644 --- a/filestreewidget.h +++ b/filestreewidget.h @@ -75,9 +75,11 @@ class FilesTreeView : public QTreeView { private: bool exitHover(bool exitVal = true); + void doHover(const QModelIndex &idx); QModelIndex mCurHover; HoverWindow *mHoverWin; - bool mHover; + bool mHoverPics; + bool mHoverMovies; }; class FilesTreeSortModel : public QSortFilterProxyModel { diff --git a/fileview.cpp b/fileview.cpp index 03fc76f..4c4e993 100644 --- a/fileview.cpp +++ b/fileview.cpp @@ -59,7 +59,7 @@ void FileView::createFolder(){ void FileView::readConfig(){ QSettings s; - mHover = s.value("ui/hoverpics", true).toBool(); + mHover = s.value("ui/hoverdirs", true).toBool(); mHoverWin->setWindowOpacity(s.value("ui/hoveropacity", 10).toFloat() / 10.0); } @@ -14,6 +14,9 @@ #include <QHash> #include <QSettings> #include <QDir> +#include <QPixmap> +#include <QTemporaryFile> +#include <QProcess> #include <stdio.h> @@ -149,7 +152,7 @@ namespace Helper { return qMakePair(programData.value("path").toString(), programData.value("args").toStringList()); } - QString durationFromSecs(qint64 secs){ + const QString durationFromSecs(qint64 secs){ int minutes = secs / 60; int hours = 0; int seconds = 0; @@ -164,6 +167,27 @@ namespace Helper { return retval; } + const QPixmap grabFrame(const QString &file, QString when){ + QSettings s; + QString ffMpegPath = s.value("paths/ffmpeg").toString(); + if(when.isEmpty()){ + when = s.value("ui/grabframe", "00:00:00").toString(); + } + QString tmptmp = QString("%1/smhover-XXXXXX.png").arg(QDir::tempPath()); + QTemporaryFile tmpPic(tmptmp); + if(tmpPic.open()){ + QStringList ffMpegArgs = QStringList() << "-vframes" << "1" << "-ss" << when << "-i" << file << "-y" << tmpPic.fileName(); + QProcess ffmpeg; + ffmpeg.start(ffMpegPath, ffMpegArgs); + if(!ffmpeg.waitForStarted()){ + return QPixmap(); + } + ffmpeg.waitForFinished(); + return QPixmap(tmpPic.fileName()); + } + return QPixmap(); + } + bool SortFileInfoList::operator ()(const QFileInfo &lhs, const QFileInfo &rhs) const { return lhs.fileName().toLower() < rhs.fileName().toLower(); } @@ -15,6 +15,7 @@ class QString; class QFileInfo; +class QPixmap; namespace Helper { const QString mimeType(const QString &path); @@ -23,7 +24,8 @@ namespace Helper { bool removeFromArchive(const QString &filename, const QString &md5); const QString createArchivePath(const QString &path, const QString &md5, bool withMd5 = false); QPair<QString, QStringList> programData(const QString &prefix, const QString &preferred = QString()); - QString durationFromSecs(qint64 secs); + const QString durationFromSecs(qint64 secs); + const QPixmap grabFrame(const QString &file, QString when = QString()); class SortFileInfoList : public std::binary_function<QFileInfo, QFileInfo, bool> { public: bool operator()(const QFileInfo &lhs, const QFileInfo &rhs) const; @@ -102,6 +102,7 @@ SheMov::SheMov(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, fla setCentralWidget(centralWidget); show(); mATree->seriesWidget()->readSettings(); + mATree->filesWidget()->filesTree()->readConfig(); mFSWidget->readSettings(); readSettings(); mFSWidget->fileView()->setFocus(Qt::ActiveWindowFocusReason); @@ -486,12 +487,30 @@ void SheMov::createActions(){ connect(mOpenWithMapperAV, SIGNAL(mapped(QString)), mATree, SLOT(playSelected(QString))); connect(mCleanupMapper, SIGNAL(mapped(QString)), mATree, SLOT(cleanDatabase(QString))); connect(viewMapper, SIGNAL(mapped(int)), mATree, SLOT(setFileViewMode(int))); + + //hover + QSignalMapper *hoverMapper = new QSignalMapper(this); mHoverPicsA = new QAction(QIcon(":/bald_pussy.png"), tr("Hover over pictures"), this); - connect(mHoverPicsA, SIGNAL(toggled(bool)), this, SLOT(toggleHoverPics(bool))); + mHoverPicsA->setData("ui/hoverpics"); mHoverPicsA->setCheckable(true); - mHoverArchiveA = new QAction(QIcon(":/prince_albert.png"), tr("Hover over directories"), this); - connect(mHoverArchiveA, SIGNAL(toggled(bool)), this, SLOT(toggleHoverArchive(bool))); + hoverMapper->setMapping(mHoverPicsA, mHoverPicsA); + connect(mHoverPicsA, SIGNAL(triggered()), hoverMapper, SLOT(map())); + mHoverArchiveA = new QAction(QIcon(":/prince_albert.png"), tr("Hover over archive tree"), this); + mHoverArchiveA->setData("ui/hoverarchive"); mHoverArchiveA->setCheckable(true); + hoverMapper->setMapping(mHoverArchiveA, mHoverArchiveA); + connect(mHoverArchiveA, SIGNAL(triggered()), hoverMapper, SLOT(map())); + mHoverDirectoriesA = new QAction(QIcon(":/french_maid_dress.png"), tr("Hover over directories"), this); + mHoverDirectoriesA->setData("ui/hoverdirs"); + mHoverDirectoriesA->setCheckable(true); + hoverMapper->setMapping(mHoverDirectoriesA, mHoverDirectoriesA); + connect(mHoverDirectoriesA, SIGNAL(triggered()), hoverMapper, SLOT(map())); + mHoverMoviesA = new QAction(QIcon(":/ball_gag.png"), tr("Hover over movies"), this); + mHoverMoviesA->setData("ui/hovermovies"); + mHoverMoviesA->setCheckable(true); + hoverMapper->setMapping(mHoverMoviesA, mHoverMoviesA); + connect(mHoverMoviesA, SIGNAL(triggered()), hoverMapper, SLOT(map())); + connect(hoverMapper, SIGNAL(mapped(QObject*)), this, SLOT(toggleHover(QObject*))); } void SheMov::createMenus(){ @@ -722,6 +741,8 @@ void SheMov::createToolBar(){ toolBar->addSeparator(); toolBar->addAction(mHoverPicsA); toolBar->addAction(mHoverArchiveA); + toolBar->addAction(mHoverDirectoriesA); + toolBar->addAction(mHoverMoviesA); toolBar->addSeparator(); toolBar->addAction(mMountDvdA); addToolBar(Qt::LeftToolBarArea, toolBar); @@ -775,10 +796,10 @@ void SheMov::readSettings(){ default: ; } - bool hoverPics = s.value("ui/hoverpics").toBool(); - mHoverPicsA->setChecked(hoverPics); - bool hoverArchive = s.value("ui/hoverarchive").toBool(); - mHoverArchiveA->setChecked(hoverArchive); + mHoverPicsA->setChecked(s.value("ui/hoverpics").toBool()); + mHoverArchiveA->setChecked(s.value("ui/hoverarchive").toBool()); + mHoverDirectoriesA->setChecked(s.value("ui/hoverdirs").toBool()); + mHoverMoviesA->setChecked(s.value("ui/hovermovies").toBool()); QString dvdMount = s.value("paths/dvdmount").toString(); if(dvdMount.isEmpty()){ mMountDvdA->setEnabled(false); @@ -805,16 +826,15 @@ void SheMov::checkConsistency(){ c.exec(); } -void SheMov::toggleHoverArchive(bool toggled){ - QSettings s; - s.setValue("ui/hoverarchive", toggled); - emit configChanged(); -} - -void SheMov::toggleHoverPics(bool toggled){ - QSettings s; - s.setValue("ui/hoverpics", toggled); - emit configChanged(); +void SheMov::toggleHover(QObject *object){ + QAction *action = qobject_cast<QAction*>(object); + if(action){ + QSettings s; + QString toSet = action->data().toString(); + bool checked = action->isChecked(); + s.setValue(toSet, checked); + emit configChanged(); + } } void SheMov::checkMount(bool mounted){ @@ -45,8 +45,7 @@ class SheMov : public QMainWindow { void newMovieWizardWithFiles(); void setSize(qint64 size); void checkConsistency(); - void toggleHoverPics(bool toggled); - void toggleHoverArchive(bool toggled); + void toggleHover(QObject *object); void checkMount(bool mounted); signals: @@ -99,6 +98,8 @@ class SheMov : public QMainWindow { QAction *mConsistencyA; QAction *mHoverPicsA; QAction *mHoverArchiveA; + QAction *mHoverDirectoriesA; + QAction *mHoverMoviesA; //TreeView Actions //Series Actions @@ -17,5 +17,7 @@ <file>prince_albert.png</file> <file>diaper.png</file> <file>higheels.png</file> + <file>ball_gag.png</file> + <file>french_maid_dress.png</file> </qresource> </RCC> |