diff --git a/src/gui/qxtflowview.cpp b/src/gui/qxtflowview.cpp index 30317281..31c38c09 100644 --- a/src/gui/qxtflowview.cpp +++ b/src/gui/qxtflowview.cpp @@ -1,795 +1,795 @@ /**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtGui module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ** ** This is a derived work of PictureFlow (http://pictureflow.googlecode.com) ** The original code was distributed under the following terms: ** ** Copyright (C) 2008 Ariya Hidayat (ariya@kde.org) ** Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal ** in the Software without restriction, including without limitation the rights ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ** copies of the Software, and to permit persons to whom the Software is ** furnished to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in ** all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ** THE SOFTWARE. ****************************************************************************/ #include "qxtflowview_p.h" #include "../tellico_debug.h" #include /*! \class QxtFlowView \inmodule QxtGui \brief The QxtFlowView widget is an item view for images with impressive flow effects A widget for showin images with animation effects, like Apple's Cover Flow (in iTunes and iPod). Images are arranged in form of slides, one main slide is shown at the center with few slides on the left and right sides of the center slide. When the next or previous slide is brought to the front, the whole slides flow to the left or to the right with smooth animation effect; until the new slide is finally placed at the center. \image qxtflowview.png "QxtFlowView in action." This is a derived work of \l{http://pictureflow.googlecode.com}{PictureFlow} */ /*! \enum QxtFlowView::ReflectionEffect \brief This enum describes available reflection effects. \value NoReflection No reflection \value PlainReflection Plain reflection \value BlurredReflection Blurred reflection */ /*! \fn QxtFlowView::currentIndexChanged(QModelIndex index) This signal is emitted whenever the current \a index has changed. */ /*! Constructs a new QxtFlowView with \a parent. */ QxtFlowView::QxtFlowView(QWidget* parent): QWidget(parent) { d = new QxtFlowViewPrivate; d->model = 0; d->selectionModel = 0; d->picrole = Qt::DecorationRole; d->textrole = Qt::DisplayRole; d->piccolumn = 0; d->textcolumn = 0; d->state = new QxtFlowViewState; d->state->reset(); d->state->reposition(); d->renderer = new QxtFlowViewSoftwareRenderer; d->renderer->state = d->state; d->renderer->widget = this; d->renderer->init(); d->animator = new QxtFlowViewAnimator; d->animator->state = d->state; QObject::connect(&d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation())); QObject::connect(&d->triggerTimer, SIGNAL(timeout()), this, SLOT(render())); setAttribute(Qt::WA_StaticContents, true); setAttribute(Qt::WA_OpaquePaintEvent, true); setAttribute(Qt::WA_NoSystemBackground, true); } /*! Destructs the flow view. */ QxtFlowView::~QxtFlowView() { delete d->renderer; delete d->animator; delete d->state; delete d; } /*! Sets the \a model. \bold {Note:} The view does not take ownership of the model unless it is the model's parent object because it may be shared between many different views. */ void QxtFlowView::setModel(QAbstractItemModel* model) { if(model == d->model) return; // TODO: move this into QxtFlowView class d->setModel(model); QItemSelectionModel* selection_model = new QItemSelectionModel(model, this); connect(model, SIGNAL(destroyed()), selection_model, SLOT(deleteLater())); setSelectionModel(selection_model); d->reset(); } /*! Returns the model. */ QAbstractItemModel* QxtFlowView::model() { return d->model; } /*! Sets the selection \a model. */ void QxtFlowView::setSelectionModel(QItemSelectionModel* selectionModel) { if(selectionModel && d->model && selectionModel->model() != d->model) { qWarning("QxtFlowViewPrivate::setSelectionModel() failed: " "Trying to set a selection model, which works on " "a different model than the view."); return; } if(d->selectionModel) { disconnect(d->selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); disconnect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SIGNAL(currentChanged(QModelIndex,QModelIndex))); } d->selectionModel = selectionModel; if(d->selectionModel) { connect(d->selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); connect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SIGNAL(currentChanged(QModelIndex,QModelIndex))); } } /*! Returns the selection model. */ QItemSelectionModel* QxtFlowView::selectionModel() { return d->selectionModel; } /*! \property QxtFlowView::backgroundColor \brief the background color The default value is black. */ QColor QxtFlowView::backgroundColor() const { return QColor(d->state->backgroundColor); } void QxtFlowView::setBackgroundColor(const QColor& c) { d->state->backgroundColor = c.rgb(); triggerRender(); } /*! \property QxtFlowView::slideSize \brief the slide size The slide dimensions are in pixels. The default value is 150x200. */ QSize QxtFlowView::slideSize() const { return QSize(d->state->slideWidth, d->state->slideHeight); } void QxtFlowView::setSlideSize(QSize size) { d->state->slideWidth = size.width(); d->state->slideHeight = size.height(); d->state->reposition(); triggerRender(); } /*! \property QxtFlowView::reflectionEffect \brief the reflection effect The default value is PlainReflection. */ QxtFlowView::ReflectionEffect QxtFlowView::reflectionEffect() const { return d->state->reflectionEffect; } void QxtFlowView::setReflectionEffect(ReflectionEffect effect) { d->state->reflectionEffect = effect; d->reset(); } /*! \property QxtFlowView::pictureRole \brief the picture role The default value is Qt::DecorationRole. */ int QxtFlowView::pictureRole() { return d->picrole; } void QxtFlowView::setPictureRole(int a) { d->picrole = a; d->reset(); } /*! \property QxtFlowView::pictureColumn \brief the picture column The default value is \c 0. */ int QxtFlowView::pictureColumn() { return d->piccolumn; } void QxtFlowView::setPictureColumn(int a) { d->piccolumn = a; d->reset(); } #if 0 int QxtFlowView::textRole() { return d->textrole; } void QxtFlowView::setTextRole(int a) { d->textrole = a; d->reset(); } int QxtFlowView::textColumn() { return d->textcolumn; } void QxtFlowView::setTextColumn(int a) { d->textcolumn = a; d->reset(); } #endif /*! \property QxtFlowView::rootIndex \brief the root index The root index is the parent index to the view's toplevel items. The root can be invalid. */ QModelIndex QxtFlowView::rootIndex() const { return d->rootindex; } void QxtFlowView::setRootIndex(QModelIndex index) { d->rootindex = index; } /*! \property QxtFlowView::currentIndex \brief the current index The slide of the current index is shown in the middle of the viewport. \bold {Note:} No animation effect will be produced. \sa showSlide() */ QModelIndex QxtFlowView::currentIndex() const { if (!d->model) return QModelIndex(); return d->currentcenter; } void QxtFlowView::setCurrentIndex(QModelIndex index) { d->setCurrentIndex(index); } /*! Rerender the widget. Normally this function will be automatically invoked whenever necessary, e.g. during the transition animation. */ void QxtFlowView::render() { d->renderer->dirty = true; update(); } /*! Schedules a rendering update. Unlike render(), this function does not cause immediate rendering.*/ void QxtFlowView::triggerRender() { d->triggerRender(); } /*! Shows previous slide using animation effect. */ void QxtFlowView::showPrevious() { int step = d->animator->step; int center = d->state->centerIndex; if (step > 0) d->animator->start(center); if (step == 0) if (center > 0) d->animator->start(center - 1); if (step < 0) d->animator->target = qMax(0, center - 2); } /*! Shows next slide using animation effect. */ void QxtFlowView::showNext() { int step = d->animator->step; int center = d->state->centerIndex; if (step < 0) d->animator->start(center); if (step == 0) if (center < d->state->slideImages.count() - 1) d->animator->start(center + 1); if (step > 0) d->animator->target = qMin(center + 2, d->state->slideImages.count() - 1); } /*! Go to specified slide at \a index using animation effect. */ void QxtFlowView::showSlide(QModelIndex index) { int r = d->modelmap.indexOf(index); if (r < 0) return; d->showSlide(r); } /*! \reimp */ void QxtFlowView::keyPressEvent(QKeyEvent * event) { if (event->key() == Qt::Key_Left) { if (event->modifiers() == Qt::ControlModifier) d->showSlide(currentIndex().row() - 10); else showPrevious(); event->accept(); return; } if (event->key() == Qt::Key_Right) { if (event->modifiers() == Qt::ControlModifier) d->showSlide(currentIndex().row() + 10); else showNext(); event->accept(); return; } event->ignore(); } /*! \reimp */ void QxtFlowView::mousePressEvent(QMouseEvent * event) { d->lastgrabpos = event->pos(); if(!d->selectionModel) return; QPersistentModelIndex index = d->modelmap.at(d->state->centerIndex); if (index.isValid()) { - myDebug() << "mousePressEvent() valid current index" << d->state->centerIndex; + myDebug() << "mousePressEvent(): valid current index" << d->state->centerIndex; d->selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); QItemSelection selection; selection.select(index, index); d->selectionModel->select(selection, QItemSelectionModel::Select); } else { - myDebug() << "mousePressEvent() invalid current index"; + myDebug() << "mousePressEvent(): invalid current index"; // Forces a finalize() even if mouse is pressed, but not on a item d->selectionModel->select(QModelIndex(), QItemSelectionModel::Select); } } /*! \reimp */ void QxtFlowView::mouseMoveEvent(QMouseEvent * event) { int i = (event->pos() - d->lastgrabpos).x() / (d->state->slideWidth / 4); if (i > 0) { showPrevious(); d->lastgrabpos = event->pos(); } if (i < 0) { showNext(); d->lastgrabpos = event->pos(); } } /*! \reimp */ void QxtFlowView::mouseReleaseEvent(QMouseEvent* event) { Q_UNUSED(event); } /*! \reimp */ void QxtFlowView::paintEvent(QPaintEvent* event) { Q_UNUSED(event); d->renderer->paint(); } /*! \reimp */ void QxtFlowView::resizeEvent(QResizeEvent* event) { triggerRender(); QWidget::resizeEvent(event); } /*! \reimp */ void QxtFlowView::wheelEvent(QWheelEvent * event) { if (event->orientation() == Qt::Horizontal) { event->ignore(); } else { int numSteps = -((event->delta() / 8) / 15); if (numSteps > 0) { for (int i = 0;i < numSteps;i++) { showNext(); } } else { for (int i = numSteps;i < 0;i++) { showPrevious(); } } event->accept(); } } /*! \internal */ void QxtFlowView::updateAnimation() { int old_center = d->state->centerIndex; d->animator->update(); triggerRender(); if (d->state->centerIndex != old_center) { d->currentcenter = d->modelmap.at(d->state->centerIndex); emit currentChanged(d->currentcenter, d->modelmap.at(old_center)); } } void QxtFlowViewPrivate::columnsAboutToBeInserted(const QModelIndex & parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); } void QxtFlowViewPrivate::columnsAboutToBeRemoved(const QModelIndex & parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); } void QxtFlowViewPrivate::columnsInserted(const QModelIndex & parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); } void QxtFlowViewPrivate::columnsRemoved(const QModelIndex & parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); } void QxtFlowViewPrivate::dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight) { Q_UNUSED(topLeft); Q_UNUSED(bottomRight); if (topLeft.parent() != rootindex) return; if (bottomRight.parent() != rootindex) return; int start = topLeft.row(); int end = bottomRight.row(); for (int i = start;i <= end;i++) replaceSlide(i, qvariant_cast(model->data(model->index(i, piccolumn, rootindex), picrole))); } void QxtFlowViewPrivate::headerDataChanged(Qt::Orientation orientation, int first, int last) { Q_UNUSED(orientation); Q_UNUSED(first); Q_UNUSED(last); } void QxtFlowViewPrivate::layoutAboutToBeChanged() { } void QxtFlowViewPrivate::layoutChanged() { reset(); setCurrentIndex(currentcenter); } void QxtFlowViewPrivate::modelAboutToBeReset() { } void QxtFlowViewPrivate::modelReset() { reset(); } void QxtFlowViewPrivate::rowsAboutToBeInserted(const QModelIndex & parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); } void QxtFlowViewPrivate::rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); } void QxtFlowViewPrivate::rowsInserted(const QModelIndex & parent, int start, int end) { if (rootindex != parent) return; for (int i = start;i <= end;i++) { QModelIndex idx = model->index(i, piccolumn, rootindex); insertSlide(i, qvariant_cast(model->data(idx, picrole))); modelmap.insert(i, idx); } } void QxtFlowViewPrivate::rowsRemoved(const QModelIndex & parent, int start, int end) { if (rootindex != parent) return; for (int i = start;i <= end;i++) { removeSlide(i); modelmap.removeAt(i); } } void QxtFlowViewPrivate::setModel(QAbstractItemModel * m) { if (model) { disconnect(this->model, SIGNAL(columnsAboutToBeInserted(const QModelIndex & , int , int)), this, SLOT(columnsAboutToBeInserted(const QModelIndex & , int , int))); disconnect(this->model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex & , int , int)), this, SLOT(columnsAboutToBeRemoved(const QModelIndex & , int , int))); disconnect(this->model, SIGNAL(columnsInserted(const QModelIndex & , int , int)), this, SLOT(columnsInserted(const QModelIndex & , int , int))); disconnect(this->model, SIGNAL(columnsRemoved(const QModelIndex & , int , int)), this, SLOT(columnsRemoved(const QModelIndex & , int , int))); disconnect(this->model, SIGNAL(dataChanged(const QModelIndex & , const QModelIndex &)), this, SLOT(dataChanged(const QModelIndex & , const QModelIndex &))); disconnect(this->model, SIGNAL(headerDataChanged(Qt::Orientation , int , int)), this, SLOT(headerDataChanged(Qt::Orientation , int , int))); disconnect(this->model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); disconnect(this->model, SIGNAL(layoutChanged()), this, SLOT(layoutChanged())); disconnect(this->model, SIGNAL(modelAboutToBeReset()), this, SLOT(modelAboutToBeReset())); disconnect(this->model, SIGNAL(modelReset()), this, SLOT(modelReset())); disconnect(this->model, SIGNAL(rowsAboutToBeInserted(const QModelIndex & , int , int)), this, SLOT(rowsAboutToBeInserted(const QModelIndex & , int , int))); disconnect(this->model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex & , int , int)), this, SLOT(rowsAboutToBeRemoved(const QModelIndex & , int , int))); disconnect(this->model, SIGNAL(rowsInserted(const QModelIndex & , int , int)), this, SLOT(rowsInserted(const QModelIndex & , int , int))); disconnect(this->model, SIGNAL(rowsRemoved(const QModelIndex & , int , int)), this, SLOT(rowsRemoved(const QModelIndex & , int , int))); } model = m; if (model) { rootindex = model->parent(QModelIndex()); connect(this->model, SIGNAL(columnsAboutToBeInserted(const QModelIndex & , int , int)), this, SLOT(columnsAboutToBeInserted(const QModelIndex & , int , int))); connect(this->model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex & , int , int)), this, SLOT(columnsAboutToBeRemoved(const QModelIndex & , int , int))); connect(this->model, SIGNAL(columnsInserted(const QModelIndex & , int , int)), this, SLOT(columnsInserted(const QModelIndex & , int , int))); connect(this->model, SIGNAL(columnsRemoved(const QModelIndex & , int , int)), this, SLOT(columnsRemoved(const QModelIndex & , int , int))); connect(this->model, SIGNAL(dataChanged(const QModelIndex & , const QModelIndex &)), this, SLOT(dataChanged(const QModelIndex & , const QModelIndex &))); connect(this->model, SIGNAL(headerDataChanged(Qt::Orientation , int , int)), this, SLOT(headerDataChanged(Qt::Orientation , int , int))); connect(this->model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); connect(this->model, SIGNAL(layoutChanged()), this, SLOT(layoutChanged())); connect(this->model, SIGNAL(modelAboutToBeReset()), this, SLOT(modelAboutToBeReset())); connect(this->model, SIGNAL(modelReset()), this, SLOT(modelReset())); connect(this->model, SIGNAL(rowsAboutToBeInserted(const QModelIndex & , int , int)), this, SLOT(rowsAboutToBeInserted(const QModelIndex & , int , int))); connect(this->model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex & , int , int)), this, SLOT(rowsAboutToBeRemoved(const QModelIndex & , int , int))); connect(this->model, SIGNAL(rowsInserted(const QModelIndex & , int , int)), this, SLOT(rowsInserted(const QModelIndex & , int , int))); connect(this->model, SIGNAL(rowsRemoved(const QModelIndex & , int , int)), this, SLOT(rowsRemoved(const QModelIndex & , int , int))); } } void QxtFlowViewPrivate::clear() { int c = state->slideImages.count(); for (int i = 0; i < c; i++) delete state->slideImages[i]; state->slideImages.resize(0); state->reset(); modelmap.clear(); triggerRender(); } void QxtFlowViewPrivate::triggerRender() { triggerTimer.setSingleShot(true); triggerTimer.start(0); } void QxtFlowViewPrivate::insertSlide(int index, const QImage& image) { state->slideImages.insert(index, new QImage(image)); triggerRender(); } void QxtFlowViewPrivate::replaceSlide(int index, const QImage& image) { Q_ASSERT((index >= 0) && (index < state->slideImages.count())); QImage* i = image.isNull() ? 0 : new QImage(image); delete state->slideImages[index]; state->slideImages[index] = i; triggerRender(); } void QxtFlowViewPrivate::removeSlide(int index) { delete state->slideImages[index]; state->slideImages.remove(index); triggerRender(); } void QxtFlowViewPrivate::showSlide(int index) { if (index == state->centerSlide.slideIndex) return; animator->start(index); } void QxtFlowViewPrivate::reset() { clear(); if (model) { for (int i = 0;i < model->rowCount(rootindex);i++) { QModelIndex idx = model->index(i, piccolumn, rootindex); insertSlide(i, qvariant_cast(model->data(idx, picrole))); modelmap.insert(i, idx); } if(modelmap.count()) currentcenter=modelmap.at(0); else currentcenter=QModelIndex(); } if(selectionModel) selectionModel->reset(); triggerRender(); } void QxtFlowViewPrivate::setCurrentIndex(QModelIndex index) { if (model->parent(index) != rootindex) return; int r = modelmap.indexOf(index); if (r < 0) return; state->centerIndex = r; state->reset(); animator->stop(r); triggerRender(); } diff --git a/src/gui/qxtflowview.h b/src/gui/qxtflowview.h index 3ab0b321..f3da7009 100644 --- a/src/gui/qxtflowview.h +++ b/src/gui/qxtflowview.h @@ -1,145 +1,145 @@ /**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtGui module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ** ** This is a derived work of PictureFlow (http://pictureflow.googlecode.com) ** The original code was distributed under the following terms: ** ** Copyright (C) 2008 Ariya Hidayat (ariya@kde.org) ** Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal ** in the Software without restriction, including without limitation the rights ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ** copies of the Software, and to permit persons to whom the Software is ** furnished to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in ** all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ** THE SOFTWARE. ****************************************************************************/ #ifndef QXT_FLOWVIEW_H #define QXT_FLOWVIEW_H #include #include #include class QxtFlowViewPrivate; class QxtFlowView : public QWidget { Q_OBJECT public: enum ReflectionEffect { NoReflection, PlainReflection, BlurredReflection }; private: Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) Q_PROPERTY(QSize slideSize READ slideSize WRITE setSlideSize) Q_PROPERTY(QModelIndex currentIndex READ currentIndex WRITE setCurrentIndex) Q_PROPERTY(int pictureRole READ pictureRole WRITE setPictureRole) Q_PROPERTY(int pictureColumn READ pictureColumn WRITE setPictureColumn) Q_PROPERTY(QModelIndex rootIndex READ rootIndex WRITE setRootIndex) Q_PROPERTY(ReflectionEffect reflectionEffect READ reflectionEffect WRITE setReflectionEffect) Q_ENUM(ReflectionEffect) public: QxtFlowView(QWidget* parent = 0); ~QxtFlowView(); void setModel(QAbstractItemModel* model); QAbstractItemModel* model(); void setSelectionModel(QItemSelectionModel* model); QItemSelectionModel* selectionModel(); QColor backgroundColor() const; void setBackgroundColor(const QColor& c); QSize slideSize() const; void setSlideSize(QSize size); QModelIndex currentIndex() const; QModelIndex rootIndex() const; void setRootIndex(QModelIndex index); ReflectionEffect reflectionEffect() const; void setReflectionEffect(ReflectionEffect effect); int pictureRole(); void setPictureRole(int); int pictureColumn(); void setPictureColumn(int); public Q_SLOTS: void setCurrentIndex(QModelIndex index); void showPrevious(); void showNext(); void showSlide(QModelIndex index); void render(); void triggerRender(); Q_SIGNALS: - virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); // TODO: update QxtFlowView::updateAnimation() to use selection model - virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); protected: virtual void paintEvent(QPaintEvent *event); virtual void keyPressEvent(QKeyEvent* event); virtual void mousePressEvent(QMouseEvent* event); virtual void mouseMoveEvent(QMouseEvent * event); virtual void mouseReleaseEvent(QMouseEvent* event); virtual void resizeEvent(QResizeEvent* event); virtual void wheelEvent(QWheelEvent * event); private Q_SLOTS: void updateAnimation(); private: QxtFlowViewPrivate* d; }; -#endif // PICTUREFLOW_H +#endif diff --git a/src/gui/qxtflowview_p.cpp b/src/gui/qxtflowview_p.cpp index a1cbc006..6276c074 100644 --- a/src/gui/qxtflowview_p.cpp +++ b/src/gui/qxtflowview_p.cpp @@ -1,706 +1,665 @@ /**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtGui module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ** ** This is a derived work of QxtFlowView (http://pictureflow.googlecode.com) ** The original code was distributed under the following terms: ** ** Copyright (C) 2008 Ariya Hidayat (ariya@kde.org) ** Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal ** in the Software without restriction, including without limitation the rights ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ** copies of the Software, and to permit persons to whom the Software is ** furnished to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in ** all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ** THE SOFTWARE. ****************************************************************************/ #include "qxtflowview_p.h" // ------------- QxtFlowViewState --------------------------------------- QxtFlowViewState::QxtFlowViewState(): backgroundColor(0), slideWidth(150), slideHeight(200), reflectionEffect(QxtFlowView::BlurredReflection), centerIndex(0) { } QxtFlowViewState::~QxtFlowViewState() { for (int i = 0; i < (int)slideImages.count(); i++) delete slideImages[i]; } // readjust the settings, call this when slide dimension is changed void QxtFlowViewState::reposition() { angle = 70 * IANGLE_MAX / 360; // approx. 70 degrees tilted offsetX = slideWidth / 2 * (PFREAL_ONE - fcos(angle)); offsetY = slideWidth / 2 * fsin(angle); offsetX += slideWidth * PFREAL_ONE; offsetY += slideWidth * PFREAL_ONE / 4; spacing = 40; } // adjust slides so that they are in "steady state" position void QxtFlowViewState::reset() { centerSlide.angle = 0; centerSlide.cx = 0; centerSlide.cy = 0; centerSlide.slideIndex = centerIndex; centerSlide.blend = 256; leftSlides.resize(6); for (int i = 0; i < (int)leftSlides.count(); i++) { SlideInfo& si = leftSlides[i]; si.angle = angle; si.cx = -(offsetX + spacing * i * PFREAL_ONE); si.cy = offsetY; si.slideIndex = centerIndex - 1 - i; si.blend = 256; if (i == (int)leftSlides.count() - 2) si.blend = 128; if (i == (int)leftSlides.count() - 1) si.blend = 0; } rightSlides.resize(6); for (int i = 0; i < (int)rightSlides.count(); i++) { SlideInfo& si = rightSlides[i]; si.angle = -angle; si.cx = offsetX + spacing * i * PFREAL_ONE; si.cy = offsetY; si.slideIndex = centerIndex + 1 + i; si.blend = 256; if (i == (int)rightSlides.count() - 2) si.blend = 128; if (i == (int)rightSlides.count() - 1) si.blend = 0; } } // ------------- QxtFlowViewAnimator --------------------------------------- QxtFlowViewAnimator::QxtFlowViewAnimator(): state(0), target(0), step(0), frame(0) { } void QxtFlowViewAnimator::start(int slide) { target = slide; if (!animateTimer.isActive() && state) { step = (target < state->centerSlide.slideIndex) ? -1 : 1; animateTimer.start(30); } } void QxtFlowViewAnimator::stop(int slide) { step = 0; target = slide; frame = slide << 16; animateTimer.stop(); } void QxtFlowViewAnimator::update() { if (!animateTimer.isActive()) return; if (step == 0) return; if (!state) return; int speed = 16384 / 4; #if 1 // deaccelerate when approaching the target const int max = 2 * 65536; int fi = frame; fi -= (target << 16); if (fi < 0) fi = -fi; fi = qMin(fi, max); int ia = IANGLE_MAX * (fi - max / 2) / (max * 2); speed = 512 + 16384 * (PFREAL_ONE + fsin(ia)) / PFREAL_ONE; #endif frame += speed * step; int index = frame >> 16; int pos = frame & 0xffff; int neg = 65536 - pos; int tick = (step < 0) ? neg : pos; PFreal ftick = (tick * PFREAL_ONE) >> 16; if (step < 0) index++; if (state->centerIndex != index) { state->centerIndex = index; frame = index << 16; state->centerSlide.slideIndex = state->centerIndex; for (int i = 0; i < (int)state->leftSlides.count(); i++) state->leftSlides[i].slideIndex = state->centerIndex - 1 - i; for (int i = 0; i < (int)state->rightSlides.count(); i++) state->rightSlides[i].slideIndex = state->centerIndex + 1 + i; } state->centerSlide.angle = (step * tick * state->angle) >> 16; state->centerSlide.cx = -step * fmul(state->offsetX, ftick); state->centerSlide.cy = fmul(state->offsetY, ftick); if (state->centerIndex == target) { stop(target); state->reset(); return; } for (int i = 0; i < (int)state->leftSlides.count(); i++) { SlideInfo& si = state->leftSlides[i]; si.angle = state->angle; si.cx = -(state->offsetX + state->spacing * i * PFREAL_ONE + step * state->spacing * ftick); si.cy = state->offsetY; } for (int i = 0; i < (int)state->rightSlides.count(); i++) { SlideInfo& si = state->rightSlides[i]; si.angle = -state->angle; si.cx = state->offsetX + state->spacing * i * PFREAL_ONE - step * state->spacing * ftick; si.cy = state->offsetY; } if (step > 0) { PFreal ftick = (neg * PFREAL_ONE) >> 16; state->rightSlides[0].angle = -(neg * state->angle) >> 16; state->rightSlides[0].cx = fmul(state->offsetX, ftick); state->rightSlides[0].cy = fmul(state->offsetY, ftick); } else { PFreal ftick = (pos * PFREAL_ONE) >> 16; state->leftSlides[0].angle = (pos * state->angle) >> 16; state->leftSlides[0].cx = -fmul(state->offsetX, ftick); state->leftSlides[0].cy = fmul(state->offsetY, ftick); } // must change direction ? if (target < index) if (step > 0) step = -1; if (target > index) if (step < 0) step = 1; // the first and last slide must fade in/fade out int nleft = state->leftSlides.count(); int nright = state->rightSlides.count(); int fade = pos / 256; for (int index = 0; index < nleft; index++) { int blend = 256; if (index == nleft - 1) blend = (step > 0) ? 0 : 128 - fade / 2; if (index == nleft - 2) blend = (step > 0) ? 128 - fade / 2 : 256 - fade / 2; if (index == nleft - 3) blend = (step > 0) ? 256 - fade / 2 : 256; state->leftSlides[index].blend = blend; } for (int index = 0; index < nright; index++) { int blend = (index < nright - 2) ? 256 : 128; if (index == nright - 1) blend = (step > 0) ? fade / 2 : 0; if (index == nright - 2) blend = (step > 0) ? 128 + fade / 2 : fade / 2; if (index == nright - 3) blend = (step > 0) ? 256 : 128 + fade / 2; state->rightSlides[index].blend = blend; } } // ------------- QxtFlowViewSoftwareRenderer --------------------------------------- QxtFlowViewSoftwareRenderer::QxtFlowViewSoftwareRenderer(): QxtFlowViewAbstractRenderer(), size(0, 0), bgcolor(0), effect(-1), blankSurface(0) { } QxtFlowViewSoftwareRenderer::~QxtFlowViewSoftwareRenderer() { surfaceCache.clear(); buffer = QImage(); delete blankSurface; } void QxtFlowViewSoftwareRenderer::paint() { if (!widget) return; if (widget->size() != size) init(); if (state->backgroundColor != bgcolor) { bgcolor = state->backgroundColor; surfaceCache.clear(); } if ((int)(state->reflectionEffect) != effect) { effect = (int)state->reflectionEffect; surfaceCache.clear(); } if (dirty) render(); QPainter painter(widget); painter.drawImage(QPoint(0, 0), buffer); } void QxtFlowViewSoftwareRenderer::init() { if (!widget) return; surfaceCache.clear(); blankSurface = 0; size = widget->size(); int ww = size.width(); int wh = size.height(); int w = (ww + 1) / 2; int h = (wh + 1) / 2; -#ifdef PICTUREFLOW_QT4 buffer = QImage(ww, wh, QImage::Format_RGB32); -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - buffer.create(ww, wh, 32); -#endif buffer.fill(bgcolor); rays.resize(w*2); for (int i = 0; i < w; i++) { PFreal gg = ((PFREAL_ONE >> 1) + i * PFREAL_ONE) / (2 * h); rays[w-i-1] = -gg; rays[w+i] = gg; } dirty = true; } // TODO: optimize this with lookup tables static QRgb blendColor(QRgb c1, QRgb c2, int blend) { int r = qRed(c1) * blend / 256 + qRed(c2) * (256 - blend) / 256; int g = qGreen(c1) * blend / 256 + qGreen(c2) * (256 - blend) / 256; int b = qBlue(c1) * blend / 256 + qBlue(c2) * (256 - blend) / 256; return qRgb(r, g, b); } static QImage* prepareSurface(const QImage* slideImage, int w, int h, QRgb bgcolor, QxtFlowView::ReflectionEffect reflectionEffect) { -#ifdef PICTUREFLOW_QT4 Qt::TransformationMode mode = Qt::SmoothTransformation; QImage img = slideImage->scaled(w, h, Qt::IgnoreAspectRatio, mode); -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - QImage img = slideImage->smoothScale(w, h); -#endif // slightly larger, to accommodate for the reflection int hs = h * 2; int hofs = h / 3; // offscreen buffer: black is sweet -#ifdef PICTUREFLOW_QT4 QImage* result = new QImage(hs, w, QImage::Format_RGB32); -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - QImage* result = new QImage; - result->create(hs, w, 32); -#endif result->fill(bgcolor); // transpose the image, this is to speed-up the rendering // because we process one column at a time // (and much better and faster to work row-wise, i.e in one scanline) for (int x = 0; x < w; x++) for (int y = 0; y < h; y++) result->setPixel(hofs + y, x, img.pixel(x, y)); if (reflectionEffect != QxtFlowView::NoReflection) { // create the reflection int ht = hs - h - hofs; int hte = ht; for (int x = 0; x < w; x++) for (int y = 0; y < ht; y++) { QRgb color = img.pixel(x, img.height() - y - 1); result->setPixel(h + hofs + y, x, blendColor(color, bgcolor, 128*(hte - y) / hte)); } if (reflectionEffect == QxtFlowView::BlurredReflection) { // blur the reflection everything first // Based on exponential blur algorithm by Jani Huhtanen QRect rect(hs / 2, 0, hs / 2, w); rect &= result->rect(); int r1 = rect.top(); int r2 = rect.bottom(); int c1 = rect.left(); int c2 = rect.right(); int bpl = result->bytesPerLine(); int rgba[4]; unsigned char* p; // how many times blur is applied? // for low-end system, limit this to only 1 loop for (int loop = 0; loop < 2; loop++) { for (int col = c1; col <= c2; col++) { p = result->scanLine(r1) + col * 4; for (int i = 0; i < 3; i++) rgba[i] = p[i] << 4; p += bpl; for (int j = r1; j < r2; j++, p += bpl) for (int i = 0; i < 3; i++) p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4; } for (int row = r1; row <= r2; row++) { p = result->scanLine(row) + c1 * 4; for (int i = 0; i < 3; i++) rgba[i] = p[i] << 4; p += 4; for (int j = c1; j < c2; j++, p += 4) for (int i = 0; i < 3; i++) p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4; } for (int col = c1; col <= c2; col++) { p = result->scanLine(r2) + col * 4; for (int i = 0; i < 3; i++) rgba[i] = p[i] << 4; p -= bpl; for (int j = r1; j < r2; j++, p -= bpl) for (int i = 0; i < 3; i++) p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4; } for (int row = r1; row <= r2; row++) { p = result->scanLine(row) + c2 * 4; for (int i = 0; i < 3; i++) rgba[i] = p[i] << 4; p -= 4; for (int j = c1; j < c2; j++, p -= 4) for (int i = 0; i < 3; i++) p[i] = (rgba[i] += (((p[i] << 4) - rgba[i])) >> 1) >> 4; } } // overdraw to leave only the reflection blurred (but not the actual image) for (int x = 0; x < w; x++) for (int y = 0; y < h; y++) result->setPixel(hofs + y, x, img.pixel(x, y)); } } return result; } QImage* QxtFlowViewSoftwareRenderer::surface(int slideIndex) { if (!state) return 0; if (slideIndex < 0) return 0; if (slideIndex >= (int)state->slideImages.count()) return 0; -#ifdef PICTUREFLOW_QT4 int key = slideIndex; -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - QString key = QString::number(slideIndex); -#endif QImage* img = state->slideImages.at(slideIndex); bool empty = img ? img->isNull() : true; if (empty) { surfaceCache.remove(key); imageHash.remove(slideIndex); if (!blankSurface) { int sw = state->slideWidth; int sh = state->slideHeight; -#ifdef PICTUREFLOW_QT4 QImage img = QImage(sw, sh, QImage::Format_RGB32); QPainter painter(&img); QPoint p1(sw*4 / 10, 0); QPoint p2(sw*6 / 10, sh); QLinearGradient linearGrad(p1, p2); linearGrad.setColorAt(0, Qt::black); linearGrad.setColorAt(1, Qt::white); painter.setBrush(linearGrad); painter.fillRect(0, 0, sw, sh, QBrush(linearGrad)); painter.setPen(QPen(QColor(64, 64, 64), 4)); painter.setBrush(QBrush()); painter.drawRect(2, 2, sw - 3, sh - 3); painter.end(); -#endif -#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) - QPixmap pixmap(sw, sh, 32); - QPainter painter(&pixmap); - painter.fillRect(pixmap.rect(), QColor(192, 192, 192)); - painter.fillRect(5, 5, sw - 10, sh - 10, QColor(64, 64, 64)); - painter.end(); - QImage img = pixmap.convertToImage(); -#endif blankSurface = prepareSurface(&img, sw, sh, bgcolor, state->reflectionEffect); } return blankSurface; } -#ifdef PICTUREFLOW_QT4 bool exist = imageHash.contains(slideIndex); if (exist) if (img == imageHash.find(slideIndex).value()) -#endif -#ifdef PICTUREFLOW_QT3 - bool exist = imageHash.find(slideIndex) != imageHash.end(); - if (exist) - if (img == imageHash.find(slideIndex).data()) -#endif -#ifdef PICTUREFLOW_QT2 - if (img == imageHash[slideIndex]) -#endif - if (surfaceCache.contains(key)) - return surfaceCache[key]; + if (surfaceCache.contains(key)) + return surfaceCache[key]; QImage* sr = prepareSurface(img, state->slideWidth, state->slideHeight, bgcolor, state->reflectionEffect); surfaceCache.insert(key, sr); imageHash.insert(slideIndex, img); return sr; } // Renders a slide to offscreen buffer. Returns a rect of the rendered area. // col1 and col2 limit the column for rendering. QRect QxtFlowViewSoftwareRenderer::renderSlide(const SlideInfo &slide, int col1, int col2) { int blend = slide.blend; if (!blend) return QRect(); QImage* src = surface(slide.slideIndex); if (!src) return QRect(); QRect rect(0, 0, 0, 0); int sw = src->height(); int sh = src->width(); int h = buffer.height(); int w = buffer.width(); if (col1 > col2) { int c = col2; col2 = col1; col1 = c; } col1 = (col1 >= 0) ? col1 : 0; col2 = (col2 >= 0) ? col2 : w - 1; col1 = qMin(col1, w - 1); col2 = qMin(col2, w - 1); int zoom = 100; int distance = h * 100 / zoom; PFreal sdx = fcos(slide.angle); PFreal sdy = fsin(slide.angle); PFreal xs = slide.cx - state->slideWidth * sdx / 2; PFreal ys = slide.cy - state->slideWidth * sdy / 2; PFreal dist = distance * PFREAL_ONE; int xi = qMax((PFreal)0, ((w * PFREAL_ONE / 2) + fdiv(xs * h, dist + ys)) >> PFREAL_SHIFT); if (xi >= w) return rect; bool flag = false; rect.setLeft(xi); for (int x = qMax(xi, col1); x <= col2; x++) { PFreal hity = 0; PFreal fk = rays[x]; if (sdy) { fk = fk - fdiv(sdx, sdy); hity = -fdiv((rays[x] * distance - slide.cx + slide.cy * sdx / sdy), fk); } dist = distance * PFREAL_ONE + hity; if (dist < 0) continue; PFreal hitx = fmul(dist, rays[x]); PFreal hitdist = fdiv(hitx - slide.cx, sdx); int column = sw / 2 + (hitdist >> PFREAL_SHIFT); if (column >= sw) break; if (column < 0) continue; rect.setRight(x); if (!flag) rect.setLeft(x); flag = true; int y1 = h / 2; int y2 = y1 + 1; QRgb* pixel1 = (QRgb*)(buffer.scanLine(y1)) + x; QRgb* pixel2 = (QRgb*)(buffer.scanLine(y2)) + x; QRgb pixelstep = pixel2 - pixel1; int center = (sh / 2); int dy = dist / h; int p1 = center * PFREAL_ONE - dy / 2; int p2 = center * PFREAL_ONE + dy / 2; const QRgb *ptr = (const QRgb*)(src->scanLine(column)); if (blend == 256) while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) { *pixel1 = ptr[p1 >> PFREAL_SHIFT]; *pixel2 = ptr[p2 >> PFREAL_SHIFT]; p1 -= dy; p2 += dy; y1--; y2++; pixel1 -= pixelstep; pixel2 += pixelstep; } else while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) { QRgb c1 = ptr[p1 >> PFREAL_SHIFT]; QRgb c2 = ptr[p2 >> PFREAL_SHIFT]; *pixel1 = blendColor(c1, bgcolor, blend); *pixel2 = blendColor(c2, bgcolor, blend); p1 -= dy; p2 += dy; y1--; y2++; pixel1 -= pixelstep; pixel2 += pixelstep; } } rect.setTop(0); rect.setBottom(h - 1); return rect; } void QxtFlowViewSoftwareRenderer::renderSlides() { int nleft = state->leftSlides.count(); int nright = state->rightSlides.count(); QRect r = renderSlide(state->centerSlide); int c1 = r.left(); int c2 = r.right(); for (int index = 0; index < nleft; index++) { QRect rs = renderSlide(state->leftSlides[index], 0, c1 - 1); if (!rs.isEmpty()) c1 = rs.left(); } for (int index = 0; index < nright; index++) { QRect rs = renderSlide(state->rightSlides[index], c2 + 1, buffer.width()); if (!rs.isEmpty()) c2 = rs.right(); } } // Render the slides. Updates only the offscreen buffer. void QxtFlowViewSoftwareRenderer::render() { buffer.fill(state->backgroundColor); renderSlides(); dirty = false; } diff --git a/src/gui/qxtflowview_p.h b/src/gui/qxtflowview_p.h index 559e90f2..2f100f86 100644 --- a/src/gui/qxtflowview_p.h +++ b/src/gui/qxtflowview_p.h @@ -1,300 +1,287 @@ /**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtGui module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ** ** This is a derived work of PictureFlow (http://pictureflow.googlecode.com) ** The original code was distributed under the following terms: ** ** Copyright (C) 2008 Ariya Hidayat (ariya@kde.org) ** Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal ** in the Software without restriction, including without limitation the rights ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ** copies of the Software, and to permit persons to whom the Software is ** furnished to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in ** all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ** THE SOFTWARE. ****************************************************************************/ #ifndef QXTFLOWVIEW_P_H_INCLUDED #define QXTFLOWVIEW_P_H_INCLUDED - #include "qxtflowview.h" -#define PICTUREFLOW_QT4 - #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for fixed-point arithmetic, we need minimum 32-bit long // long long (64-bit) might be useful for multiplication and division typedef long PFreal; #define PFREAL_SHIFT 10 #define PFREAL_ONE (1 << PFREAL_SHIFT) #define IANGLE_MAX 1024 #define IANGLE_MASK 1023 inline PFreal fmul(PFreal a, PFreal b) { return ((long long)(a))*((long long)(b)) >> PFREAL_SHIFT; } inline PFreal fdiv(PFreal num, PFreal den) { long long p = (long long)(num) << (PFREAL_SHIFT * 2); long long q = p / (long long)den; long long r = q >> PFREAL_SHIFT; return r; } inline PFreal fsin(int iangle) { // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed! static const PFreal tab[] = { 3, 103, 202, 300, 394, 485, 571, 652, 726, 793, 853, 904, 947, 980, 1004, 1019, 1023, 1018, 1003, 978, 944, 901, 849, 789, 721, 647, 566, 479, 388, 294, 196, 97, -4, -104, -203, -301, -395, -486, -572, -653, -727, -794, -854, -905, -948, -981, -1005, -1020, -1024, -1019, -1004, -979, -945, -902, -850, -790, -722, -648, -567, -480, -389, -295, -197, -98, 3 }; while (iangle < 0) iangle += IANGLE_MAX; iangle &= IANGLE_MASK; int i = (iangle >> 4); PFreal p = tab[i]; PFreal q = tab[(i+1)]; PFreal g = (q - p); return p + g * (iangle - i*16) / 16; } inline PFreal fcos(int iangle) { return fsin(iangle + (IANGLE_MAX >> 2)); } /* ---------------------------------------------------------- QxtFlowViewState stores the state of all slides, i.e. all the necessary information to be able to render them. QxtFlowViewAnimator is responsible to move the slides during the transition between slides, to achieve the effect similar to Cover Flow, by changing the state. QxtFlowViewSoftwareRenderer (or QxtFlowViewOpenGLRenderer) is the actual 3-d renderer. It should render all slides given the state (an instance of QxtFlowViewState). Instances of all the above three classes are stored in QxtFlowViewPrivate. ------------------------------------------------------- */ struct SlideInfo { int slideIndex; int angle; PFreal cx; PFreal cy; int blend; }; class QxtFlowViewState { public: QxtFlowViewState(); ~QxtFlowViewState(); void reposition(); void reset(); QRgb backgroundColor; int slideWidth; int slideHeight; QxtFlowView::ReflectionEffect reflectionEffect; QVector slideImages; int angle; int spacing; PFreal offsetX; PFreal offsetY; SlideInfo centerSlide; QVector leftSlides; QVector rightSlides; int centerIndex; }; class QxtFlowViewAnimator { public: QxtFlowViewAnimator(); QxtFlowViewState* state; void start(int slide); void stop(int slide); void update(); int target; int step; int frame; QTimer animateTimer; }; class QxtFlowViewAbstractRenderer { public: QxtFlowViewAbstractRenderer(): state(0), dirty(false), widget(0) {} virtual ~QxtFlowViewAbstractRenderer() {} QxtFlowViewState* state; bool dirty; QWidget* widget; virtual void init() = 0; virtual void paint() = 0; }; class QxtFlowViewSoftwareRenderer: public QxtFlowViewAbstractRenderer { public: QxtFlowViewSoftwareRenderer(); ~QxtFlowViewSoftwareRenderer(); virtual void init(); virtual void paint(); private: QSize size; QRgb bgcolor; int effect; QImage buffer; QVector rays; QImage* blankSurface; -#ifdef PICTUREFLOW_QT4 QCache surfaceCache; QHash imageHash; -#endif -#ifdef PICTUREFLOW_QT3 - QCache surfaceCache; - QMap imageHash; -#endif -#ifdef PICTUREFLOW_QT2 - QCache surfaceCache; - QIntDict imageHash; -#endif void render(); void renderSlides(); QRect renderSlide(const SlideInfo &slide, int col1 = -1, int col2 = -1); QImage* surface(int slideIndex); }; // ----------------------------------------- class QxtFlowViewPrivate : public QObject { Q_OBJECT public: QxtFlowViewState* state; QxtFlowViewAnimator* animator; QxtFlowViewAbstractRenderer* renderer; QTimer triggerTimer; QAbstractItemModel* model; QItemSelectionModel* selectionModel; void setModel(QAbstractItemModel* model); void clear(); void triggerRender(); void insertSlide(int index, const QImage& image); void replaceSlide(int index, const QImage& image); void removeSlide(int index); void setCurrentIndex(QModelIndex index); void showSlide(int index); int picrole; int textrole; int piccolumn; int textcolumn; void reset(); QList modelmap; QPersistentModelIndex currentcenter; QPoint lastgrabpos; QModelIndex rootindex; public Q_SLOTS: void columnsAboutToBeInserted(const QModelIndex & parent, int start, int end); void columnsAboutToBeRemoved(const QModelIndex & parent, int start, int end); void columnsInserted(const QModelIndex & parent, int start, int end); void columnsRemoved(const QModelIndex & parent, int start, int end); void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight); void headerDataChanged(Qt::Orientation orientation, int first, int last); void layoutAboutToBeChanged(); void layoutChanged(); void modelAboutToBeReset(); void modelReset(); void rowsAboutToBeInserted(const QModelIndex & parent, int start, int end); void rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end); void rowsInserted(const QModelIndex & parent, int start, int end); void rowsRemoved(const QModelIndex & parent, int start, int end); }; #endif // QXTFLOWVIEW_P_H_INCLUDED