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