File indexing completed on 2024-05-19 16:01:25
0001 /* This file is part of the KDE project 0002 Copyright (C) 2010 KO GmbH <jos.van.den.oever@kogmbh.com> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 */ 0019 #include "slideview.h" 0020 #include "slideloader.h" 0021 #include <QDebug> 0022 #include <QEvent> 0023 #include <QGraphicsItem> 0024 #include <QImage> 0025 #include <QVBoxLayout> 0026 #include <QPixmapCache> 0027 #include <QGraphicsSceneEvent> 0028 #include <QScrollBar> 0029 #include <QTransform> 0030 #include <QWheelEvent> 0031 #include <QGLWidget> 0032 #include <cmath> 0033 0034 class GraphicsSlideItem : public QGraphicsItem { 0035 public: 0036 #ifdef QT46 0037 class Cache { 0038 private: 0039 QPixmapCache::Key key; 0040 public: 0041 bool find(QPixmap& pixmap) const { 0042 return QPixmapCache::find(key, &pixmap); 0043 } 0044 void clear() const { QPixmapCache::remove(key); } 0045 void add(const QPixmap& pixmap) { 0046 clear(); 0047 key = QPixmapCache::insert(pixmap); 0048 } 0049 }; 0050 #else 0051 class Cache { 0052 private: 0053 const QString key; 0054 public: 0055 Cache() :key(QString("%1 GraphicsSlideItem").arg((qlonglong)this)) {} 0056 bool find(QPixmap& pixmap) const { 0057 return QPixmapCache::find(key, pixmap); 0058 } 0059 void clear() const { QPixmapCache::remove(key); } 0060 void add(const QPixmap& pixmap) { 0061 clear(); 0062 QPixmapCache::insert(key, pixmap); 0063 } 0064 }; 0065 #endif 0066 Cache cache; 0067 QRectF rect; 0068 QPainterPath path; 0069 SlideView* const view; 0070 int position; 0071 int slideVersion; 0072 0073 explicit GraphicsSlideItem(int pos, SlideView* v) 0074 :QGraphicsItem(), view(v), position(pos) { 0075 slideVersion = -1; 0076 } 0077 void setPosition(int pos) { 0078 if (position != pos) { 0079 position = pos; 0080 slideVersion = -1; 0081 } 0082 if (view->loader->slideVersion(position) != slideVersion) { 0083 update(rect); 0084 } 0085 } 0086 void paint(QPainter* painter, const QStyleOptionGraphicsItem * /*option*/, 0087 QWidget * /*widget*/ = 0) { 0088 QPixmap pixmap; 0089 if (slideVersion != view->loader->slideVersion(position) 0090 || !cache.find(pixmap)) { 0091 slideVersion = view->loader->slideVersion(position); 0092 pixmap = view->loader->loadSlide(position, rect.size().toSize()); 0093 cache.add(pixmap); 0094 } 0095 if (pixmap.isNull()) { 0096 painter->fillRect(rect, Qt::gray); 0097 } else { 0098 painter->drawPixmap(rect.topLeft(), pixmap); 0099 } 0100 } 0101 QRectF boundingRect() const { 0102 return rect; 0103 } 0104 QPainterPath opaqueArea() const { 0105 return path; 0106 } 0107 void setBoundingRect(const QRectF& newrect) { 0108 if (rect != newrect) { 0109 if (rect.size() != newrect.size()) { 0110 cache.clear(); 0111 } 0112 prepareGeometryChange(); 0113 rect = newrect; 0114 path = QPainterPath(); 0115 path.addRect(rect); 0116 } 0117 } 0118 void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * /*event*/) { 0119 view->toggleSlideZoom(this); 0120 } 0121 }; 0122 0123 SlideView::SlideView(SlideLoader* l, QWidget* parent) :QWidget(parent), 0124 loader(l), zoomfactor(0.25), sendingChange(false) 0125 { 0126 // use opengl canvas 0127 view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); 0128 // listen to resize and wheel events 0129 view.viewport()->installEventFilter(this); 0130 0131 connect(loader, SIGNAL(slidesChanged()), this, SLOT(slotUpdateSlides())); 0132 connect(view.verticalScrollBar(), SIGNAL(valueChanged(int)), 0133 this, SLOT(slotViewChanged())); 0134 connect(view.horizontalScrollBar(), SIGNAL(valueChanged(int)), 0135 this, SLOT(slotViewChanged())); 0136 0137 QVBoxLayout *layout = new QVBoxLayout(); 0138 layout->setContentsMargins(0, 0, 0, 0); 0139 layout->setSpacing(0); 0140 layout->addWidget(&view); 0141 layout->addWidget(&progressBar); 0142 setLayout(layout); 0143 0144 progressBar.setVisible(false); 0145 scene.setBackgroundBrush(Qt::Dense4Pattern); 0146 view.setScene(&scene); 0147 0148 slotUpdateSlides(); 0149 } 0150 0151 void SlideView::slotUpdateSlides() { 0152 int numberOfSlides = loader->numberOfSlides(); 0153 QList<QGraphicsItem*> items = scene.items(); 0154 if (items.size() > numberOfSlides) { 0155 // remove surplus items 0156 for (int i = numberOfSlides; i < items.size(); ++i) { 0157 scene.removeItem(items[i]); 0158 } 0159 } else if (numberOfSlides > items.size()) { 0160 // add new items 0161 for (int i=items.size(); i<numberOfSlides; ++i) { 0162 GraphicsSlideItem* item = new GraphicsSlideItem(i, this); 0163 item->setVisible(true); 0164 scene.addItem(item); 0165 } 0166 } 0167 items = scene.items(); 0168 for (int i=0; i<items.size(); ++i) { 0169 static_cast<GraphicsSlideItem*>(items[i])->setPosition(i); 0170 } 0171 layout(); 0172 } 0173 void SlideView::layout() { 0174 const qreal spacing = 2; 0175 QSizeF slidesize = loader->slideSize(); 0176 int slidesPerRow = (zoomfactor > 1) ?1 :1/zoomfactor; 0177 0178 if (zoomfactor <= 1) { 0179 view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0180 } else { 0181 view.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); 0182 } 0183 qreal viewwidth = view.viewport()->width(); 0184 0185 qreal x = 0; 0186 qreal y = 0; 0187 qreal screenslidewidth = zoomfactor * viewwidth; 0188 0189 qreal w, h, viewscale; 0190 if (screenslidewidth > slidesize.width()) { 0191 viewscale = screenslidewidth / slidesize.width(); 0192 w = slidesize.width(); 0193 h = slidesize.height(); 0194 } else { 0195 viewscale = 1; 0196 qreal slidescale = screenslidewidth / slidesize.width(); 0197 w = slidesize.width() * slidescale; 0198 h = slidesize.height() * slidescale; 0199 } 0200 QTransform transform; 0201 transform.scale(viewscale, viewscale); 0202 view.setTransform(transform); 0203 0204 const QList<QGraphicsItem *> items = scene.items(); 0205 qreal dx = w + spacing; 0206 qreal dy = h + spacing; 0207 for (int i=0; i < items.size(); ++i) { 0208 GraphicsSlideItem* item = static_cast<GraphicsSlideItem*>(items[i]); 0209 item->setBoundingRect(QRectF(x, y, w, h)); 0210 if ((i+1) % slidesPerRow) { 0211 x += dx; 0212 } else { 0213 x = 0; 0214 y += dy; 0215 } 0216 } 0217 if (x == 0) { 0218 scene.setSceneRect(0, 0, slidesPerRow*dx, y); 0219 } else { 0220 scene.setSceneRect(0, 0, slidesPerRow*dx, y + dy); 0221 } 0222 } 0223 bool SlideView::eventFilter(QObject * obj, QEvent *event) { 0224 if (obj != view.viewport()) return false; 0225 if (event->type() == QEvent::Wheel) { 0226 const QWheelEvent* e = static_cast<QWheelEvent*>(event); 0227 if (e->modifiers() == Qt::ControlModifier) { 0228 zoomfactor *= pow(1.1, e->delta()/120.0); 0229 layout(); 0230 slotViewChanged(); 0231 return true; 0232 } 0233 } else if (event->type() == QEvent::Resize) { 0234 layout(); 0235 slotViewChanged(); 0236 } 0237 return false; 0238 } 0239 void 0240 SlideView::setView(qreal zoomfactor, int h, int v) { 0241 if (sendingChange) return; 0242 this->zoomfactor = zoomfactor; 0243 if (scene.items().size()) 0244 layout(); 0245 view.horizontalScrollBar()->setValue(h); 0246 view.verticalScrollBar()->setValue(v); 0247 } 0248 void SlideView::slotViewChanged() { 0249 sendingChange = true; 0250 int h = view.horizontalScrollBar()->value(); 0251 int v = view.verticalScrollBar()->value(); 0252 emit viewChanged(zoomfactor, h, v); 0253 sendingChange = false; 0254 } 0255 void SlideView::toggleSlideZoom(const GraphicsSlideItem* item) { 0256 // zoom to zoomfactor 1 with clicked slide on the top, unless that slide 0257 // is already active, then zoom to level 0.25 in the range of the clicked 0258 // slide 0259 // this function does not work well, the behavior of the scrollbar is a 0260 // mystery, but the magic dance seems to work ok so far 0261 QScrollBar* sb = view.verticalScrollBar(); 0262 int offset = 2; 0263 qreal y = item->boundingRect().top(); 0264 qDebug() << y << " " << sb->value(); 0265 if (zoomfactor == 1 0266 && (qAbs(y - sb->value()) <= offset+1 0267 || sb->value()+1 >= sb->maximum())) { 0268 zoomfactor = 0.25; 0269 } else { 0270 zoomfactor = 1; 0271 } 0272 layout(); 0273 // magic dance 0274 layout(); 0275 view.mapToScene(0, 0).y(); 0276 // end of magic dance 0277 y = item->boundingRect().top(); 0278 view.verticalScrollBar()->setValue(y - offset); // small offset looks nice 0279 } 0280 void SlideView::SlideGraphicsScene::dragEnterEvent( 0281 QGraphicsSceneDragDropEvent *event) 0282 { 0283 event->ignore(); 0284 }