1 /*******************************************************************************************************
2 DkBaseViewPort.cpp
3 Created on: 03.07.2013
4
5 nomacs is a fast and small image viewer with the capability of synchronizing multiple instances
6
7 Copyright (C) 2011-2013 Markus Diem <markus@nomacs.org>
8 Copyright (C) 2011-2013 Stefan Fiel <stefan@nomacs.org>
9 Copyright (C) 2011-2013 Florian Kleber <florian@nomacs.org>
10
11 This file is part of nomacs.
12
13 nomacs is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 nomacs is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
25
26 *******************************************************************************************************/
27
28 #include "DkBaseViewPort.h"
29 #include "DkActionManager.h"
30 #include "DkSettings.h"
31 #include "DkUtils.h"
32 #include "DkStatusBar.h"
33
34 #pragma warning(push, 0) // no warnings from includes - begin
35 #include <QCoreApplication>
36 #include <QTimer>
37 #include <QMovie>
38 #include <QShortcut>
39 #include <QDebug>
40 #include <QTimer>
41 #include <QSvgRenderer>
42 #include <QMainWindow>
43 #include <QScrollBar>
44
45 // gestures
46 #include <QSwipeGesture>
47
48 #pragma warning(pop) // no warnings from includes - end
49
50 #include <float.h>
51 #include <cassert>
52
53 namespace nmc {
54
55 // DkBaseViewport --------------------------------------------------------------------
DkBaseViewPort(QWidget * parent)56 DkBaseViewPort::DkBaseViewPort(QWidget *parent) : QGraphicsView(parent) {
57
58 grabGesture(Qt::PanGesture);
59 grabGesture(Qt::PinchGesture);
60 grabGesture(Qt::SwipeGesture);
61 setAttribute(Qt::WA_AcceptTouchEvents);
62
63 mViewportRect = QRect(0, 0, width(), height());
64
65 mPanControl = QPointF(-1.0f, -1.0f);
66
67 mAltMod = DkSettingsManager::param().global().altMod;
68 mCtrlMod = DkSettingsManager::param().global().ctrlMod;
69
70 mZoomTimer = new QTimer(this);
71 mZoomTimer->setSingleShot(true);
72 connect(mZoomTimer, SIGNAL(timeout()), this, SLOT(stopBlockZooming()));
73 connect(&mImgStorage, SIGNAL(imageUpdated()), this, SLOT(update()));
74
75 mPattern.setTexture(QPixmap(":/nomacs/img/tp-pattern.png"));
76
77 if (DkSettingsManager::param().display().defaultBackgroundColor)
78 setObjectName("DkBaseViewPortDefaultColor");
79 else
80 setObjectName("DkBaseViewPort");
81
82 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
83 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
84
85 setMouseTracking(true);
86
87 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
88 setMinimumSize(10, 10);
89
90 // connect pan actions
91 const DkActionManager& am = DkActionManager::instance();
92 connect(am.action(DkActionManager::sc_pan_left), SIGNAL(triggered()), this, SLOT(panLeft()));
93 connect(am.action(DkActionManager::sc_pan_right), SIGNAL(triggered()), this, SLOT(panRight()));
94 connect(am.action(DkActionManager::sc_pan_up), SIGNAL(triggered()), this, SLOT(panUp()));
95 connect(am.action(DkActionManager::sc_pan_down), SIGNAL(triggered()), this, SLOT(panDown()));
96
97 connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrollVertically(int)));
98 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrollHorizontally(int)));
99
100 mHideCursorTimer = new QTimer(this);
101 mHideCursorTimer->setInterval(1000);
102 connect(mHideCursorTimer, SIGNAL(timeout()), this, SLOT(hideCursor()));
103
104
105 }
106
~DkBaseViewPort()107 DkBaseViewPort::~DkBaseViewPort() {
108 }
109
zoomConstraints(double minZoom,double maxZoom)110 void DkBaseViewPort::zoomConstraints(double minZoom, double maxZoom) {
111
112 mMinZoom = minZoom;
113 mMaxZoom = maxZoom;
114 }
115
116 // zoom - pan --------------------------------------------------------------------
resetView()117 void DkBaseViewPort::resetView() {
118
119 mWorldMatrix.reset();
120 changeCursor();
121
122 update();
123 }
124
fullView()125 void DkBaseViewPort::fullView() {
126
127 mWorldMatrix.reset();
128 zoom(1.0/mImgMatrix.m11());
129 changeCursor();
130
131 update();
132 }
133
togglePattern(bool show)134 void DkBaseViewPort::togglePattern(bool show) {
135
136 DkSettingsManager::param().display().tpPattern = show;
137 update();
138 }
139
panLeft()140 void DkBaseViewPort::panLeft() {
141
142 float delta = -2*width()/(100.0f*(float)mWorldMatrix.m11());
143 moveView(QPointF(delta,0));
144 }
145
panRight()146 void DkBaseViewPort::panRight() {
147
148 float delta = 2*width()/(100.0f*(float)mWorldMatrix.m11());
149 moveView(QPointF(delta,0));
150 }
151
panUp()152 void DkBaseViewPort::panUp() {
153
154 float delta = -2*height()/(100.0f*(float)mWorldMatrix.m11());
155 moveView(QPointF(0,delta));
156 }
157
panDown()158 void DkBaseViewPort::panDown() {
159
160 float delta = 2*height()/(100.0f*(float)mWorldMatrix.m11());
161 moveView(QPointF(0,delta));
162 }
163
moveView(const QPointF & delta)164 void DkBaseViewPort::moveView(const QPointF& delta) {
165
166 QPointF lDelta = delta;
167 QRectF imgWorldRect = mWorldMatrix.mapRect(mImgViewRect);
168 if (imgWorldRect.width() < width())
169 lDelta.setX(0);
170 if (imgWorldRect.height() < height())
171 lDelta.setY(0);
172
173 mWorldMatrix.translate(lDelta.x(), lDelta.y());
174 controlImagePosition();
175 update();
176 }
177
178
zoomIn()179 void DkBaseViewPort::zoomIn() {
180
181 zoomLeveled(1.5);
182 }
183
zoomOut()184 void DkBaseViewPort::zoomOut() {
185
186 zoomLeveled(0.5);
187 }
188
zoomLeveled(double factor,const QPointF & center)189 void DkBaseViewPort::zoomLeveled(double factor, const QPointF& center) {
190
191 factor = DkZoomConfig::instance().nextFactor(mWorldMatrix.m11()*mImgMatrix.m11(), factor);
192 zoom(factor, center);
193 }
194
zoom(double factor,const QPointF & center,bool force)195 void DkBaseViewPort::zoom(double factor, const QPointF& center, bool force) {
196
197 if (mImgStorage.isEmpty())
198 return;
199
200 //limit zoom out ---
201 if (mWorldMatrix.m11()*factor < mMinZoom && factor < 1)
202 return;
203
204 // reset view & block if we pass the 'image fit to screen' on zoom out
205 if (mWorldMatrix.m11() > 1 && mWorldMatrix.m11()*factor < 1 && !force) {
206
207 mBlockZooming = true;
208 mZoomTimer->start(500);
209 resetView();
210 return;
211 }
212
213 // reset view if we pass the 'image fit to screen' on zoom in
214 if (mWorldMatrix.m11() < 1 && mWorldMatrix.m11()*factor > 1 && !force) {
215
216 resetView();
217 return;
218 }
219
220 //limit zoom in ---
221 if (mWorldMatrix.m11()*mImgMatrix.m11() > mMaxZoom && factor > 1)
222 return;
223
224 QPointF pos = center;
225
226 // if no center assigned: zoom in at the image center
227 if (pos.x() == -1 || pos.y() == -1)
228 pos = mImgViewRect.center();
229
230 zoomToPoint(factor, pos, mWorldMatrix);
231
232 controlImagePosition();
233 changeCursor();
234
235 update();
236 }
237
zoomToPoint(double factor,const QPointF & pos,QTransform & matrix) const238 void DkBaseViewPort::zoomToPoint(double factor, const QPointF & pos, QTransform & matrix) const {
239
240 //inverse the transform
241 double a, b;
242 matrix.inverted().map(pos.x(), pos.y(), &a, &b);
243
244 matrix.translate(a - factor * a, b - factor * b);
245 matrix.scale(factor, factor);
246 }
247
stopBlockZooming()248 void DkBaseViewPort::stopBlockZooming() {
249 mBlockZooming = false;
250 }
251
252 // set image --------------------------------------------------------------------
253 #ifdef WITH_OPENCV
setImage(cv::Mat newImg)254 void DkBaseViewPort::setImage(cv::Mat newImg) {
255
256 QImage imgQt = DkImage::mat2QImage(newImg);
257 setImage(imgQt);
258 }
259 #endif
260
setImage(QImage newImg)261 void DkBaseViewPort::setImage(QImage newImg) {
262
263 mImgStorage.setImage(newImg);
264 QRectF oldImgRect = mImgRect;
265 mImgRect = QRectF(QPoint(), getImageSize());
266
267 if (!DkSettingsManager::param().display().keepZoom || mImgRect != oldImgRect)
268 mWorldMatrix.reset();
269
270 updateImageMatrix();
271 update();
272 emit newImageSignal(&newImg);
273 }
274
hideCursor()275 void DkBaseViewPort::hideCursor() {
276
277 if (isFullScreen())
278 setCursor(Qt::BlankCursor);
279 }
280
getImage() const281 QImage DkBaseViewPort::getImage() const {
282
283 if (mMovie && mMovie->isValid())
284 return mMovie->currentImage();
285 if (mSvg && mSvg->isValid() && !mImgViewRect.isEmpty()) {
286
287 QImage img(mImgViewRect.size().toSize(), QImage::Format_ARGB32);
288 img.fill(QColor(0, 0, 0, 0));
289
290 QPainter p(&img);
291
292 if (mSvg && mSvg->isValid()) {
293 mSvg->render(&p, mImgViewRect);
294 }
295
296 return img;
297 }
298
299 return mImgStorage.imageConst();
300 }
301
getImageSize() const302 QSize DkBaseViewPort::getImageSize() const {
303
304 if (mSvg) {
305 //qDebug() << "win: " << size() << "svg:" << mSvg->defaultSize() << "scaled:" << mSvg->defaultSize().scaled(size(), Qt::KeepAspectRatio);
306 return mSvg->defaultSize().scaled(size(), Qt::KeepAspectRatio);
307 }
308
309 return mImgStorage.size();
310 }
311
getImageViewRect() const312 QRectF DkBaseViewPort::getImageViewRect() const {
313
314 return mWorldMatrix.mapRect(mImgViewRect);
315 }
316
getCurrentImageRegion()317 QImage DkBaseViewPort::getCurrentImageRegion() {
318
319 QRectF viewRect = QRectF(QPoint(), size());
320 viewRect = mWorldMatrix.inverted().mapRect(viewRect);
321 viewRect = mImgMatrix.inverted().mapRect(viewRect);
322
323 QImage imgR(viewRect.size().toSize(), QImage::Format_ARGB32);
324 imgR.fill(0);
325
326 QPainter painter(&imgR);
327 painter.drawImage(imgR.rect(), mImgStorage.image(), viewRect.toRect());
328 painter.end();
329
330 return imgR;
331 }
332
unloadImage(bool)333 bool DkBaseViewPort::unloadImage(bool) {
334
335 return true;
336 }
337
338 // events --------------------------------------------------------------------
paintEvent(QPaintEvent * event)339 void DkBaseViewPort::paintEvent(QPaintEvent* event) {
340
341 QPainter painter(viewport());
342
343 if (!mImgStorage.isEmpty()) {
344 painter.setWorldTransform(mWorldMatrix);
345
346 // don't interpolate - we have a sophisticated anti-aliasing methods
347 //// don't interpolate if we are forced to, at 100% or we exceed the maximal interpolation level
348 if (!mForceFastRendering && // force?
349 mImgMatrix.m11()*mWorldMatrix.m11()-DBL_EPSILON > 1.0 && // @100% ?
350 mImgMatrix.m11()*mWorldMatrix.m11() <= DkSettingsManager::param().display().interpolateZoomLevel/100.0) { // > max zoom level
351 painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing);
352 }
353
354 draw(painter);
355 }
356
357 // propagate
358 QGraphicsView::paintEvent(event);
359 }
360
resizeEvent(QResizeEvent * event)361 void DkBaseViewPort::resizeEvent(QResizeEvent *event) {
362
363 if (event->oldSize() == event->size())
364 return;
365
366 mViewportRect = QRect(0, 0, event->size().width(), event->size().height());
367
368 updateImageMatrix();
369 centerImage();
370 changeCursor();
371
372 return QGraphicsView::resizeEvent(event);
373 }
374
event(QEvent * event)375 bool DkBaseViewPort::event(QEvent *event) {
376
377 // TODO: check if we still need this
378 if (event->type() == QEvent::Gesture)
379 return gestureEvent(static_cast<QGestureEvent*>(event));
380
381 return QGraphicsView::event(event);
382 }
383
gestureEvent(QGestureEvent * event)384 bool DkBaseViewPort::gestureEvent(QGestureEvent* event) {
385
386 if (QGesture *swipeG = event->gesture(Qt::SwipeGesture)) {
387 QSwipeGesture *swipe = static_cast<QSwipeGesture *>(swipeG);
388
389 // thanks qt documentation : )
390 if (swipe->state() == Qt::GestureFinished) {
391 if (swipe->horizontalDirection() == QSwipeGesture::Left
392 || swipe->verticalDirection() == QSwipeGesture::Up)
393 qDebug() << "here comes the previous image function...";
394 else
395 qDebug() << "here comes the next image function...";
396 }
397 qDebug() << "swiping...";
398 }
399 else if (QPinchGesture *pinch = static_cast<QPinchGesture*>(event->gesture(Qt::PinchGesture))) {
400
401 #if QT_VERSION >= 0x050000
402 double scale = pinch->lastScaleFactor();
403
404 if (fabs(scale-1.0) > FLT_EPSILON) {
405 zoom(scale, mapFromGlobal(pinch->centerPoint().toPoint()));
406 }
407 #endif
408 qDebug() << "[Qt] pinching...";
409 }
410 else if (/*QGesture *pan = */event->gesture(Qt::PanGesture)) {
411 qDebug() << "panning...";
412 }
413 else
414 return false;
415
416 return true;
417 }
418
419 // key events --------------------------------------------------------------------
keyPressEvent(QKeyEvent * event)420 void DkBaseViewPort::keyPressEvent(QKeyEvent* event) {
421
422 // we want to change the behaviour on auto-repeat - so we cannot use QShortcuts here...
423 if (event->key() == DkActionManager::shortcut_zoom_in || event->key() == DkActionManager::shortcut_zoom_in_alt) {
424 zoom(event->isAutoRepeat() ? 1.1f : 1.5f);
425 }
426 if (event->key() == DkActionManager::shortcut_zoom_out || event->key() == DkActionManager::shortcut_zoom_out_alt) {
427 zoom(event->isAutoRepeat() ? 0.9f : 0.5f);
428 }
429
430 QWidget::keyPressEvent(event);
431 }
432
keyReleaseEvent(QKeyEvent * event)433 void DkBaseViewPort::keyReleaseEvent(QKeyEvent* event) {
434
435 #ifdef DK_CORE_DLL_EXPORT
436 if (!event->isAutoRepeat())
437 emit keyReleaseSignal(event); // make key presses available
438 #endif
439
440 QWidget::keyReleaseEvent(event);
441 }
442
443 // mouse events --------------------------------------------------------------------
mousePressEvent(QMouseEvent * event)444 void DkBaseViewPort::mousePressEvent(QMouseEvent *event) {
445
446 // ok, start panning
447 if (mWorldMatrix.m11() > 1 && !imageInside() && event->buttons() == Qt::LeftButton) {
448 setCursor(Qt::ClosedHandCursor);
449 }
450
451 mPosGrab = event->pos();
452
453 QWidget::mousePressEvent(event);
454 }
455
mouseReleaseEvent(QMouseEvent * event)456 void DkBaseViewPort::mouseReleaseEvent(QMouseEvent *event) {
457
458 if (mWorldMatrix.m11() > 1 && !imageInside())
459 setCursor(Qt::OpenHandCursor);
460
461 QWidget::mouseReleaseEvent(event);
462 }
463
mouseDoubleClickEvent(QMouseEvent * event)464 void DkBaseViewPort::mouseDoubleClickEvent(QMouseEvent *event) {
465
466 QCoreApplication::sendEvent(parentWidget(), event);
467 }
468
mouseMoveEvent(QMouseEvent * event)469 void DkBaseViewPort::mouseMoveEvent(QMouseEvent *event) {
470
471 if (mWorldMatrix.m11() > 1 && event->buttons() == Qt::LeftButton) {
472
473 QPointF cPos = event->pos();
474 QPointF dxy = (cPos - mPosGrab);
475 mPosGrab = cPos;
476 moveView(dxy/mWorldMatrix.m11());
477 }
478 if (event->buttons() != Qt::LeftButton && event->buttons() != Qt::RightButton) {
479
480 if (event->modifiers() == mCtrlMod && event->modifiers() != mAltMod) {
481 setCursor(Qt::CrossCursor);
482 //DkStatusBarManager::instance().show(true, false);
483 }
484 else if (mWorldMatrix.m11() > 1 && !imageInside())
485 setCursor(Qt::OpenHandCursor);
486 else {
487
488 if (!DkSettingsManager::param().app().showStatusBar)
489 DkStatusBarManager::instance().show(false, false);
490
491 if (cursor().shape() != Qt::ArrowCursor)
492 unsetCursor();
493 }
494
495 if (isFullScreen())
496 mHideCursorTimer->start(3000);
497 }
498
499 QWidget::mouseMoveEvent(event);
500 }
501
wheelEvent(QWheelEvent * event)502 void DkBaseViewPort::wheelEvent(QWheelEvent *event) {
503
504 double factor = -event->delta();
505 if (DkSettingsManager::param().display().invertZoom) factor *= -1.0;
506
507 factor /= -1200.0;
508 factor += 1.0;
509
510 //qDebug() << "zoom factor..." << factor;
511 zoomLeveled(factor, event->pos());
512 }
513
contextMenuEvent(QContextMenuEvent * event)514 void DkBaseViewPort::contextMenuEvent(QContextMenuEvent *event) {
515
516 // send this event to my parent...
517 QWidget::contextMenuEvent(event);
518 }
519
520 // protected functions --------------------------------------------------------------------
draw(QPainter & painter,double opacity)521 void DkBaseViewPort::draw(QPainter & painter, double opacity) {
522
523 if (DkUtils::getMainWindow()->isFullScreen()) {
524 painter.setWorldMatrixEnabled(false);
525 painter.fillRect(QRect(QPoint(), size()), DkSettingsManager::param().slideShow().backgroundColor);
526 painter.setWorldMatrixEnabled(true);
527 }
528
529 if (backgroundBrush() != Qt::NoBrush) {
530 painter.setWorldMatrixEnabled(false);
531 painter.fillRect(QRect(QPoint(), size()), backgroundBrush());
532 painter.setWorldMatrixEnabled(true);
533 }
534
535 QImage img = mImgStorage.image((float)(mImgMatrix.m11()*mWorldMatrix.m11()));
536
537 // opacity == 1.0f -> do not show pattern if we crossfade two images
538 if (DkSettingsManager::param().display().tpPattern && img.hasAlphaChannel() && opacity == 1.0)
539 drawPattern(painter);
540
541 double oldOp = painter.opacity();
542 painter.setOpacity(opacity);
543
544 if (mSvg && mSvg->isValid()) {
545 mSvg->render(&painter, mImgViewRect);
546 }
547 else if (mMovie && mMovie->isValid())
548 painter.drawPixmap(mImgViewRect, mMovie->currentPixmap(), mMovie->frameRect());
549 else {
550
551 // if we have the exact level cached: render it directly
552 QRect ir = mWorldMatrix.mapRect(mImgViewRect).toRect();
553
554 // we actually ask for ir.size() == imgQt.size()
555 // but account for rounding issues with aspect ratio (<= 1)
556 if (qAbs(ir.width() - img.width()) <= 1 &&
557 qAbs(ir.height() - img.height()) <= 1) {
558
559 // match image size & ir size
560 // this removes an (often vertical) fissure in the smoothed image
561 ir.setSize(img.size());
562
563 painter.setWorldMatrixEnabled(false);
564 painter.setRenderHint(QPainter::SmoothPixmapTransform, false);
565 painter.drawImage(ir, img, img.rect());
566 painter.setWorldMatrixEnabled(true);
567 }
568 else {
569 if (mImgMatrix.m11()*mWorldMatrix.m11() - DBL_EPSILON < 1.0)
570 painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
571 painter.drawImage(mImgViewRect, img, img.rect());
572 }
573 }
574
575 painter.setOpacity(oldOp);
576 }
577
drawPattern(QPainter & painter) const578 void DkBaseViewPort::drawPattern(QPainter & painter) const {
579
580 QBrush pt = mPattern;
581
582 // don't scale the pattern...
583 QTransform scaleIv;
584 scaleIv.scale(mWorldMatrix.m11(), mWorldMatrix.m22());
585 pt.setTransform(scaleIv.inverted());
586
587 painter.setPen(QPen(Qt::NoPen)); // no border
588 painter.setBrush(pt);
589 painter.drawRect(mImgViewRect);
590 }
591
imageInside() const592 bool DkBaseViewPort::imageInside() const {
593
594 QRect viewRect = mWorldMatrix.mapRect(mImgViewRect).toRect();
595
596 return mWorldMatrix.m11() <= 1.0f || mViewportRect.contains(viewRect);
597 }
598
updateImageMatrix()599 void DkBaseViewPort::updateImageMatrix() {
600
601 if (mImgStorage.isEmpty())
602 return;
603
604 QRectF oldImgRect = mImgViewRect;
605 QTransform oldImgMatrix = mImgMatrix;
606
607 mImgMatrix.reset();
608
609 QSize imgSize = getImageSize();
610
611 // if the image is smaller or zoom is active: paint the image as is
612 if (!mViewportRect.contains(mImgRect))
613 mImgMatrix = getScaledImageMatrix();
614 else {
615 mImgMatrix.translate((float)(width()-imgSize.width())*0.5f, (float)(height()-imgSize.height())*0.5f);
616 mImgMatrix.scale(1.0f, 1.0f);
617 }
618
619 mImgViewRect = mImgMatrix.mapRect(mImgRect);
620
621 // update world matrix
622 if (mWorldMatrix.m11() != 1) {
623
624 double scaleFactor = oldImgMatrix.m11()/mImgMatrix.m11();
625 double dx = oldImgRect.x()/scaleFactor-mImgViewRect.x();
626 double dy = oldImgRect.y()/scaleFactor-mImgViewRect.y();
627
628 mWorldMatrix.scale(scaleFactor, scaleFactor);
629 mWorldMatrix.translate(dx, dy);
630 }
631 }
632
getScaledImageMatrix() const633 QTransform DkBaseViewPort::getScaledImageMatrix() const {
634 return getScaledImageMatrix(size());
635 }
636
getScaledImageMatrix(const QSize & size) const637 QTransform DkBaseViewPort::getScaledImageMatrix(const QSize& size) const {
638
639 // the image resizes as we zoom
640 float ratioImg = (float)mImgRect.width()/(float)mImgRect.height();
641 float ratioWin = (float)size.width()/(float)size.height();
642
643 QTransform imgMatrix;
644 float s;
645 if (mImgRect.width() == 0 || mImgRect.height() == 0)
646 s = 1.0f;
647 else
648 s = (ratioImg > ratioWin) ? (float)size.width()/(float)mImgRect.width() : (float)size.height()/(float)mImgRect.height();
649
650 imgMatrix.scale(s, s);
651
652 QRectF imgViewRect = imgMatrix.mapRect(mImgRect);
653 imgMatrix.translate((size.width()-imgViewRect.width())*0.5f/s, (size.height()-imgViewRect.height())*0.5f/s);
654
655 return imgMatrix;
656 }
657
controlImagePosition(float lb,float ub)658 void DkBaseViewPort::controlImagePosition(float lb, float ub) {
659
660 QRectF imgRectWorld = mWorldMatrix.mapRect(mImgViewRect);
661
662 if (lb == -1 && ub == -1 && mPanControl.x() != -1 && mPanControl.y() != -1) {
663 lb = (float)mPanControl.x();
664 ub = (float)mPanControl.y();
665 }
666 // we must not pan further if scrollbars are visible
667 else if (lb == -1 && ub == -1 && DkSettingsManager::instance().param().display().showScrollBars) {
668 lb = 0.0f;
669 ub = 0.0f;
670 }
671 else {
672
673 // default behavior
674 if (lb == -1) lb = (float)mViewportRect.width()/2.0f;
675 if (ub == -1) ub = (float)mViewportRect.height()/2.0f;
676 }
677
678 if (imgRectWorld.left() > lb && imgRectWorld.width() > width())
679 mWorldMatrix.translate((lb-imgRectWorld.left())/mWorldMatrix.m11(), 0);
680
681 if (imgRectWorld.top() > ub && imgRectWorld.height() > height())
682 mWorldMatrix.translate(0, (ub-imgRectWorld.top())/mWorldMatrix.m11());
683
684 if (imgRectWorld.right() < width()-lb && imgRectWorld.width() > width())
685 mWorldMatrix.translate(((width()-lb)-imgRectWorld.right())/mWorldMatrix.m11(), 0);
686
687 if (imgRectWorld.bottom() < height()-ub && imgRectWorld.height() > height())
688 mWorldMatrix.translate(0, ((height()-ub)-imgRectWorld.bottom())/mWorldMatrix.m11());
689
690 // update scene size (this is needed to make the scroll area work)
691 if (DkSettingsManager::instance().param().display().showScrollBars)
692 setSceneRect(getImageViewRect());
693
694 emit imageUpdated();
695 }
696
centerImage()697 void DkBaseViewPort::centerImage() {
698
699 QRectF imgWorldRect = mWorldMatrix.mapRect(mImgViewRect);
700 float dx, dy;
701
702 // if black border - center the image
703 if (imgWorldRect.width() < (float)width()) {
704 dx = (float)((width()-imgWorldRect.width())*0.5f-mImgViewRect.x()*mWorldMatrix.m11());
705 dx = (dx-(float)mWorldMatrix.dx())/(float)mWorldMatrix.m11();
706 mWorldMatrix.translate(dx, 0);
707 }
708 else if (imgWorldRect.left() > 0)
709 mWorldMatrix.translate(-imgWorldRect.left()/mWorldMatrix.m11(), 0);
710 else if (imgWorldRect.right() < width())
711 mWorldMatrix.translate((width()-imgWorldRect.right())/mWorldMatrix.m11(), 0);
712
713 if (imgWorldRect.height() < height()) {
714 dy = (float)((height()-imgWorldRect.height())*0.5f-mImgViewRect.y()*mWorldMatrix.m22());
715 dy = (float)((dy-mWorldMatrix.dy())/mWorldMatrix.m22());
716 mWorldMatrix.translate(0, dy);
717 }
718 else if (imgWorldRect.top() > 0) {
719 mWorldMatrix.translate(0, -imgWorldRect.top()/mWorldMatrix.m22());
720 }
721 else if (imgWorldRect.bottom() < height()) {
722 mWorldMatrix.translate(0, (height()-imgWorldRect.bottom())/mWorldMatrix.m22());
723 }
724 }
725
changeCursor()726 void DkBaseViewPort::changeCursor() {
727
728 if (mWorldMatrix.m11() > 1 && !imageInside())
729 setCursor(Qt::OpenHandCursor);
730 else
731 unsetCursor();
732 }
733
setBackgroundBrush(const QBrush & brush)734 void DkBaseViewPort::setBackgroundBrush(const QBrush &brush) {
735
736 QGraphicsView::setBackgroundBrush(brush);
737 }
738
scrollHorizontally(int val)739 void DkBaseViewPort::scrollHorizontally(int val) {
740 moveView(QPointF(-val / mWorldMatrix.m11(), 0.0f));
741 }
742
scrollVertically(int val)743 void DkBaseViewPort::scrollVertically(int val) {
744 moveView(QPointF(0.0f, -val/mWorldMatrix.m11()));
745 }
746
747 // Anna's first text:
748 //gcfbxxxxxxxxxxxbxbbbcx
749 //tggbeeeeeeeeeeecddddeebljlljl�
750 //
751 //
752 //rr45�[, mgd 7re � 8n484welkmsclsdmvcdsm wr., bpodsa0][gb b c
753
754 }
755