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