From 29feba342c84c26f294fb732dab94503649d8337 Mon Sep 17 00:00:00 2001 From: birkeh Date: Sat, 3 Nov 2018 22:32:05 +0100 Subject: [PATCH] Movie and Serie Discover --- ccheckboxitemdelegate.cpp | 29 ++ ccheckboxitemdelegate.h | 37 ++ cdiscover.cpp | 14 + cdiscover.h | 22 ++ cdiscover.ui | 71 ++++ cmainwindow.cpp | 29 ++ cmainwindow.h | 2 + cmoviediscover.cpp | 92 +++++ cmoviediscover.h | 31 ++ cmoviediscover.ui | 217 +++++++++++ cthemoviedbv3.cpp | 199 ++++++++++ cthemoviedbv3.h | 19 +- qtMovies.pro | 17 +- qxtspanslider.cpp | 750 ++++++++++++++++++++++++++++++++++++++ qxtspanslider.h | 109 ++++++ qxtspanslider_p.h | 84 +++++ 16 files changed, 1713 insertions(+), 9 deletions(-) create mode 100644 ccheckboxitemdelegate.cpp create mode 100644 ccheckboxitemdelegate.h create mode 100644 cdiscover.cpp create mode 100644 cdiscover.h create mode 100644 cdiscover.ui create mode 100644 cmoviediscover.cpp create mode 100644 cmoviediscover.h create mode 100644 cmoviediscover.ui create mode 100644 qxtspanslider.cpp create mode 100644 qxtspanslider.h create mode 100644 qxtspanslider_p.h diff --git a/ccheckboxitemdelegate.cpp b/ccheckboxitemdelegate.cpp new file mode 100644 index 0000000..cc5e568 --- /dev/null +++ b/ccheckboxitemdelegate.cpp @@ -0,0 +1,29 @@ +/* */ +#include "ccheckboxitemdelegate.h" + +#include +#include +#include +#include + + +void cCheckBoxItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem & option, const QModelIndex &index) const +{ + QStyleOptionViewItem options = option; + initStyleOption(&options, index); + + if(index.data(Qt::CheckStateRole) == Qt::Checked) + options.font.setBold(true); + QStyledItemDelegate::paint(painter, options, index); +} + +QSize cCheckBoxItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const +{ + QStyleOptionViewItem options = option; + initStyleOption(&options, index); + + //if(index.data(Qt::CheckStateRole) == Qt::Checked) + options.font.setBold(true); + + return(QStyledItemDelegate::sizeHint(options, index)); +} diff --git a/ccheckboxitemdelegate.h b/ccheckboxitemdelegate.h new file mode 100644 index 0000000..b420d6a --- /dev/null +++ b/ccheckboxitemdelegate.h @@ -0,0 +1,37 @@ +#ifndef CCHECKBOXITEMDELEGATE_H +#define CCHECKBOXITEMDELEGATE_H + + +#include + + +/*! + \brief + + \class cCheckBoxItemDelegate ccheckboxitemdelegate.h "ccheckboxitemdelegate.h" +*/ +class cCheckBoxItemDelegate : public QStyledItemDelegate +{ +public: +protected: + /*! + \brief + + \fn paint + \param painter + \param option + \param index + */ + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + /*! + \brief + + \fn sizeHint + \param option + \param index + \return QSize + */ + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +#endif // CCHECKBOXITEMDELEGATE_H diff --git a/cdiscover.cpp b/cdiscover.cpp new file mode 100644 index 0000000..c8e6dae --- /dev/null +++ b/cdiscover.cpp @@ -0,0 +1,14 @@ +#include "cdiscover.h" +#include "ui_cdiscover.h" + +cDiscover::cDiscover(QWidget *parent) : + QDialog(parent), + ui(new Ui::cDiscover) +{ + ui->setupUi(this); +} + +cDiscover::~cDiscover() +{ + delete ui; +} diff --git a/cdiscover.h b/cdiscover.h new file mode 100644 index 0000000..6b1532d --- /dev/null +++ b/cdiscover.h @@ -0,0 +1,22 @@ +#ifndef CDISCOVER_H +#define CDISCOVER_H + +#include + +namespace Ui { +class cDiscover; +} + +class cDiscover : public QDialog +{ + Q_OBJECT + +public: + explicit cDiscover(QWidget *parent = 0); + ~cDiscover(); + +private: + Ui::cDiscover *ui; +}; + +#endif // CDISCOVER_H diff --git a/cdiscover.ui b/cdiscover.ui new file mode 100644 index 0000000..1c3a687 --- /dev/null +++ b/cdiscover.ui @@ -0,0 +1,71 @@ + + + + + cDiscover + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + 30 + 240 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + buttonBox + accepted() + cDiscover + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + cDiscover + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/cmainwindow.cpp b/cmainwindow.cpp index a98fbc2..4be9a44 100644 --- a/cmainwindow.cpp +++ b/cmainwindow.cpp @@ -17,6 +17,9 @@ #include "cexportdialog.h" +#include "cdiscover.h" +#include "cmoviediscover.h" + #include #include #include @@ -44,6 +47,8 @@ #include #include +#include + static bool serieSort(cSerie* s1, cSerie* s2) { @@ -1048,6 +1053,9 @@ void cMainWindow::showSeriesContextMenu(QTreeView* lpTreeView, const QPoint &pos lpMenu->addSeparator(); lpMenu->addAction("export...", this, SLOT(onActionExport())); + lpMenu->addSeparator(); + lpMenu->addAction("discover...", this, SLOT(onActionDiscover())); + lpMenu->popup(lpTreeView->viewport()->mapToGlobal(pos)); } @@ -1098,6 +1106,9 @@ void cMainWindow::showMoviesContextMenu(QTreeView* lpTreeView, const QPoint &pos lpMenu->addSeparator(); lpMenu->addAction("export...", this, SLOT(onActionExport())); + lpMenu->addSeparator(); + lpMenu->addAction("discover...", this, SLOT(onActionMovieDiscover())); + lpMenu->popup(lpTreeView->viewport()->mapToGlobal(pos)); } @@ -2454,3 +2465,21 @@ void cMainWindow::onActionExit() { qApp->exit(); } + +void cMainWindow::onActionDiscover() +{ + cTheMovieDBV3 movieDB3; + QMap genres; + + genres = movieDB3.genresSerie("de-DE"); +} + +void cMainWindow::onActionMovieDiscover() +{ + cMovieDiscover* lpMovieDiscover; + + lpMovieDiscover = new cMovieDiscover(this); + lpMovieDiscover->exec(); + + delete lpMovieDiscover; +} diff --git a/cmainwindow.h b/cmainwindow.h index 019df5d..f3ebefa 100644 --- a/cmainwindow.h +++ b/cmainwindow.h @@ -61,6 +61,7 @@ private slots: void onActionLoadPictures(); void onActionFind(); void onActionFindAgain(); + void onActionDiscover(); void onActionGotoAllDownload(); void onActionGotoAllDownloadOpen(); @@ -74,6 +75,7 @@ private slots: void onActionMovieLoadPictures(); void onActionMovieFind(); void onActionMovieFindAgain(); + void onActionMovieDiscover(); void onActionExport(); void onActionAddGlobal(); diff --git a/cmoviediscover.cpp b/cmoviediscover.cpp new file mode 100644 index 0000000..6fd1f16 --- /dev/null +++ b/cmoviediscover.cpp @@ -0,0 +1,92 @@ +#include "cmoviediscover.h" +#include "ui_cmoviediscover.h" + +#include "ccheckboxitemdelegate.h" +#include "cthemoviedbv3.h" + +#include "cmovie.h" + +#include +#include +#include + + +cMovieDiscover::cMovieDiscover(QWidget *parent) : + QDialog(parent), + ui(new Ui::cMovieDiscover) +{ + ui->setupUi(this); + + m_lpGenresModel = new QStandardItemModel(0, 1); + QStringList headerLabels = QStringList() << tr("Name"); + m_lpGenresModel->setHorizontalHeaderLabels(headerLabels); + ui->m_lpGenres->setModel(m_lpGenresModel); + ui->m_lpGenres->setItemDelegate(new cCheckBoxItemDelegate()); + ui->m_lpGenres->setWrapping(true); + + connect(ui->m_lpVoting, &QxtSpanSlider::spanChanged, this, &cMovieDiscover::spanChanged); + + ui->m_lpVoting->setRange(0, 100); + ui->m_lpVoting->setLowerValue(0); + ui->m_lpVoting->setUpperValue(100); + + cTheMovieDBV3 movieDB3; + QMap genres; + + genres = movieDB3.genresMovie("de-DE"); + + QMapIterator iterator(genres); + while(iterator.hasNext()) + { + iterator.next(); + QStandardItem* lpItem = new QStandardItem(iterator.value()); + lpItem->setData(iterator.key()); + lpItem->setCheckable(true); + + m_lpGenresModel->appendRow(lpItem); + } +} + +cMovieDiscover::~cMovieDiscover() +{ + delete ui; + delete m_lpGenresModel; +} + +void cMovieDiscover::spanChanged(int lower, int upper) +{ + ui->m_lpVotingFrom->setText(QString::number((qreal)lower/10)); + ui->m_lpVotingTo->setText(QString::number((qreal)upper/10)); +} + +void cMovieDiscover::on_m_lpDiscover_clicked() +{ + QString szText = ui->m_lpText->text(); + bool bAdult = ui->m_lpIncludeAdult->isChecked(); + qint16 iYear = ui->m_lpYear->value(); + qreal voteMin = (qreal)ui->m_lpVoting->lowerValue()/10; + qreal voteMax = (qreal)ui->m_lpVoting->upperValue()/10; + + QList genres; + + if(!ui->m_lpYearEnable->isChecked()) + iYear = -1; + + for(int x = 0;x < m_lpGenresModel->rowCount();x++) + { + QStandardItem* lpItem = m_lpGenresModel->item(x); + + if(lpItem->checkState() == Qt::Checked) + genres.append(lpItem->data().toInt()); + } + + cTheMovieDBV3 movieDB3; + QList movieList = movieDB3.discoverMovie(szText, bAdult, iYear, genres, voteMin, voteMax, "de-DE"); + + m_lp +} + +void cMovieDiscover::on_m_lpYearEnable_clicked(bool checked) +{ + ui->m_lpYear->setEnabled(checked); +} diff --git a/cmoviediscover.h b/cmoviediscover.h new file mode 100644 index 0000000..c309796 --- /dev/null +++ b/cmoviediscover.h @@ -0,0 +1,31 @@ +#ifndef CMOVIEDISCOVER_H +#define CMOVIEDISCOVER_H + + +#include +#include + + +namespace Ui { +class cMovieDiscover; +} + +class cMovieDiscover : public QDialog +{ + Q_OBJECT + +public: + explicit cMovieDiscover(QWidget *parent = 0); + ~cMovieDiscover(); + +private slots: + void spanChanged(int lower, int upper); + void on_m_lpDiscover_clicked(); + void on_m_lpYearEnable_clicked(bool checked); + +private: + Ui::cMovieDiscover* ui; + QStandardItemModel* m_lpGenresModel; +}; + +#endif // CMOVIEDISCOVER_H diff --git a/cmoviediscover.ui b/cmoviediscover.ui new file mode 100644 index 0000000..9500a4e --- /dev/null +++ b/cmoviediscover.ui @@ -0,0 +1,217 @@ + + + cMovieDiscover + + + + 0 + 0 + 868 + 499 + + + + Dialog + + + + + + + + + + + + + + + + Text: + + + + + + + + + + + + inklusive Erotik + + + + + + + + + Erscheinungsjahr: + + + + + + + 1800 + + + 2100 + + + + + + + + + + + Voting: + + + + + + + TextLabel + + + + + + + TextLabel + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + + + + + Genre + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QListView::TopToBottom + + + + + + + + + + + + Discover + + + + + + + + + + + + + + true + + + + + 0 + 0 + 418 + 283 + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + QxtSpanSlider + QSlider +
qxtspanslider.h
+
+
+ + + + buttonBox + accepted() + cMovieDiscover + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + cMovieDiscover + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/cthemoviedbv3.cpp b/cthemoviedbv3.cpp index b0d72d7..60edea8 100644 --- a/cthemoviedbv3.cpp +++ b/cthemoviedbv3.cpp @@ -524,3 +524,202 @@ cSerie* cTheMovieDBV3::loadSerie(const qint32 &iID, const QString& szLanguage) } return(lpSerie); } + +QMap cTheMovieDBV3::genresMovie(const QString& szLanguage) +{ + QMap genres; + QNetworkAccessManager networkManager; + QString szRequest = QString("https://api.themoviedb.org/3/genre/movie/list?api_key=%1").arg(m_szToken); + + if(!szLanguage.contains("all")) + szRequest.append(QString("&language=%1").arg(szLanguage)); + + QNetworkReply* reply = networkManager.get(QNetworkRequest(QUrl(szRequest))); + QEventLoop loop; + + QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); + + if(reply->error() == QNetworkReply::NoError) + { + QString strReply = (QString)reply->readAll(); + QJsonDocument jsonResponse = QJsonDocument::fromJson(strReply.toUtf8()); + QJsonObject jsonObj = jsonResponse.object(); + QJsonArray jsonArray = jsonObj["genres"].toArray(); + + for(int z = 0;z < jsonArray.count();z++) + { + QJsonObject genre = jsonArray[z].toObject(); + + genres.insert(genre["id"].toInt(), genre["name"].toString()); + } + } + else + { + qDebug() << reply->errorString(); + delete reply; + } + + delete reply; + + return(genres); +} + +QList cTheMovieDBV3::discoverMovie(const QString& szText, const bool& bAdult, const qint16& iYear, const QList& genres, const qreal& voteMin, const qreal& voteMax, const QString& szLanguage) +{ + if(m_genres.isEmpty()) + m_genres = genresMovie("de-DE"); + + QList movieList; + QNetworkAccessManager networkManager; + QString szRequest = QString("https://api.themoviedb.org/3/discover/movie?api_key=%1").arg(m_szToken); + qint16 page = 1; + + if(!szLanguage.contains("all")) + szRequest.append(QString("&language=%1").arg(szLanguage)); + + if(!szText.isEmpty()) + szRequest.append(QString("&with_keywords=%1").arg(szText)); + szRequest.append(QString("&include_adult=%1").arg(bAdult?"true":"false")); + szRequest.append(QString("&include_video=false")); + if(iYear != -1) + szRequest.append(QString("&year=%1").arg(iYear)); + if(genres.count()) + { + QString tmp = QString::number(genres[0]); + + for(int x = 1;x < genres.count();x++) + tmp.append(QString(",%1").arg(genres[x])); + szRequest.append(QString("&with_genres=%1").arg(tmp)); + } + szRequest.append(QString("&vote_average.gte=%1").arg(voteMin)); + szRequest.append(QString("&vote_average.lte=%1").arg(voteMax)); + + szRequest.append("&include_adult=false"); + + for(;;) + { + QNetworkRequest request(QUrl(QString("%1&page=%2").arg(szRequest).arg(page))); + + QNetworkReply* reply = networkManager.get(request); + QEventLoop loop; + + QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); + + if (reply->error() == QNetworkReply::NoError) + { + QString strReply = (QString)reply->readAll(); + QJsonDocument jsonResponse = QJsonDocument::fromJson(strReply.toUtf8()); + QJsonObject jsonObj = jsonResponse.object(); + QJsonArray jsonArray = jsonObj["results"].toArray(); + + for(int z = 0;z < jsonArray.count();z++) + { + QJsonObject movie = jsonArray[z].toObject(); + cMovie* lpMovie = new cMovie; + lpMovie->setMovieTitle(movie["title"].toString()); + lpMovie->setMovieID(movie["id"].toInt()); + lpMovie->setOriginalTitle(movie["original_title"].toString()); + lpMovie->setReleaseDate(movie["release_date"].toString()); + lpMovie->setVoteCount(movie["vote_count"].toInt()); + lpMovie->setVideo(movie["video"].toBool()); + lpMovie->setVoteAverage(movie["vote_average"].toDouble()); + lpMovie->setPopularity(movie["popularity"].toDouble()); + lpMovie->setPosterPath(movie["poster_path"].toString()); + lpMovie->setOriginalLanguage(movie["original_language"].toString()); + + QJsonArray genresArray = movie["genre_ids"].toArray(); + QList genresIDs; + for(int y = 0;y < genresArray.count();y++) + genresIDs.append(genresArray[y].toInt()); + if(genresIDs.count()) + lpMovie->setGenres(genresFromID(genresIDs)); + lpMovie->setBackdropPath(movie["backdrop_path"].toString()); + lpMovie->setAdult(movie["adult"].toBool()); + lpMovie->setOverview(movie["overview"].toString()); + lpMovie->setReleaseDate(movie["release_date"].toString()); + + movieList.append(lpMovie); + } + if(jsonObj["total_pages"].toInt() == page) + break; + + page++; + if(page > 20) + break; + + delete reply; + } + else + { + qDebug() << reply->errorString(); + delete reply; + } + } + return(movieList); +} + +QMap cTheMovieDBV3::genresSerie(const QString& szLanguage) +{ + QMap genres; + QNetworkAccessManager networkManager; + QString szRequest = QString("https://api.themoviedb.org/3/genre/tv/list?api_key=%1").arg(m_szToken); + + if(!szLanguage.contains("all")) + szRequest.append(QString("&language=%1").arg(szLanguage)); + + QNetworkReply* reply = networkManager.get(QNetworkRequest(QUrl(szRequest))); + QEventLoop loop; + + QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); + + if(reply->error() == QNetworkReply::NoError) + { + QString strReply = (QString)reply->readAll(); + QJsonDocument jsonResponse = QJsonDocument::fromJson(strReply.toUtf8()); + QJsonObject jsonObj = jsonResponse.object(); + QJsonArray jsonArray = jsonObj["genres"].toArray(); + + for(int z = 0;z < jsonArray.count();z++) + { + QJsonObject genre = jsonArray[z].toObject(); + + genres.insert(genre["id"].toInt(), genre["name"].toString()); + } + } + else + { + qDebug() << reply->errorString(); + delete reply; + } + + delete reply; + + return(genres); +} + +QStringList cTheMovieDBV3::genresFromID(QList genres) +{ + QStringList szGenres; + + for(int x = 0;x < genres.count();x++) + { + qint32 genre = genres[x]; + + QMapIterator iterator(m_genres); + while(iterator.hasNext()) + { + iterator.next(); + + if(iterator.key() == genre) + { + szGenres.append(iterator.value()); + break; + } + } + } + + return(szGenres); +} diff --git a/cthemoviedbv3.h b/cthemoviedbv3.h index c3af882..2c95051 100644 --- a/cthemoviedbv3.h +++ b/cthemoviedbv3.h @@ -8,6 +8,7 @@ #include #include #include +#include class cTheMovieDBV3 @@ -15,14 +16,20 @@ class cTheMovieDBV3 public: cTheMovieDBV3(); - QList searchMovie(const QString& szMovie, const qint16& year = -1, const QString& szLanguage = "all"); - cMovie* loadMovie(const qint32 &iID, const QString& szLanguage); + QList searchMovie(const QString& szMovie, const qint16& year = -1, const QString& szLanguage = "all"); + cMovie* loadMovie(const qint32 &iID, const QString& szLanguage); + QMap genresMovie(const QString& szLanguage = "all"); + QList discoverMovie(const QString& szText, const bool& bAdult, const qint16& iYear, const QList& genres, const qreal& voteMin, const qreal& voteMax, const QString& szLanguage = "all"); - QList searchSerie(const QString& szSerie, const qint16& year = -1, const QString& szLanguage = "all"); - cSerie* loadSerie(const qint32 &iID, const QString& szLanguage); - cSerie* loadSerie(const QString& szIMDBID); + QList searchSerie(const QString& szSerie, const qint16& year = -1, const QString& szLanguage = "all"); + cSerie* loadSerie(const qint32 &iID, const QString& szLanguage); + cSerie* loadSerie(const QString& szIMDBID); + QMap genresSerie(const QString& szLanguage = "all"); private: - QString m_szToken; + QString m_szToken; + QMap m_genres; + + QStringList genresFromID(QList genres); }; #endif // CTHEMOVIEDBV3_H diff --git a/qtMovies.pro b/qtMovies.pro index 951e0af..2107ba7 100644 --- a/qtMovies.pro +++ b/qtMovies.pro @@ -39,7 +39,11 @@ SOURCES += main.cpp\ cfanart.cpp \ cfanarttv.cpp \ cfanartimage.cpp \ - cexportdialog.cpp + cexportdialog.cpp \ + cdiscover.cpp \ + cmoviediscover.cpp \ + qxtspanslider.cpp \ + ccheckboxitemdelegate.cpp HEADERS += cmainwindow.h \ cserie.h \ @@ -67,7 +71,12 @@ HEADERS += cmainwindow.h \ cfanart.h \ cfanarttv.h \ cfanartimage.h \ - cexportdialog.h + cexportdialog.h \ + cdiscover.h \ + cmoviediscover.h \ + qxtspanslider.h \ + qxtspanslider_p.h \ + ccheckboxitemdelegate.h FORMS += cmainwindow.ui \ csearch.ui \ @@ -78,7 +87,9 @@ FORMS += cmainwindow.ui \ cmessagedialog.ui \ cmoviesearch.ui \ cmovieedit.ui \ - cexportdialog.ui + cexportdialog.ui \ + cdiscover.ui \ + cmoviediscover.ui DISTFILES += \ qtMovies.ico diff --git a/qxtspanslider.cpp b/qxtspanslider.cpp new file mode 100644 index 0000000..d8e5a8f --- /dev/null +++ b/qxtspanslider.cpp @@ -0,0 +1,750 @@ +#include "qxtspanslider.h" +/**************************************************************************** +** Copyright (c) 2006 - 2011, the LibQxt project. +** See the Qxt AUTHORS file for a list of authors and copyright holders. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the LibQxt project nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +** +*****************************************************************************/ + +#include "qxtspanslider_p.h" +#include +#include +#include +#include +#include + +QxtSpanSliderPrivate::QxtSpanSliderPrivate() : + lower(0), + upper(0), + lowerPos(0), + upperPos(0), + offset(0), + position(0), + lastPressed(QxtSpanSlider::NoHandle), + mainControl(QxtSpanSlider::LowerHandle), + lowerPressed(QStyle::SC_None), + upperPressed(QStyle::SC_None), + movement(QxtSpanSlider::FreeMovement), + firstMovement(false), + blockTracking(false) +{ +} + +void QxtSpanSliderPrivate::initStyleOption(QStyleOptionSlider* option, QxtSpanSlider::SpanHandle handle) const +{ + const QxtSpanSlider* p = q_ptr; + p->initStyleOption(option); + option->sliderPosition = (handle == QxtSpanSlider::LowerHandle ? lowerPos : upperPos); + option->sliderValue = (handle == QxtSpanSlider::LowerHandle ? lower : upper); +} + +int QxtSpanSliderPrivate::pixelPosToRangeValue(int pos) const +{ + QStyleOptionSlider opt; + initStyleOption(&opt); + + int sliderMin = 0; + int sliderMax = 0; + int sliderLength = 0; + const QSlider* p = q_ptr; + const QRect gr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, p); + const QRect sr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, p); + if (p->orientation() == Qt::Horizontal) + { + sliderLength = sr.width(); + sliderMin = gr.x(); + sliderMax = gr.right() - sliderLength + 1; + } + else + { + sliderLength = sr.height(); + sliderMin = gr.y(); + sliderMax = gr.bottom() - sliderLength + 1; + } + return QStyle::sliderValueFromPosition(p->minimum(), p->maximum(), pos - sliderMin, + sliderMax - sliderMin, opt.upsideDown); +} + +void QxtSpanSliderPrivate::handleMousePress(const QPoint& pos, QStyle::SubControl& control, int value, QxtSpanSlider::SpanHandle handle) +{ + QStyleOptionSlider opt; + initStyleOption(&opt, handle); + QxtSpanSlider* p = q_ptr; + const QStyle::SubControl oldControl = control; + control = p->style()->hitTestComplexControl(QStyle::CC_Slider, &opt, pos, p); + const QRect sr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, p); + if (control == QStyle::SC_SliderHandle) + { + position = value; + offset = pick(pos - sr.topLeft()); + lastPressed = handle; + p->setSliderDown(true); + emit p->sliderPressed(handle); + } + if (control != oldControl) + p->update(sr); +} + +void QxtSpanSliderPrivate::setupPainter(QPainter* painter, Qt::Orientation orientation, qreal x1, qreal y1, qreal x2, qreal y2) const +{ + QColor highlight = q_ptr->palette().color(QPalette::Highlight); + QLinearGradient gradient(x1, y1, x2, y2); + gradient.setColorAt(0, highlight.dark(120)); + gradient.setColorAt(1, highlight.light(108)); + painter->setBrush(gradient); + + if (orientation == Qt::Horizontal) + painter->setPen(QPen(highlight.dark(130), 0)); + else + painter->setPen(QPen(highlight.dark(150), 0)); +} + +void QxtSpanSliderPrivate::drawSpan(QStylePainter* painter, const QRect& rect) const +{ + QStyleOptionSlider opt; + initStyleOption(&opt); + const QSlider* p = q_ptr; + + // area + QRect groove = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, p); + if (opt.orientation == Qt::Horizontal) + groove.adjust(0, 0, -1, 0); + else + groove.adjust(0, 0, 0, -1); + + // pen & brush + painter->setPen(QPen(p->palette().color(QPalette::Dark).light(110), 0)); + if (opt.orientation == Qt::Horizontal) + setupPainter(painter, opt.orientation, groove.center().x(), groove.top(), groove.center().x(), groove.bottom()); + else + setupPainter(painter, opt.orientation, groove.left(), groove.center().y(), groove.right(), groove.center().y()); + + // draw groove + painter->drawRect(rect.intersected(groove)); +} + +void QxtSpanSliderPrivate::drawHandle(QStylePainter* painter, QxtSpanSlider::SpanHandle handle) const +{ + QStyleOptionSlider opt; + initStyleOption(&opt, handle); + opt.subControls = QStyle::SC_SliderHandle; + QStyle::SubControl pressed = (handle == QxtSpanSlider::LowerHandle ? lowerPressed : upperPressed); + if (pressed == QStyle::SC_SliderHandle) + { + opt.activeSubControls = pressed; + opt.state |= QStyle::State_Sunken; + } + painter->drawComplexControl(QStyle::CC_Slider, opt); +} + +void QxtSpanSliderPrivate::triggerAction(QAbstractSlider::SliderAction action, bool main) +{ + int value = 0; + bool no = false; + bool up = false; + const int min = q_ptr->minimum(); + const int max = q_ptr->maximum(); + const QxtSpanSlider::SpanHandle altControl = (mainControl == QxtSpanSlider::LowerHandle ? QxtSpanSlider::UpperHandle : QxtSpanSlider::LowerHandle); + + blockTracking = true; + + switch (action) + { + case QAbstractSlider::SliderSingleStepAdd: + if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) + { + value = qBound(min, upper + q_ptr->singleStep(), max); + up = true; + break; + } + value = qBound(min, lower + q_ptr->singleStep(), max); + break; + case QAbstractSlider::SliderSingleStepSub: + if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) + { + value = qBound(min, upper - q_ptr->singleStep(), max); + up = true; + break; + } + value = qBound(min, lower - q_ptr->singleStep(), max); + break; + case QAbstractSlider::SliderToMinimum: + value = min; + if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) + up = true; + break; + case QAbstractSlider::SliderToMaximum: + value = max; + if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) + up = true; + break; + case QAbstractSlider::SliderMove: + if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) + up = true; + case QAbstractSlider::SliderNoAction: + no = true; + break; + default: + qWarning("QxtSpanSliderPrivate::triggerAction: Unknown action"); + break; + } + + if (!no && !up) + { + if (movement == QxtSpanSlider::NoCrossing) + value = qMin(value, upper); + else if (movement == QxtSpanSlider::NoOverlapping) + value = qMin(value, upper - 1); + + if (movement == QxtSpanSlider::FreeMovement && value > upper) + { + swapControls(); + q_ptr->setUpperPosition(value); + } + else + { + q_ptr->setLowerPosition(value); + } + } + else if (!no) + { + if (movement == QxtSpanSlider::NoCrossing) + value = qMax(value, lower); + else if (movement == QxtSpanSlider::NoOverlapping) + value = qMax(value, lower + 1); + + if (movement == QxtSpanSlider::FreeMovement && value < lower) + { + swapControls(); + q_ptr->setLowerPosition(value); + } + else + { + q_ptr->setUpperPosition(value); + } + } + + blockTracking = false; + q_ptr->setLowerValue(lowerPos); + q_ptr->setUpperValue(upperPos); +} + +void QxtSpanSliderPrivate::swapControls() +{ + qSwap(lower, upper); + qSwap(lowerPressed, upperPressed); + lastPressed = (lastPressed == QxtSpanSlider::LowerHandle ? QxtSpanSlider::UpperHandle : QxtSpanSlider::LowerHandle); + mainControl = (mainControl == QxtSpanSlider::LowerHandle ? QxtSpanSlider::UpperHandle : QxtSpanSlider::LowerHandle); +} + +void QxtSpanSliderPrivate::updateRange(int min, int max) +{ + Q_UNUSED(min); + Q_UNUSED(max); + // setSpan() takes care of keeping span in range + q_ptr->setSpan(lower, upper); +} + +void QxtSpanSliderPrivate::movePressedHandle() +{ + switch (lastPressed) + { + case QxtSpanSlider::LowerHandle: + if (lowerPos != lower) + { + bool main = (mainControl == QxtSpanSlider::LowerHandle); + triggerAction(QAbstractSlider::SliderMove, main); + } + break; + case QxtSpanSlider::UpperHandle: + if (upperPos != upper) + { + bool main = (mainControl == QxtSpanSlider::UpperHandle); + triggerAction(QAbstractSlider::SliderMove, main); + } + break; + default: + break; + } +} + +/*! + \class QxtSpanSlider + \inmodule QxtWidgets + \brief The QxtSpanSlider widget is a QSlider with two handles. + + QxtSpanSlider is a slider with two handles. QxtSpanSlider is + handy for letting user to choose an span between min/max. + + The span color is calculated based on QPalette::Highlight. + + The keys are bound according to the following table: + \table + \header \o Orientation \o Key \o Handle + \row \o Qt::Horizontal \o Qt::Key_Left \o lower + \row \o Qt::Horizontal \o Qt::Key_Right \o lower + \row \o Qt::Horizontal \o Qt::Key_Up \o upper + \row \o Qt::Horizontal \o Qt::Key_Down \o upper + \row \o Qt::Vertical \o Qt::Key_Up \o lower + \row \o Qt::Vertical \o Qt::Key_Down \o lower + \row \o Qt::Vertical \o Qt::Key_Left \o upper + \row \o Qt::Vertical \o Qt::Key_Right \o upper + \endtable + + Keys are bound by the time the slider is created. A key is bound + to same handle for the lifetime of the slider. So even if the handle + representation might change from lower to upper, the same key binding + remains. + + \image qxtspanslider.png "QxtSpanSlider in Plastique style." + + \bold {Note:} QxtSpanSlider inherits QSlider for implementation specific + reasons. Adjusting any single handle specific properties like + \list + \o QAbstractSlider::sliderPosition + \o QAbstractSlider::value + \endlist + has no effect. However, all slider specific properties like + \list + \o QAbstractSlider::invertedAppearance + \o QAbstractSlider::invertedControls + \o QAbstractSlider::minimum + \o QAbstractSlider::maximum + \o QAbstractSlider::orientation + \o QAbstractSlider::pageStep + \o QAbstractSlider::singleStep + \o QSlider::tickInterval + \o QSlider::tickPosition + \endlist + are taken into consideration. + */ + +/*! + \enum QxtSpanSlider::HandleMovementMode + + This enum describes the available handle movement modes. + + \value FreeMovement The handles can be moved freely. + \value NoCrossing The handles cannot cross, but they can still overlap each other. The lower and upper values can be the same. + \value NoOverlapping The handles cannot overlap each other. The lower and upper values cannot be the same. + */ + +/*! + \enum QxtSpanSlider::SpanHandle + + This enum describes the available span handles. + + \omitvalue NoHandle \omit Internal only (for now). \endomit + \value LowerHandle The lower boundary handle. + \value UpperHandle The upper boundary handle. + */ + +/*! + \fn QxtSpanSlider::lowerValueChanged(int lower) + + This signal is emitted whenever the \a lower value has changed. + */ + +/*! + \fn QxtSpanSlider::upperValueChanged(int upper) + + This signal is emitted whenever the \a upper value has changed. + */ + +/*! + \fn QxtSpanSlider::spanChanged(int lower, int upper) + + This signal is emitted whenever both the \a lower and the \a upper + values have changed ie. the span has changed. + */ + +/*! + \fn QxtSpanSlider::lowerPositionChanged(int lower) + + This signal is emitted whenever the \a lower position has changed. + */ + +/*! + \fn QxtSpanSlider::upperPositionChanged(int upper) + + This signal is emitted whenever the \a upper position has changed. + */ + +/*! + \fn QxtSpanSlider::sliderPressed(SpanHandle handle) + + This signal is emitted whenever the \a handle has been pressed. + */ + +/*! + Constructs a new QxtSpanSlider with \a parent. + */ +QxtSpanSlider::QxtSpanSlider(QWidget* parent) : QSlider(parent), d_ptr(new QxtSpanSliderPrivate()) +{ + d_ptr->q_ptr = this; + connect(this, SIGNAL(rangeChanged(int, int)), d_ptr, SLOT(updateRange(int, int))); + connect(this, SIGNAL(sliderReleased()), d_ptr, SLOT(movePressedHandle())); +} + +/*! + Constructs a new QxtSpanSlider with \a orientation and \a parent. + */ +QxtSpanSlider::QxtSpanSlider(Qt::Orientation orientation, QWidget* parent) : QSlider(orientation, parent), d_ptr(new QxtSpanSliderPrivate()) +{ + d_ptr->q_ptr = this; + connect(this, SIGNAL(rangeChanged(int, int)), d_ptr, SLOT(updateRange(int, int))); + connect(this, SIGNAL(sliderReleased()), d_ptr, SLOT(movePressedHandle())); +} + +/*! + Destructs the span slider. + */ +QxtSpanSlider::~QxtSpanSlider() +{ +} + +/*! + \property QxtSpanSlider::handleMovementMode + \brief the handle movement mode + */ +QxtSpanSlider::HandleMovementMode QxtSpanSlider::handleMovementMode() const +{ + return d_ptr->movement; +} + +void QxtSpanSlider::setHandleMovementMode(QxtSpanSlider::HandleMovementMode mode) +{ + d_ptr->movement = mode; +} + +/*! + \property QxtSpanSlider::lowerValue + \brief the lower value of the span + */ +int QxtSpanSlider::lowerValue() const +{ + return qMin(d_ptr->lower, d_ptr->upper); +} + +void QxtSpanSlider::setLowerValue(int lower) +{ + setSpan(lower, d_ptr->upper); +} + +/*! + \property QxtSpanSlider::upperValue + \brief the upper value of the span + */ +int QxtSpanSlider::upperValue() const +{ + return qMax(d_ptr->lower, d_ptr->upper); +} + +void QxtSpanSlider::setUpperValue(int upper) +{ + setSpan(d_ptr->lower, upper); +} + +/*! + Sets the span from \a lower to \a upper. + */ +void QxtSpanSlider::setSpan(int lower, int upper) +{ + const int low = qBound(minimum(), qMin(lower, upper), maximum()); + const int upp = qBound(minimum(), qMax(lower, upper), maximum()); + if (low != d_ptr->lower || upp != d_ptr->upper) + { + if (low != d_ptr->lower) + { + d_ptr->lower = low; + d_ptr->lowerPos = low; + emit lowerValueChanged(low); + } + if (upp != d_ptr->upper) + { + d_ptr->upper = upp; + d_ptr->upperPos = upp; + emit upperValueChanged(upp); + } + emit spanChanged(d_ptr->lower, d_ptr->upper); + update(); + } +} + +/*! + \property QxtSpanSlider::lowerPosition + \brief the lower position of the span + */ +int QxtSpanSlider::lowerPosition() const +{ + return d_ptr->lowerPos; +} + +void QxtSpanSlider::setLowerPosition(int lower) +{ + if (d_ptr->lowerPos != lower) + { + d_ptr->lowerPos = lower; + if (!hasTracking()) + update(); + if (isSliderDown()) + emit lowerPositionChanged(lower); + if (hasTracking() && !d_ptr->blockTracking) + { + bool main = (d_ptr->mainControl == QxtSpanSlider::LowerHandle); + d_ptr->triggerAction(SliderMove, main); + } + } +} + +/*! + \property QxtSpanSlider::upperPosition + \brief the upper position of the span + */ +int QxtSpanSlider::upperPosition() const +{ + return d_ptr->upperPos; +} + +void QxtSpanSlider::setUpperPosition(int upper) +{ + if (d_ptr->upperPos != upper) + { + d_ptr->upperPos = upper; + if (!hasTracking()) + update(); + if (isSliderDown()) + emit upperPositionChanged(upper); + if (hasTracking() && !d_ptr->blockTracking) + { + bool main = (d_ptr->mainControl == QxtSpanSlider::UpperHandle); + d_ptr->triggerAction(SliderMove, main); + } + } +} + +/*! + \reimp + */ +void QxtSpanSlider::keyPressEvent(QKeyEvent* event) +{ + QSlider::keyPressEvent(event); + + bool main = true; + SliderAction action = SliderNoAction; + switch (event->key()) + { + case Qt::Key_Left: + main = (orientation() == Qt::Horizontal); + action = !invertedAppearance() ? SliderSingleStepSub : SliderSingleStepAdd; + break; + case Qt::Key_Right: + main = (orientation() == Qt::Horizontal); + action = !invertedAppearance() ? SliderSingleStepAdd : SliderSingleStepSub; + break; + case Qt::Key_Up: + main = (orientation() == Qt::Vertical); + action = invertedControls() ? SliderSingleStepSub : SliderSingleStepAdd; + break; + case Qt::Key_Down: + main = (orientation() == Qt::Vertical); + action = invertedControls() ? SliderSingleStepAdd : SliderSingleStepSub; + break; + case Qt::Key_Home: + main = (d_ptr->mainControl == QxtSpanSlider::LowerHandle); + action = SliderToMinimum; + break; + case Qt::Key_End: + main = (d_ptr->mainControl == QxtSpanSlider::UpperHandle); + action = SliderToMaximum; + break; + default: + event->ignore(); + break; + } + + if (action) + d_ptr->triggerAction(action, main); +} + +/*! + \reimp + */ +void QxtSpanSlider::mousePressEvent(QMouseEvent* event) +{ + if (minimum() == maximum() || (event->buttons() ^ event->button())) + { + event->ignore(); + return; + } + + d_ptr->handleMousePress(event->pos(), d_ptr->upperPressed, d_ptr->upper, QxtSpanSlider::UpperHandle); + if (d_ptr->upperPressed != QStyle::SC_SliderHandle) + d_ptr->handleMousePress(event->pos(), d_ptr->lowerPressed, d_ptr->lower, QxtSpanSlider::LowerHandle); + + d_ptr->firstMovement = true; + event->accept(); +} + +/*! + \reimp + */ +void QxtSpanSlider::mouseMoveEvent(QMouseEvent* event) +{ + if (d_ptr->lowerPressed != QStyle::SC_SliderHandle && d_ptr->upperPressed != QStyle::SC_SliderHandle) + { + event->ignore(); + return; + } + + QStyleOptionSlider opt; + d_ptr->initStyleOption(&opt); + const int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this); + int newPosition = d_ptr->pixelPosToRangeValue(d_ptr->pick(event->pos()) - d_ptr->offset); + if (m >= 0) + { + const QRect r = rect().adjusted(-m, -m, m, m); + if (!r.contains(event->pos())) + { + newPosition = d_ptr->position; + } + } + + // pick the preferred handle on the first movement + if (d_ptr->firstMovement) + { + if (d_ptr->lower == d_ptr->upper) + { + if (newPosition < lowerValue()) + { + d_ptr->swapControls(); + d_ptr->firstMovement = false; + } + } + else + { + d_ptr->firstMovement = false; + } + } + + if (d_ptr->lowerPressed == QStyle::SC_SliderHandle) + { + if (d_ptr->movement == NoCrossing) + newPosition = qMin(newPosition, upperValue()); + else if (d_ptr->movement == NoOverlapping) + newPosition = qMin(newPosition, upperValue() - 1); + + if (d_ptr->movement == FreeMovement && newPosition > d_ptr->upper) + { + d_ptr->swapControls(); + setUpperPosition(newPosition); + } + else + { + setLowerPosition(newPosition); + } + } + else if (d_ptr->upperPressed == QStyle::SC_SliderHandle) + { + if (d_ptr->movement == NoCrossing) + newPosition = qMax(newPosition, lowerValue()); + else if (d_ptr->movement == NoOverlapping) + newPosition = qMax(newPosition, lowerValue() + 1); + + if (d_ptr->movement == FreeMovement && newPosition < d_ptr->lower) + { + d_ptr->swapControls(); + setLowerPosition(newPosition); + } + else + { + setUpperPosition(newPosition); + } + } + event->accept(); +} + +/*! + \reimp + */ +void QxtSpanSlider::mouseReleaseEvent(QMouseEvent* event) +{ + QSlider::mouseReleaseEvent(event); + setSliderDown(false); + d_ptr->lowerPressed = QStyle::SC_None; + d_ptr->upperPressed = QStyle::SC_None; + update(); +} + +/*! + \reimp + */ +void QxtSpanSlider::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event); + QStylePainter painter(this); + + // groove & ticks + QStyleOptionSlider opt; + d_ptr->initStyleOption(&opt); + opt.sliderValue = 0; + opt.sliderPosition = 0; + opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderTickmarks; + painter.drawComplexControl(QStyle::CC_Slider, opt); + + // handle rects + opt.sliderPosition = d_ptr->lowerPos; + const QRect lr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); + const int lrv = d_ptr->pick(lr.center()); + opt.sliderPosition = d_ptr->upperPos; + const QRect ur = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); + const int urv = d_ptr->pick(ur.center()); + + // span + const int minv = qMin(lrv, urv); + const int maxv = qMax(lrv, urv); + const QPoint c = QRect(lr.center(), ur.center()).center(); + QRect spanRect; + if (orientation() == Qt::Horizontal) + spanRect = QRect(QPoint(minv, c.y() - 2), QPoint(maxv, c.y() + 1)); + else + spanRect = QRect(QPoint(c.x() - 2, minv), QPoint(c.x() + 1, maxv)); + d_ptr->drawSpan(&painter, spanRect); + + // handles + switch (d_ptr->lastPressed) + { + case QxtSpanSlider::LowerHandle: + d_ptr->drawHandle(&painter, QxtSpanSlider::UpperHandle); + d_ptr->drawHandle(&painter, QxtSpanSlider::LowerHandle); + break; + case QxtSpanSlider::UpperHandle: + default: + d_ptr->drawHandle(&painter, QxtSpanSlider::LowerHandle); + d_ptr->drawHandle(&painter, QxtSpanSlider::UpperHandle); + break; + } +} diff --git a/qxtspanslider.h b/qxtspanslider.h new file mode 100644 index 0000000..cd28ad9 --- /dev/null +++ b/qxtspanslider.h @@ -0,0 +1,109 @@ +#ifndef QXTSPANSLIDER_H +/**************************************************************************** +** Copyright (c) 2006 - 2011, the LibQxt project. +** See the Qxt AUTHORS file for a list of authors and copyright holders. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the LibQxt project nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +** +*****************************************************************************/ + +#define QXTSPANSLIDER_H + +#include +//#include "qxtnamespace.h" +//#include "qxtglobal.h" + +class QxtSpanSliderPrivate; + +class QxtSpanSlider : public QSlider { + Q_OBJECT + + //QXT_DECLARE_PRIVATE(QxtSpanSlider) + Q_PROPERTY(int lowerValue READ lowerValue WRITE setLowerValue) + Q_PROPERTY(int upperValue READ upperValue WRITE setUpperValue) + Q_PROPERTY(int lowerPosition READ lowerPosition WRITE setLowerPosition) + Q_PROPERTY(int upperPosition READ upperPosition WRITE setUpperPosition) + Q_PROPERTY(HandleMovementMode handleMovementMode READ handleMovementMode WRITE setHandleMovementMode) + Q_ENUMS(HandleMovementMode) + +public: + explicit QxtSpanSlider(QWidget* parent = 0); + explicit QxtSpanSlider(Qt::Orientation orientation, QWidget* parent = 0); + virtual ~QxtSpanSlider(); + + enum HandleMovementMode + { + FreeMovement, + NoCrossing, + NoOverlapping + }; + + enum SpanHandle + { + NoHandle, + LowerHandle, + UpperHandle + }; + + HandleMovementMode handleMovementMode() const; + void setHandleMovementMode(HandleMovementMode mode); + + int lowerValue() const; + int upperValue() const; + + int lowerPosition() const; + int upperPosition() const; + +public Q_SLOTS: + void setLowerValue(int lower); + void setUpperValue(int upper); + void setSpan(int lower, int upper); + + void setLowerPosition(int lower); + void setUpperPosition(int upper); + +Q_SIGNALS: + void spanChanged(int lower, int upper); + void lowerValueChanged(int lower); + void upperValueChanged(int upper); + + void lowerPositionChanged(int lower); + void upperPositionChanged(int upper); + + void sliderPressed(SpanHandle handle); + +protected: + virtual void keyPressEvent(QKeyEvent* event); + virtual void mousePressEvent(QMouseEvent* event); + virtual void mouseMoveEvent(QMouseEvent* event); + virtual void mouseReleaseEvent(QMouseEvent* event); + virtual void paintEvent(QPaintEvent* event); + +private: + QxtSpanSliderPrivate* d_ptr; + friend class QxtSpanSliderPrivate; +}; + +#endif // QXTSPANSLIDER_H diff --git a/qxtspanslider_p.h b/qxtspanslider_p.h new file mode 100644 index 0000000..f960efa --- /dev/null +++ b/qxtspanslider_p.h @@ -0,0 +1,84 @@ +#ifndef QXTSPANSLIDER_P_H +/**************************************************************************** +** Copyright (c) 2006 - 2011, the LibQxt project. +** See the Qxt AUTHORS file for a list of authors and copyright holders. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the LibQxt project nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +** +*****************************************************************************/ + +#define QXTSPANSLIDER_P_H + +#include +#include +#include "qxtspanslider.h" + +QT_FORWARD_DECLARE_CLASS(QStylePainter) +QT_FORWARD_DECLARE_CLASS(QStyleOptionSlider) + +class QxtSpanSliderPrivate : public QObject { + Q_OBJECT +public: + QxtSpanSliderPrivate(); + void initStyleOption(QStyleOptionSlider* option, QxtSpanSlider::SpanHandle handle = QxtSpanSlider::UpperHandle) const; + int pick(const QPoint& pt) const + { + return q_ptr->orientation() == Qt::Horizontal ? pt.x() : pt.y(); + } + int pixelPosToRangeValue(int pos) const; + void handleMousePress(const QPoint& pos, QStyle::SubControl& control, int value, QxtSpanSlider::SpanHandle handle); + void drawHandle(QStylePainter* painter, QxtSpanSlider::SpanHandle handle) const; + void setupPainter(QPainter* painter, Qt::Orientation orientation, qreal x1, qreal y1, qreal x2, qreal y2) const; + void drawSpan(QStylePainter* painter, const QRect& rect) const; + void triggerAction(QAbstractSlider::SliderAction action, bool main); + void swapControls(); + + int lower; + int upper; + int lowerPos; + int upperPos; + int offset; + int position; + QxtSpanSlider::SpanHandle lastPressed; + QxtSpanSlider::SpanHandle mainControl; + QStyle::SubControl lowerPressed; + QStyle::SubControl upperPressed; + QxtSpanSlider::HandleMovementMode movement; + bool firstMovement; + bool blockTracking; + + +public Q_SLOTS: + void updateRange(int min, int max); + void movePressedHandle(); + +private: + QxtSpanSlider* q_ptr; + friend class QxtSpanSlider; + + +}; + +#endif // QXTSPANSLIDER_P_H