1 /*******************************************************************************************************
2 DkViewPort.cpp
3 Created on: 05.05.2011
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 "DkViewPort.h"
29
30 #include "DkControlWidget.h"
31 #include "DkImageLoader.h"
32 #include "DkWidgets.h"
33 #include "DkSettings.h"
34 #include "DkNetwork.h"
35 #include "DkThumbsWidgets.h" // needed in the connects -> shall we move them to mController?
36 #include "DkMetaDataWidgets.h"
37 #include "DkToolbars.h"
38 #include "DkMetaData.h"
39 #include "DkPluginManager.h"
40 #include "DkActionManager.h"
41 #include "DkStatusBar.h"
42 #include "DkUtils.h"
43 #include "DkBasicLoader.h"
44 #include "DkDialog.h"
45 #include "DkMessageBox.h"
46 #include "DkToolbars.h"
47
48 #pragma warning(push, 0) // no warnings from includes - begin
49 #include <QClipboard>
50 #include <QMovie>
51 #include <QMimeData>
52 #include <QAction>
53 #include <QApplication>
54 #include <QVBoxLayout>
55 #include <QDragLeaveEvent>
56 #include <QDrag>
57 #include <QInputDialog>
58 #include <QMessageBox>
59 #include <QDesktopWidget>
60 #include <QSvgRenderer>
61 #include <QMenu>
62 #include <QtConcurrentRun>
63 #include <QPainterPath>
64
65 #include <qmath.h>
66 #pragma warning(pop) // no warnings from includes - end
67
68 #include <assert.h>
69
70 #ifdef Q_OS_WIN
71 #include <windows.h>
72 #endif
73
74 namespace nmc {
75
76 // DkViewPort --------------------------------------------------------------------
DkViewPort(QWidget * parent)77 DkViewPort::DkViewPort(QWidget *parent) : DkBaseViewPort(parent) {
78
79 mRepeatZoomTimer = new QTimer(this);
80 mAnimationTimer = new QTimer(this);
81
82 // try loading a custom file
83 mImgBg.load(QFileInfo(QApplication::applicationDirPath(), "bg.png").absoluteFilePath());
84 if (mImgBg.isNull() && DkSettingsManager::param().global().showBgImage) {
85 QColor col = backgroundBrush().color().darker();
86 mImgBg = DkImage::loadIcon(":/nomacs/img/nomacs-bg.svg", col, QSize(100, 100)).toImage();
87 }
88
89 mRepeatZoomTimer->setInterval(20);
90 connect(mRepeatZoomTimer, SIGNAL(timeout()), this, SLOT(repeatZoom()));
91
92 mAnimationTimer->setInterval(5);
93 connect(mAnimationTimer, SIGNAL(timeout()), this, SLOT(animateFade()));
94
95 //no border
96 setMouseTracking(true);//receive mouse event everytime
97
98 mPaintLayout = new QVBoxLayout(this);
99 mPaintLayout->setContentsMargins(0,0,0,0);
100
101 createShortcuts();
102
103 mController = new DkControlWidget(this);
104
105 mLoader = QSharedPointer<DkImageLoader>(new DkImageLoader());
106 connectLoader(mLoader);
107
108 if (DkSettingsManager::param().display().showScrollBars) {
109 setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
110 setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
111 }
112
113 mController->getOverview()->setTransforms(&mWorldMatrix, &mImgMatrix);
114 mController->getCropWidget()->setWorldTransform(&mWorldMatrix);
115 mController->getCropWidget()->setImageTransform(&mImgMatrix);
116 mController->getCropWidget()->setImageRect(&mImgViewRect);
117
118 // this must be initialized after mController to be above it
119 mNavigationWidget = new DkHudNavigation(this);
120 mPaintLayout->addWidget(mNavigationWidget);
121 // TODO: if visible, currently mNavigationWidget eats all mouse events that are supposed for control widget
122
123 // add actions
124 DkActionManager& am = DkActionManager::instance();
125 addActions(am.fileActions().toList());
126 addActions(am.viewActions().toList());
127 addActions(am.editActions().toList());
128 addActions(am.manipulatorActions().toList());
129 addActions(am.sortActions().toList());
130 addActions(am.toolsActions().toList());
131 addActions(am.panelActions().toList());
132 addActions(am.syncActions().toList());
133 addActions(am.pluginActions().toList());
134 addActions(am.helpActions().toList());
135 addActions(am.hiddenActions().toList());
136
137 addActions(am.openWithActions().toList());
138 #ifdef WITH_PLUGINS
139 addActions(am.pluginActionManager()->pluginDummyActions().toList());
140 #endif
141
142 connect(&mImgStorage, SIGNAL(infoSignal(const QString&)), this, SIGNAL(infoSignal(const QString&)));
143
144 if (am.pluginActionManager())
145 connect(am.pluginActionManager(), SIGNAL(runPlugin(DkPluginContainer*, const QString&)), this, SLOT(applyPlugin(DkPluginContainer*, const QString&)));
146
147 // connect
148 connect(am.action(DkActionManager::menu_file_reload), SIGNAL(triggered()), this, SLOT(reloadFile()));
149 connect(am.action(DkActionManager::menu_file_next), SIGNAL(triggered()), this, SLOT(loadNextFileFast()));
150 connect(am.action(DkActionManager::menu_file_prev), SIGNAL(triggered()), this, SLOT(loadPrevFileFast()));
151 connect(am.action(DkActionManager::menu_file_save), SIGNAL(triggered()), this, SLOT(saveFile()));
152 connect(am.action(DkActionManager::menu_file_save_as), SIGNAL(triggered()), this, SLOT(saveFileAs()));
153 connect(am.action(DkActionManager::menu_file_save_web), SIGNAL(triggered()), this, SLOT(saveFileWeb()));
154 connect(am.action(DkActionManager::menu_tools_wallpaper), SIGNAL(triggered()), this, SLOT(setAsWallpaper()));
155
156 connect(am.action(DkActionManager::menu_edit_rotate_cw), SIGNAL(triggered()), this, SLOT(rotateCW()));
157 connect(am.action(DkActionManager::menu_edit_rotate_ccw), SIGNAL(triggered()), this, SLOT(rotateCCW()));
158 connect(am.action(DkActionManager::menu_edit_rotate_180), SIGNAL(triggered()), this, SLOT(rotate180()));
159 connect(am.action(DkActionManager::menu_edit_transform), SIGNAL(triggered()), this, SLOT(resizeImage()));
160 connect(am.action(DkActionManager::menu_edit_delete), SIGNAL(triggered()), this, SLOT(deleteImage()));
161 connect(am.action(DkActionManager::menu_edit_copy), SIGNAL(triggered()), this, SLOT(copyImage()));
162 connect(am.action(DkActionManager::menu_edit_copy_buffer), SIGNAL(triggered()), this, SLOT(copyImageBuffer()));
163 connect(am.action(DkActionManager::menu_edit_copy_color), SIGNAL(triggered()), this, SLOT(copyPixelColorValue()));
164
165 connect(am.action(DkActionManager::menu_view_reset), SIGNAL(triggered()), this, SLOT(zoomToFit()));
166 connect(am.action(DkActionManager::menu_view_100), SIGNAL(triggered()), this, SLOT(fullView()));
167 connect(am.action(DkActionManager::menu_view_zoom_in), SIGNAL(triggered()), this, SLOT(zoomIn()));
168 connect(am.action(DkActionManager::menu_view_zoom_out), SIGNAL(triggered()), this, SLOT(zoomOut()));
169 connect(am.action(DkActionManager::menu_view_tp_pattern), SIGNAL(toggled(bool)), this, SLOT(togglePattern(bool)));
170 connect(am.action(DkActionManager::menu_view_movie_pause), SIGNAL(triggered(bool)), this, SLOT(pauseMovie(bool)));
171 connect(am.action(DkActionManager::menu_view_movie_prev), SIGNAL(triggered()), this, SLOT(previousMovieFrame()));
172 connect(am.action(DkActionManager::menu_view_movie_next), SIGNAL(triggered()), this, SLOT(nextMovieFrame()));
173
174 connect(am.action(DkActionManager::sc_test_img), SIGNAL(triggered()), this, SLOT(loadLena()));
175 connect(am.action(DkActionManager::menu_sync_view), SIGNAL(triggered()), this, SLOT(tcpForceSynchronize()));
176
177 // playing
178 connect(mNavigationWidget, SIGNAL(previousSignal()), this, SLOT(loadPrevFileFast()));
179 connect(mNavigationWidget, SIGNAL(nextSignal()), this, SLOT(loadNextFileFast()));
180
181 // trivial connects
182 connect(this, &DkViewPort::movieLoadedSignal,
183 [this](bool movie) { DkActionManager::instance().enableMovieActions(movie); });
184
185 // connect sync
186 auto cm = DkSyncManager::inst().client();
187
188 connect(this, SIGNAL(sendTransformSignal(QTransform, QTransform, QPointF)), cm, SLOT(sendTransform(QTransform, QTransform, QPointF)));
189 connect(this, SIGNAL(sendNewFileSignal(qint16, const QString&)), cm, SLOT(sendNewFile(qint16, const QString&)));
190 connect(cm, SIGNAL(receivedNewFile(qint16, const QString&)), this, SLOT(tcpLoadFile(qint16, const QString&)));
191 connect(cm, SIGNAL(updateConnectionSignal(const QString&)), mController, SLOT(setInfo(const QString&)));
192 connect(cm, SIGNAL(receivedTransformation(QTransform, QTransform, QPointF)), this, SLOT(tcpSetTransforms(QTransform, QTransform, QPointF)));
193
194 for (auto action : am.manipulatorActions())
195 connect(action, SIGNAL(triggered()), this, SLOT(applyManipulator()));
196
197 connect(&mManipulatorWatcher, SIGNAL(finished()), this, SLOT(manipulatorApplied()));
198
199 // TODO:
200 // one could blur the canvas if a transparent GUI is present
201 // what we would need: QGraphicsBlurEffect...
202 // render all widgets to the alpha channel (bw)
203 // pre-render the mViewport to that image... apply blur
204 // and then render the blurred image after the widget is rendered...
205 // performance?!
206
207 }
208
~DkViewPort()209 DkViewPort::~DkViewPort() {
210
211 mController->closePlugin(false, true);
212
213 mManipulatorWatcher.cancel();
214 mManipulatorWatcher.blockSignals(true);
215 }
216
createShortcuts()217 void DkViewPort::createShortcuts() {
218
219 DkActionManager& am = DkActionManager::instance();
220 connect(am.action(DkActionManager::sc_first_file), SIGNAL(triggered()), this, SLOT(loadFirst()));
221 connect(am.action(DkActionManager::sc_last_file), SIGNAL(triggered()), this, SLOT(loadLast()));
222 connect(am.action(DkActionManager::sc_skip_prev), SIGNAL(triggered()), this, SLOT(loadSkipPrev10()));
223 connect(am.action(DkActionManager::sc_skip_next), SIGNAL(triggered()), this, SLOT(loadSkipNext10()));
224 connect(am.action(DkActionManager::sc_first_file_sync), SIGNAL(triggered()), this, SLOT(loadFirst()));
225 connect(am.action(DkActionManager::sc_last_file_sync), SIGNAL(triggered()), this, SLOT(loadLast()));
226 connect(am.action(DkActionManager::sc_skip_next_sync), SIGNAL(triggered()), this, SLOT(loadNextFileFast()));
227 connect(am.action(DkActionManager::sc_skip_prev_sync), SIGNAL(triggered()), this, SLOT(loadPrevFileFast()));
228
229 }
230
setPaintWidget(QWidget * widget,bool removeWidget)231 void DkViewPort::setPaintWidget(QWidget* widget, bool removeWidget) {
232
233 if (!removeWidget) {
234 mPaintLayout->addWidget(widget);
235 //pluginImageWasApplied = false;
236 } else {
237 mPaintLayout->removeWidget(widget);
238 //widget->deleteLater();
239 }
240
241 mController->raise();
242 }
243
244 #ifdef WITH_OPENCV
setImage(cv::Mat newImg)245 void DkViewPort::setImage(cv::Mat newImg) {
246
247 QImage imgQt = DkImage::mat2QImage(newImg);
248 setImage(imgQt);
249 }
250 #endif
251
updateImage(QSharedPointer<DkImageContainerT> image,bool loaded)252 void DkViewPort::updateImage(QSharedPointer<DkImageContainerT> image, bool loaded) {
253
254 // things todo if a file was not loaded...
255 if (!loaded) {
256 mController->getPlayer()->startTimer();
257 return;
258 }
259
260 // should not happen -> the mLoader should send this signal
261 if (!mLoader)
262 return;
263
264 if (mLoader->hasImage()) {
265 setImage(mLoader->getImage());
266 }
267 }
268
loadImage(const QImage & newImg)269 void DkViewPort::loadImage(const QImage& newImg) {
270
271 // delete current information
272 if (mLoader) {
273 if (!unloadImage(true))
274 return; // user canceled
275
276 mLoader->setImage(newImg, tr("Original Image"));
277 setImage(newImg);
278
279 // save to temp folder
280 mLoader->saveTempFile(newImg);
281 }
282 }
283
loadImage(QSharedPointer<DkImageContainerT> img)284 void DkViewPort::loadImage(QSharedPointer<DkImageContainerT> img) {
285
286 if (mLoader) {
287
288 if (!unloadImage(true))
289 return;
290
291 if (img->hasImage()) {
292 mLoader->setCurrentImage(img);
293 setImage(img->image());
294 }
295 mLoader->load(img);
296 }
297
298 }
299
setImage(QImage newImg)300 void DkViewPort::setImage(QImage newImg) {
301
302 // calling show here fixes issues with the HUD
303 show();
304
305 DkTimer dt;
306
307 emit movieLoadedSignal(false);
308 stopMovie(); // just to be sure
309
310 if (mManipulatorWatcher.isRunning())
311 mManipulatorWatcher.cancel();
312
313 mController->getOverview()->setImage(QImage()); // clear overview
314
315 mImgStorage.setImage(newImg);
316
317 if (mLoader->hasMovie() && !mLoader->isEdited())
318 loadMovie();
319 if (mLoader->hasSvg() && !mLoader->isEdited())
320 loadSvg();
321
322 mImgRect = QRectF(QPoint(), getImageSize());
323
324 DkActionManager::instance().enableImageActions(!newImg.isNull());
325 mController->imageLoaded(!newImg.isNull());
326
327 double oldZoom = mWorldMatrix.m11();// *mImgMatrix.m11();
328
329 if (!(DkSettingsManager::param().display().keepZoom == DkSettings::zoom_keep_same_size && mOldImgRect == mImgRect))
330 mWorldMatrix.reset();
331
332 updateImageMatrix();
333
334 // if image is not inside, we'll align it at the top left border
335 if (!mViewportRect.intersects(mWorldMatrix.mapRect(mImgViewRect))) {
336 mWorldMatrix.translate(-mWorldMatrix.dx(), -mWorldMatrix.dy());
337 centerImage();
338 }
339
340 if (DkSettingsManager::param().display().keepZoom == DkSettings::zoom_always_keep) {
341 zoomToPoint(oldZoom, mImgViewRect.center().toPoint(), mWorldMatrix);
342 }
343
344 mController->getPlayer()->startTimer();
345 mController->getOverview()->setImage(newImg); // TODO: maybe we could make use of the image pyramid here
346
347 mOldImgRect = mImgRect;
348
349 // init fading
350 if (DkSettingsManager::param().display().animationDuration &&
351 DkSettingsManager::param().display().transition != DkSettingsManager::param().trans_appear &&
352 (mController->getPlayer()->isPlaying() ||
353 DkUtils::getMainWindow()->isFullScreen() ||
354 DkSettingsManager::param().display().alwaysAnimate)) {
355 mAnimationTimer->start();
356 mAnimationTime.start();
357 }
358 else
359 mAnimationValue = 0.0f;
360
361 // set/clear crop rect
362 if (mLoader->getCurrentImage())
363 mCropRect = mLoader->getCurrentImage()->cropRect();
364 else
365 mCropRect = DkRotatingRect();
366
367 update();
368
369 // draw a histogram from the image -> does nothing if the histogram is invisible
370 if (mController->getHistogram())
371 mController->getHistogram()->drawHistogram(newImg);
372
373 emit newImageSignal(&newImg);
374 emit zoomSignal(mWorldMatrix.m11()*mImgMatrix.m11()*100);
375
376 // status info
377 if (!newImg.isNull()) {
378 DkStatusBarManager::instance().setMessage(QString::number(qRound((float)(mWorldMatrix.m11()*mImgMatrix.m11() * 100))) + "%", DkStatusBar::status_zoom_info);
379 DkStatusBarManager::instance().setMessage(DkUtils::formatToString(newImg.format()), DkStatusBar::status_format_info);
380 DkStatusBarManager::instance().setMessage(QString::number(newImg.width()) + " x " + QString::number(newImg.height()), DkStatusBar::status_dimension_info);
381
382 if (imageContainer())
383 DkStatusBarManager::instance().setMessage(imageContainer()->fileName(), DkStatusBar::status_file_info);
384 }
385 else {
386 DkStatusBarManager::instance().setMessage("", DkStatusBar::status_zoom_info);
387 DkStatusBarManager::instance().setMessage("", DkStatusBar::status_format_info);
388 DkStatusBarManager::instance().setMessage("", DkStatusBar::status_dimension_info);
389 DkStatusBarManager::instance().setMessage("", DkStatusBar::status_file_info);
390 }
391 }
392
zoom(double factor,const QPointF & center,bool force)393 void DkViewPort::zoom(double factor, const QPointF& center, bool force) {
394
395 if (mImgStorage.isEmpty() || mBlockZooming)
396 return;
397
398 //factor/=5;//-0.1 <-> 0.1
399 //factor+=1;//0.9 <-> 1.1
400
401 //limit zoom out ---
402 if (mWorldMatrix.m11()*factor < mMinZoom && factor < 1)
403 return;
404
405 // reset view & block if we pass the 'image fit to screen' on zoom out
406 if (mWorldMatrix.m11() > 1 && mWorldMatrix.m11()*factor < 1 && !force) {
407
408 mBlockZooming = true;
409 mZoomTimer->start(500);
410 resetView();
411 return;
412 }
413
414 // reset view if we pass the 'image fit to screen' on zoom in
415 if (mWorldMatrix.m11() < 1 && mWorldMatrix.m11()*factor > 1 && !force) {
416
417 resetView();
418 return;
419 }
420
421 // TODO: the reset in mWorldMatrix introduces wrong pans
422 //// reset view & block if we pass the '100%' on zoom out
423 //if (mWorldMatrix.m11()*mImgMatrix.m11()-FLT_EPSILON > 1 && mWorldMatrix.m11()*mImgMatrix.m11()*factor < 1) {
424 //
425 // mBlockZooming = true;
426 // mZoomTimer->start(500);
427 // mWorldMatrix.reset();
428 // factor = 1.0f / (float)mImgMatrix.m11();
429 //}
430
431 //// reset view if we pass the '100%' on zoom in
432 //if (mWorldMatrix.m11()*mImgMatrix.m11()+FLT_EPSILON < 1 && mWorldMatrix.m11()*mImgMatrix.m11()*factor > 1) {
433
434 // mBlockZooming = true;
435 // mZoomTimer->start(500);
436 // mWorldMatrix.reset();
437 // factor = 1.0f / (float)mImgMatrix.m11();
438 //}
439
440
441 //limit zoom in ---
442 if (mWorldMatrix.m11()*mImgMatrix.m11() > mMaxZoom && factor > 1)
443 return;
444
445 bool blackBorder = false;
446
447 QPointF pos = center;
448
449 // if no center assigned: zoom in at the image center
450 if (pos.x() == -1 || pos.y() == -1)
451 pos = mImgViewRect.center();
452 else {
453
454 // if black border - do not zoom to the mouse coordinate
455 if (mImgViewRect.width()*(mWorldMatrix.m11()*factor) < width()) {
456 pos.setX(mImgViewRect.center().x());
457 blackBorder = true;
458 }
459 if ((mImgViewRect.height()*mWorldMatrix.m11()*factor) < height()) {
460 pos.setY(mImgViewRect.center().y());
461 blackBorder = true;
462 }
463 }
464
465 zoomToPoint(factor, pos, mWorldMatrix);
466
467 controlImagePosition();
468 if (blackBorder && factor < 1) centerImage(); // TODO: geht auch sch�ner
469 showZoom();
470 changeCursor();
471
472 mController->update(); // why do we need to update the mController manually?
473 update();
474
475 tcpSynchronize();
476
477 emit zoomSignal(mWorldMatrix.m11()*mImgMatrix.m11()*100);
478 DkStatusBarManager::instance().setMessage(QString::number(qRound(mWorldMatrix.m11()*mImgMatrix.m11() * 100)) + "%", DkStatusBar::status_zoom_info);
479 }
480
zoomTo(double zoomLevel)481 void DkViewPort::zoomTo(double zoomLevel) {
482
483 mWorldMatrix.reset();
484 zoom(zoomLevel/mImgMatrix.m11());
485 }
486
zoomToFit()487 void DkViewPort::zoomToFit() {
488
489 QSizeF imgSize = getImageSize();
490 QSizeF winSize = size();
491 double zoomLevel = qMin(winSize.width() / imgSize.width(), winSize.height() / imgSize.height());
492
493 if (zoomLevel > 1)
494 zoomTo(zoomLevel);
495 else if (zoomLevel < 1)
496 resetView();
497 else if (zoomLevel == 1 && mLoader && mLoader->hasSvg())
498 resetView();
499 }
500
resetView()501 void DkViewPort::resetView() {
502
503 mWorldMatrix.reset();
504 showZoom();
505 changeCursor();
506
507 update();
508 controlImagePosition();
509
510 emit zoomSignal(mWorldMatrix.m11()*mImgMatrix.m11() * 100);
511 tcpSynchronize();
512 }
513
fullView()514 void DkViewPort::fullView() {
515
516 QPointF p = mViewportRect.center();
517 zoom(1.0/(mImgMatrix.m11()*mWorldMatrix.m11()), p.toPoint(), true);
518
519 emit zoomSignal(mWorldMatrix.m11()*mImgMatrix.m11() * 100);
520 changeCursor();
521 update();
522 }
523
showZoom()524 void DkViewPort::showZoom() {
525
526 // don't show zoom if we are in fullscreen mode
527 if (isFullScreen() || DkSettingsManager::param().app().hideAllPanels)
528 return;
529
530 QString zoomStr;
531 zoomStr.sprintf("%.1f%%", mImgMatrix.m11()*mWorldMatrix.m11()*100);
532
533 if (!mController->getZoomWidget()->isVisible())
534 mController->setInfo(zoomStr, 3000, DkControlWidget::bottom_left_label);
535 }
536
repeatZoom()537 void DkViewPort::repeatZoom() {
538
539 qDebug() << "repeating...";
540 if ( (DkSettingsManager::param().display().invertZoom && QApplication::mouseButtons() == Qt::XButton1) ||
541 (!DkSettingsManager::param().display().invertZoom && QApplication::mouseButtons() == Qt::XButton2)) {
542 zoom(1.1f);
543 }
544 else if ( (!DkSettingsManager::param().display().invertZoom && QApplication::mouseButtons() == Qt::XButton1) ||
545 ( DkSettingsManager::param().display().invertZoom && QApplication::mouseButtons() == Qt::XButton2)) {
546 zoom(0.9f);
547 }
548 else {
549 mRepeatZoomTimer->stop(); // safety if we don't catch the release
550 }
551 }
552
toggleResetMatrix()553 void DkViewPort::toggleResetMatrix() {
554
555 DkSettingsManager::param().display().keepZoom = !DkSettingsManager::param().display().keepZoom;
556 }
557
updateImageMatrix()558 void DkViewPort::updateImageMatrix() {
559
560 if (mImgStorage.isEmpty())
561 return;
562
563 QRectF oldImgRect = mImgViewRect;
564 QTransform oldImgMatrix = mImgMatrix;
565
566 mImgMatrix.reset();
567
568 QSize imgSize = getImageSize();
569
570 // if the image is smaller or zoom is active: paint the image as is
571 if (!mViewportRect.contains(mImgRect.toRect()))
572 mImgMatrix = getScaledImageMatrix();
573 else {
574 mImgMatrix.translate((float)(getMainGeometry().width()-imgSize.width())*0.5f, (float)(getMainGeometry().height()-imgSize.height())*0.5f);
575 mImgMatrix.scale(1.0f, 1.0f);
576 }
577
578 mImgViewRect = mImgMatrix.mapRect(mImgRect);
579
580 // update world matrix?
581 // mWorldMatrix.m11() != 1
582 if (qAbs(mWorldMatrix.m11()-1.0) > 1e-4) {
583
584 float scaleFactor = (float)(oldImgMatrix.m11()/mImgMatrix.m11());
585 double dx = oldImgRect.x()/scaleFactor-mImgViewRect.x();
586 double dy = oldImgRect.y()/scaleFactor-mImgViewRect.y();
587
588 mWorldMatrix.scale(scaleFactor, scaleFactor);
589 mWorldMatrix.translate(dx, dy);
590 }
591 // NOTE: this is not the same as resetView!
592 else if (DkSettingsManager::param().display().zoomToFit)
593 zoomToFit();
594
595 }
596
tcpSetTransforms(QTransform newWorldMatrix,QTransform newImgMatrix,QPointF canvasSize)597 void DkViewPort::tcpSetTransforms(QTransform newWorldMatrix, QTransform newImgMatrix, QPointF canvasSize) {
598
599 // ok relative transform
600 if (canvasSize.isNull()) {
601 moveView(QPointF(newWorldMatrix.dx(), newWorldMatrix.dy())/mWorldMatrix.m11());
602 }
603 else {
604 mWorldMatrix = newWorldMatrix;
605 mImgMatrix = newImgMatrix;
606 updateImageMatrix();
607
608 QPointF imgPos = QPointF(canvasSize.x()*getImageSize().width(), canvasSize.y()*getImageSize().height());
609
610 // go to screen coordinates
611 imgPos = mImgMatrix.map(imgPos);
612
613 // go to world coordinates
614 imgPos = mWorldMatrix.map(imgPos);
615
616 // compute difference to current mViewport center - in world coordinates
617 imgPos = QPointF(width()*0.5f, height()*0.5f) - imgPos;
618
619 // back to screen coordinates
620 float s = (float)mWorldMatrix.m11();
621 mWorldMatrix.translate(imgPos.x()/s, imgPos.y()/s);
622 }
623
624 update();
625 }
626
tcpSetWindowRect(QRect rect)627 void DkViewPort::tcpSetWindowRect(QRect rect) {
628 this->setGeometry(rect);
629 }
630
tcpForceSynchronize()631 void DkViewPort::tcpForceSynchronize() {
632 tcpSynchronize(QTransform(), true);
633 }
634
tcpSynchronize(QTransform relativeMatrix,bool force)635 void DkViewPort::tcpSynchronize(QTransform relativeMatrix, bool force) {
636
637 if (!relativeMatrix.isIdentity()) {
638 emit sendTransformSignal(relativeMatrix, QTransform(), QPointF());
639 return;
640 }
641
642 // check if we need a synchronization
643 if ((force || qApp->keyboardModifiers() == mAltMod ||
644 DkSettingsManager::param().sync().syncActions) &&
645 (hasFocus() || mController->hasFocus())) {
646 QPointF size = QPointF(geometry().width()/2.0f, geometry().height()/2.0f);
647 size = mWorldMatrix.inverted().map(size);
648 size = mImgMatrix.inverted().map(size);
649 size = QPointF(size.x()/(float)getImageSize().width(), size.y()/(float)getImageSize().height());
650
651 emit sendTransformSignal(mWorldMatrix, mImgMatrix, size);
652 }
653 }
654
applyPlugin(DkPluginContainer * plugin,const QString & key)655 void DkViewPort::applyPlugin(DkPluginContainer* plugin, const QString& key) {
656
657 #ifdef WITH_PLUGINS
658 if (!plugin)
659 return;
660
661 DkBatchPluginInterface* bPlugin = plugin->batchPlugin();
662 if (bPlugin)
663 bPlugin->loadSettings();
664
665 QSharedPointer<DkImageContainerT> result = DkImageContainerT::fromImageContainer(plugin->plugin()->runPlugin(key, imageContainer()));
666 if (result)
667 setEditedImage(result);
668
669 plugin->setActive(false);
670 #else
671 Q_UNUSED(plugin);
672 Q_UNUSED(key);
673 #endif
674 }
675
getImage() const676 QImage DkViewPort::getImage() const {
677
678 if (imageContainer() && (!mSvg || !mSvg->isValid()) && (!mMovie || !mMovie->isValid()))
679 return imageContainer()->image();
680
681 return DkBaseViewPort::getImage();
682 }
683
resizeImage()684 void DkViewPort::resizeImage() {
685
686 if (!mResizeDialog)
687 mResizeDialog = new DkResizeDialog(this);
688
689 QSharedPointer<DkImageContainerT> imgC = imageContainer();
690 QSharedPointer<DkMetaDataT> metaData;
691
692 if (imgC) {
693 metaData = imgC->getMetaData();
694 QVector2D res = metaData->getResolution();
695 mResizeDialog->setExifDpi((float)res.x());
696 }
697
698 if (!imgC) {
699 qWarning() << "cannot resize empty image...";
700 return;
701 }
702
703 mResizeDialog->setImage(imgC->image());
704
705 if (!mResizeDialog->exec())
706 return;
707
708 if (mResizeDialog->resample()) {
709
710 QImage rImg = mResizeDialog->getResizedImage();
711
712 if (!rImg.isNull()) {
713
714 // this reloads the image -> that's not what we want!
715 if (metaData)
716 metaData->setResolution(QVector2D(mResizeDialog->getExifDpi(), mResizeDialog->getExifDpi()));
717
718 imgC->setImage(rImg, tr("Resize"));
719 setEditedImage(imgC);
720 }
721 }
722 else if (metaData) {
723 // ok, user just wants to change the resolution
724 metaData->setResolution(QVector2D(mResizeDialog->getExifDpi(), mResizeDialog->getExifDpi()));
725 qDebug() << "setting resolution to: " << mResizeDialog->getExifDpi();
726 }
727 }
728
deleteImage()729 void DkViewPort::deleteImage() {
730
731 auto imgC = imageContainer();
732
733 if (!imgC || !imgC->hasImage())
734 return;
735
736 getController()->applyPluginChanges(true);
737
738 QFileInfo fileInfo(imgC->filePath());
739 QString question;
740
741 #if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
742 question = tr("Shall I move %1 to trash?").arg(fileInfo.fileName());
743 #else
744 question = tr("Do you want to permanently delete %1?").arg(fileInfo.fileName());
745 #endif
746
747 DkMessageBox* msgBox = new DkMessageBox(
748 QMessageBox::Question,
749 tr("Delete File"),
750 question,
751 (QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel),
752 this);
753
754 msgBox->setDefaultButton(QMessageBox::Yes);
755 msgBox->setObjectName("deleteFileDialog");
756
757 int answer = msgBox->exec();
758
759 if (answer == QMessageBox::Accepted || answer == QMessageBox::Yes) {
760 stopMovie(); // movies keep file handles so stop it before we can delete files
761
762 if (!mLoader->deleteFile())
763 loadMovie(); // load the movie again, if we could not delete it
764 }
765 }
766
saveFile()767 void DkViewPort::saveFile() {
768 saveFileAs(true);
769 }
770
saveFileAs(bool silent)771 void DkViewPort::saveFileAs(bool silent) {
772
773 if (mLoader) {
774 mController->closePlugin(false);
775
776 QImage img = getImage();
777
778 if (mLoader->hasSvg() && !mLoader->isEdited()) {
779
780 DkSvgSizeDialog* sd = new DkSvgSizeDialog(img.size(), DkUtils::getMainWindow());
781 sd->resize(270, 120);
782
783 int answer = sd->exec();
784
785 if (answer == QDialog::Accepted) {
786
787 img = QImage(sd->size(), QImage::Format_ARGB32);
788 img.fill(QColor(0, 0, 0, 0));
789
790 QPainter p(&img);
791 mSvg->render(&p, QRectF(QPointF(), sd->size()));
792 }
793 }
794
795 mLoader->saveUserFileAs(img, silent);
796 }
797 }
798
saveFileWeb()799 void DkViewPort::saveFileWeb() {
800 if (mLoader) {
801 mController->closePlugin(false);
802 mLoader->saveFileWeb(getImage());
803 }
804 }
805
setAsWallpaper()806 void DkViewPort::setAsWallpaper() {
807
808 // based on code from: http://qtwiki.org/Set_windows_background_using_QT
809 auto imgC = imageContainer();
810
811 if (!imgC || !imgC->hasImage()) {
812 qWarning() << "cannot create wallpaper because there is no image loaded...";
813 }
814
815 QImage img = imgC->image();
816 QString tmpPath = mLoader->saveTempFile(img, "wallpaper", ".jpg", true, false);
817
818 // is there a more elegant way to see if saveTempFile returned an empty path
819 if (tmpPath.isEmpty()) {
820 QMessageBox::critical(this, tr("Error"), tr("Sorry, I could not create a wallpaper..."));
821 return;
822 }
823
824 #ifdef Q_OS_WIN
825
826 //Read current windows background image path
827 QSettings appSettings("HKEY_CURRENT_USER\\Control Panel\\Desktop", QSettings::NativeFormat);
828 appSettings.setValue("Wallpaper", tmpPath);
829
830 QByteArray ba = tmpPath.toLatin1();
831 SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, (void*)ba.data(), SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
832 #endif
833 // TODO: add functionality for unix based systems
834 }
835
applyManipulator()836 void DkViewPort::applyManipulator() {
837
838 QAction* action = dynamic_cast<QAction*>(QObject::sender());
839
840 if (!action) {
841 qWarning() << "applyManipulator is not called from its action!";
842 return;
843 }
844
845 DkActionManager& am = DkActionManager::instance();
846 QSharedPointer<DkBaseManipulator> mpl = am.manipulatorManager().manipulator(action);
847
848 if (!mpl) {
849 qWarning() << "could not find manipulator for:" << action;
850 return;
851 }
852
853 // try to cast up
854 QSharedPointer<DkBaseManipulatorExt> mplExt = qSharedPointerDynamicCast<DkBaseManipulatorExt>(mpl);
855
856 // mark dirty
857 if (mManipulatorWatcher.isRunning() && mplExt && mActiveManipulator == mpl) {
858 mplExt->setDirty(true);
859 return;
860 }
861
862 if (mManipulatorWatcher.isRunning()) {
863 mController->setInfo(tr("Busy"));
864 return;
865 }
866
867 // show the dock (in case it's not shown yet)
868 if (mplExt) {
869 am.action(DkActionManager::menu_edit_image)->setChecked(true);
870 }
871
872 // undo last if it is an extended manipulator
873 QImage img;
874 if (mplExt && imageContainer()) {
875
876 auto l = imageContainer()->getLoader();
877 l->setMinHistorySize(3); // increase the min history size to 3 for correctly popping back
878 if (!l->history()->isEmpty() && l->lastEdit().editName() == mplExt->name()) {
879 imageContainer()->undo();
880 }
881
882 img = imageContainer()->image();
883 }
884 else
885 img = getImage();
886
887 mManipulatorWatcher.setFuture(
888 QtConcurrent::run(
889 mpl.data(),
890 &nmc::DkBaseManipulator::apply,
891 img));
892
893 mActiveManipulator = mpl;
894
895 emit showProgress(true, 500);
896 }
897
manipulatorApplied()898 void DkViewPort::manipulatorApplied() {
899
900 DkGlobalProgress::instance().stop();
901
902 if (mManipulatorWatcher.isCanceled() || !mActiveManipulator) {
903 qDebug() << "manipulator applied - but it's canceled";
904 return;
905 }
906
907 // trigger again if it's dirty
908 QSharedPointer<DkBaseManipulatorExt> mplExt = qSharedPointerDynamicCast<DkBaseManipulatorExt>(mActiveManipulator);
909
910 // set the edited image
911 QImage img = mManipulatorWatcher.result();
912
913 if (!img.isNull())
914 setEditedImage(img, mActiveManipulator->name());
915 else
916 mController->setInfo(mActiveManipulator->errorMessage());
917
918 if (mplExt && mplExt->isDirty()) {
919 mplExt->setDirty(false);
920 mplExt->action()->trigger();
921 qDebug() << "triggering manipulator - it's dirty";
922 }
923
924 emit showProgress(false);
925 }
926
paintEvent(QPaintEvent * event)927 void DkViewPort::paintEvent(QPaintEvent* event) {
928
929 QPainter painter(viewport());
930
931 if (!mImgStorage.isEmpty()) {
932
933 // usually the QGraphicsView should do this - but we have seen issues(e.g. #706)
934 painter.setPen(Qt::NoPen);
935 painter.setBrush(backgroundBrush());
936 painter.drawRect(QRect(QPoint(), size()));
937
938 painter.setWorldTransform(mWorldMatrix);
939
940 // interpolate between 100% and max interpolate level
941 if (!mForceFastRendering && // force?
942 mImgMatrix.m11()*mWorldMatrix.m11() - DBL_EPSILON > 1.0 && // @100% ?
943 mImgMatrix.m11()*mWorldMatrix.m11() <= DkSettingsManager::param().display().interpolateZoomLevel / 100.0) { // > max zoom level
944 painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing);
945 }
946
947 if (DkSettingsManager::param().display().transition == DkSettings::trans_swipe &&
948 !mAnimationBuffer.isNull()) {
949
950 double dx = mNextSwipe ? width()*(mAnimationValue) : -width()*(mAnimationValue);
951
952 QTransform swipeTransform;
953 swipeTransform.translate(dx, 0);
954 painter.setTransform(swipeTransform);
955 }
956
957 // TODO: if fading is active we interpolate with background instead of the other image
958 double opacity = (DkSettingsManager::param().display().transition == DkSettings::trans_fade) ? 1.0 - mAnimationValue : 1.0;
959 draw(painter, opacity);
960
961 if (!mAnimationBuffer.isNull() && mAnimationValue > 0) {
962
963 float oldOp = (float)painter.opacity();
964
965 // fade transition
966 if (DkSettingsManager::param().display().transition == DkSettings::trans_fade) {
967 painter.setOpacity(mAnimationValue);
968 }
969 else if (DkSettingsManager::param().display().transition == DkSettings::trans_swipe) {
970
971 double dx = mNextSwipe ? -width()*(1.0-mAnimationValue) : width()*(1.0-mAnimationValue);
972 QTransform swipeTransform;
973 swipeTransform.translate(dx, 0);
974 painter.setTransform(swipeTransform);
975 }
976
977 qDebug() << "render hints:" << painter.renderHints();
978 painter.drawImage(mFadeImgViewRect, mAnimationBuffer, mAnimationBuffer.rect());
979 painter.setOpacity(oldOp);
980 }
981
982 // now disable world matrix for overlay display
983 painter.setWorldMatrixEnabled(false);
984 }
985 else
986 drawBackground(painter);
987
988 // draw the cropping rect
989 if (!mCropRect.isEmpty() && DkSettingsManager::param().display().showCrop && imageContainer()) {
990
991 // create path
992 QPainterPath path;
993 path.addRect(getImageViewRect().toRect());
994
995 DkRotatingRect r = mCropRect;
996 QPolygonF polyF;
997 polyF = r.getClosedPoly();
998 polyF = mImgMatrix.map(polyF);
999 polyF = mWorldMatrix.map(polyF);
1000 path.addPolygon(polyF.toPolygon());
1001
1002 painter.setPen(Qt::NoPen);
1003 painter.setBrush(QColor(0,0,0,100));
1004 painter.drawPath(path);
1005 }
1006
1007 painter.end();
1008
1009 // propagate
1010 QGraphicsView::paintEvent(event);
1011 }
1012
leaveEvent(QEvent * event)1013 void DkViewPort::leaveEvent(QEvent* event) {
1014
1015 // hide navigation buttons if the mouse leaves the viewport
1016 mNavigationWidget->hide();
1017 DkBaseViewPort::leaveEvent(event);
1018 }
1019
1020 // drawing functions --------------------------------------------------------------------
drawBackground(QPainter & painter)1021 void DkViewPort::drawBackground(QPainter & painter) {
1022
1023 // fit to mViewport
1024 QSize s = mImgBg.size();
1025 if (s.width() > (float)(size().width()*0.5))
1026 s = s*((size().width()*0.5)/s.width());
1027
1028 if (s.height() > size().height()*0.6)
1029 s = s*((size().height()*0.6)/s.height());
1030
1031 QRect bgRect(QPoint(), s);
1032 bgRect.moveBottomRight(QPoint(width()-20, height()-20));
1033
1034 painter.drawImage(bgRect, mImgBg, QRect(QPoint(), mImgBg.size()));
1035 }
1036
loadMovie()1037 void DkViewPort::loadMovie() {
1038
1039 if (!mLoader)
1040 return;
1041
1042 if (mMovie)
1043 mMovie->stop();
1044
1045 // check if it truely a movie (we need this for we don't know if webp is actually animated)
1046 QSharedPointer<QMovie> m(new QMovie(mLoader->filePath()));
1047 if (m->frameCount() == 1)
1048 return;
1049
1050 mMovie = m;
1051
1052 connect(mMovie.data(), SIGNAL(frameChanged(int)), this, SLOT(update()));
1053 mMovie->start();
1054
1055 emit movieLoadedSignal(true);
1056 }
1057
loadSvg()1058 void DkViewPort::loadSvg() {
1059
1060 if (!mLoader)
1061 return;
1062
1063 auto cc = mLoader->getCurrentImage();
1064 if (cc) {
1065 mSvg = QSharedPointer<QSvgRenderer>(
1066 new QSvgRenderer(
1067 *cc->getFileBuffer()
1068 ));
1069 }
1070 else {
1071 mSvg = QSharedPointer<QSvgRenderer>(
1072 new QSvgRenderer(
1073 mLoader->filePath()
1074 ));
1075 }
1076
1077 connect(mSvg.data(), SIGNAL(repaintNeeded()), this, SLOT(update()));
1078
1079 }
1080
pauseMovie(bool pause)1081 void DkViewPort::pauseMovie(bool pause) {
1082
1083 if (!mMovie)
1084 return;
1085
1086 mMovie->setPaused(pause);
1087 }
1088
nextMovieFrame()1089 void DkViewPort::nextMovieFrame() {
1090
1091 if (!mMovie)
1092 return;
1093
1094 mMovie->jumpToNextFrame();
1095 update();
1096 }
1097
previousMovieFrame()1098 void DkViewPort::previousMovieFrame() {
1099
1100 if (!mMovie)
1101 return;
1102
1103
1104 int fn = mMovie->currentFrameNumber()-1;
1105 if (fn == -1)
1106 fn = mMovie->frameCount()-1;
1107 //qDebug() << "retrieving frame: " << fn;
1108
1109 while(mMovie->currentFrameNumber() != fn)
1110 mMovie->jumpToNextFrame();
1111
1112 //// the subsequent thing is not working if the movie is paused
1113 //bool success = movie->jumpToFrame(movie->currentFrameNumber()-1);
1114 update();
1115 }
1116
stopMovie()1117 void DkViewPort::stopMovie() {
1118
1119 if (!mMovie)
1120 return;
1121
1122 mMovie->stop();
1123 mMovie = QSharedPointer<QMovie>();
1124 }
1125
drawPolygon(QPainter & painter,const QPolygon & polygon)1126 void DkViewPort::drawPolygon(QPainter & painter, const QPolygon & polygon) {
1127
1128 QPoint lastPoint;
1129
1130 for (const QPoint& p : polygon) {
1131
1132 if (!lastPoint.isNull())
1133 painter.drawLine(p, lastPoint);
1134
1135 lastPoint = p;
1136 }
1137
1138 }
1139
1140 // event listeners --------------------------------------------------------------------
resizeEvent(QResizeEvent * event)1141 void DkViewPort::resizeEvent(QResizeEvent *event) {
1142
1143 mViewportRect = QRect(0, 0, width(), height());
1144
1145 // >DIR: diem - bug if zoom factor is large and window becomes small
1146 updateImageMatrix();
1147 centerImage();
1148 changeCursor();
1149
1150 mController->getOverview()->setViewPortRect(geometry());
1151 mController->resize(width(), height());
1152
1153 return QGraphicsView::resizeEvent(event);
1154 }
1155
1156 // mouse events --------------------------------------------------------------------
event(QEvent * event)1157 bool DkViewPort::event(QEvent *event) {
1158
1159 // ok obviously QGraphicsView eats all mouse events -> so we simply redirect these to QWidget in order to get them delivered here
1160 if (event->type() == QEvent::MouseButtonPress ||
1161 event->type() == QEvent::MouseButtonDblClick ||
1162 event->type() == QEvent::MouseButtonRelease ||
1163 event->type() == QEvent::MouseMove ||
1164 event->type() == QEvent::Wheel ||
1165 event->type() == QEvent::KeyPress ||
1166 event->type() == QEvent::KeyRelease ||
1167 event->type() == QEvent::DragEnter ||
1168
1169 event->type() == QEvent::Drop) {
1170
1171 //qDebug() << "redirecting event...";
1172 // mouse events that double are now fixed, since the mViewport is now overlayed by the mController
1173 return QWidget::event(event);
1174 }
1175 else {
1176 //qDebug() << "not redirecting - type: " << event->type();
1177 return DkBaseViewPort::event(event);
1178 }
1179 }
1180
dragLeaveEvent(QDragLeaveEvent * event)1181 void DkViewPort::dragLeaveEvent(QDragLeaveEvent *event) {
1182
1183 event->accept();
1184 }
1185
mousePressEvent(QMouseEvent * event)1186 void DkViewPort::mousePressEvent(QMouseEvent *event) {
1187
1188 // if zoom on wheel, the additional keys should be used for switching files
1189 if (DkSettingsManager::param().global().zoomOnWheel) {
1190 if(event->buttons() == Qt::XButton1)
1191 loadPrevFileFast();
1192 else if(event->buttons() == Qt::XButton2)
1193 loadNextFileFast();
1194 }
1195 else if(event->buttons() == Qt::XButton1 || event->buttons() == Qt::XButton2) {
1196 repeatZoom();
1197 mRepeatZoomTimer->start();
1198 }
1199
1200 // ok, start panning
1201 if (mWorldMatrix.m11() > 1 && !imageInside() && event->buttons() == Qt::LeftButton) {
1202 setCursor(Qt::ClosedHandCursor);
1203 mPosGrab = event->pos();
1204 }
1205
1206 // keep in mind if the gesture was started in the mViewport
1207 // this fixes issues if some HUD widgets or child widgets
1208 // do not implement mouse events correctly
1209 if (event->buttons() == Qt::LeftButton)
1210 mGestureStarted = true;
1211 else
1212 mGestureStarted = false;
1213
1214 // should be sent to QWidget?!
1215 DkBaseViewPort::mousePressEvent(event);
1216 }
1217
mouseReleaseEvent(QMouseEvent * event)1218 void DkViewPort::mouseReleaseEvent(QMouseEvent *event) {
1219
1220 mRepeatZoomTimer->stop();
1221
1222 int sa = swipeRecognition(event->pos(), mPosGrab.toPoint());
1223 QPoint pos = mapToImage(event->pos());
1224
1225 if (imageInside() && mGestureStarted) {
1226 swipeAction(sa);
1227 }
1228
1229 // needed for scientific projects...
1230 if (pos.x() != -1 && pos.y() != -1)
1231 emit mouseClickSignal(event, pos);
1232
1233 mGestureStarted = false;
1234
1235 DkBaseViewPort::mouseReleaseEvent(event);
1236 }
1237
mouseMoveEvent(QMouseEvent * event)1238 void DkViewPort::mouseMoveEvent(QMouseEvent *event) {
1239
1240 if (DkSettingsManager::param().display().showNavigation &&
1241 event->modifiers() == Qt::NoModifier &&
1242 event->buttons() == Qt::NoButton) {
1243
1244 int left = qMin(100, qRound(0.1 * width()));
1245 int right = qMax(width()-100, qRound(0.9 * width()));
1246
1247 if (event->pos().x() < left)
1248 mNavigationWidget->showPrevious();
1249 else if (event->pos().x() > right)
1250 mNavigationWidget->showNext();
1251 else if (mNavigationWidget->isVisible())
1252 mNavigationWidget->hide();
1253 }
1254
1255 //qDebug() << "mouse move (DkViewPort)";
1256 //changeCursor();
1257 mCurrentPixelPos = event->pos();
1258
1259 if (DkStatusBarManager::instance().statusbar()->isVisible())
1260 getPixelInfo(event->pos());
1261
1262 if (mWorldMatrix.m11() > 1 && event->buttons() == Qt::LeftButton) {
1263
1264 QPointF cPos = event->pos();
1265 QPointF dxy = (cPos - mPosGrab);
1266 mPosGrab = cPos;
1267 moveView(dxy/mWorldMatrix.m11());
1268
1269 // with shift also a hotkey for fast switching...
1270 if ((DkSettingsManager::param().sync().syncAbsoluteTransform &&
1271 event->modifiers() == (mAltMod | Qt::ShiftModifier)) ||
1272 (!DkSettingsManager::param().sync().syncAbsoluteTransform &&
1273 event->modifiers() == (mAltMod))) {
1274
1275 if (dxy.x() != 0 || dxy.y() != 0) {
1276 QTransform relTransform;
1277 relTransform.translate(dxy.x(), dxy.y());
1278 tcpSynchronize(relTransform);
1279 }
1280 }
1281 // absolute transformation
1282 else
1283 tcpSynchronize();
1284 }
1285
1286 int dist = QPoint(event->pos()-mPosGrab.toPoint()).manhattanLength();
1287
1288 // drag & drop action
1289 if (event->buttons() == Qt::LeftButton
1290 && dist > QApplication::startDragDistance()
1291 && imageInside()
1292 && !getImage().isNull()
1293 && mLoader
1294 && !QApplication::widgetAt(event->globalPos())) { // is NULL if the mouse leaves the window
1295
1296 QMimeData* mimeData = createMime();
1297
1298 QPixmap pm;
1299 if (!getImage().isNull())
1300 pm = QPixmap::fromImage(mImgStorage.image().scaledToHeight(73, Qt::SmoothTransformation));
1301 if (pm.width() > 130)
1302 pm = pm.scaledToWidth(100, Qt::SmoothTransformation);
1303
1304 QDrag* drag = new QDrag(this);
1305 drag->setMimeData(mimeData);
1306 drag->setPixmap(pm);
1307 drag->exec(Qt::CopyAction);
1308 }
1309
1310 // send to parent
1311 DkBaseViewPort::mouseMoveEvent(event);
1312 }
1313
wheelEvent(QWheelEvent * event)1314 void DkViewPort::wheelEvent(QWheelEvent *event) {
1315
1316 if ((!DkSettingsManager::param().global().zoomOnWheel && event->modifiers() != mCtrlMod) ||
1317 (DkSettingsManager::param().global().zoomOnWheel && (event->modifiers() & mCtrlMod ||
1318 (DkSettingsManager::param().global().horZoomSkips && event->orientation() == Qt::Horizontal && !(event->modifiers() & mAltMod))))) {
1319
1320 if (event->delta() < 0)
1321 loadNextFileFast();
1322 else
1323 loadPrevFileFast();
1324 }
1325 else
1326 DkBaseViewPort::wheelEvent(event);
1327
1328 tcpSynchronize();
1329
1330 }
1331
swipeRecognition(QPoint start,QPoint end)1332 int DkViewPort::swipeRecognition(QPoint start, QPoint end) {
1333
1334 DkVector vec((float)(start.x()-end.x()), (float)(start.y()-end.y()));
1335
1336 if (fabs(vec.norm()) < 100)
1337 return no_swipe;
1338
1339 double angle = DkMath::normAngleRad(vec.angle(DkVector(0,1)), 0.0, CV_PI);
1340 bool horizontal = false;
1341
1342 if (angle > CV_PI*0.3 && angle < CV_PI*0.6)
1343 horizontal = true;
1344 else if (angle < 0.2*CV_PI || angle > 0.8*CV_PI)
1345 horizontal = false;
1346 else
1347 return no_swipe; // angles ~45� are not accepted
1348
1349 QPoint startPos = QWidget::mapFromGlobal(end);
1350 qDebug() << "vec: " << vec.x << ", " << vec.y;
1351
1352 if (horizontal) {
1353
1354 if (vec.x < 0)
1355 return next_image;
1356 else
1357 return prev_image;
1358
1359 }
1360 // upper part of the canvas is thumbs
1361 else if (!horizontal && startPos.y() < height()*0.5f) {
1362
1363 // downward gesture is opening
1364 if (vec.y > 0)
1365 return open_thumbs;
1366 else
1367 return close_thumbs;
1368 }
1369 // lower part of the canvas is thumbs
1370 else if (!horizontal && startPos.y() > height()*0.5f) {
1371
1372 // upward gesture is opening
1373 if (vec.y < 0)
1374 return open_metadata;
1375 else
1376 return close_metadata;
1377 }
1378
1379 return no_swipe;
1380 }
1381
swipeAction(int swipeGesture)1382 void DkViewPort::swipeAction(int swipeGesture) {
1383
1384 assert(mController);
1385
1386 switch (swipeGesture) {
1387 case next_image:
1388 loadNextFileFast();
1389 break;
1390 case prev_image:
1391 loadPrevFileFast();
1392 break;
1393 case open_thumbs:
1394 mController->showPreview(true);
1395 break;
1396 case close_thumbs:
1397 mController->showPreview(false);
1398 break;
1399 case open_metadata:
1400 mController->showMetaData(true);
1401 break;
1402 case close_metadata:
1403 mController->showMetaData(false);
1404 break;
1405 default:
1406 break;
1407 }
1408 }
1409
setFullScreen(bool fullScreen)1410 void DkViewPort::setFullScreen(bool fullScreen) {
1411
1412 assert(mController);
1413 mController->setFullScreen(fullScreen);
1414 toggleLena(fullScreen);
1415
1416 if (fullScreen)
1417 QWidget::setWindowState(windowState() ^ Qt::WindowFullScreen);
1418 else
1419 QWidget::setWindowState(windowState() & ~Qt::WindowFullScreen);
1420
1421 if (fullScreen)
1422 mHideCursorTimer->start();
1423 else
1424 unsetCursor();
1425 }
1426
mapToImage(const QPoint & windowPos) const1427 QPoint DkViewPort::mapToImage(const QPoint& windowPos) const {
1428
1429 QPointF imgPos = mWorldMatrix.inverted().map(QPointF(windowPos));
1430 imgPos = mImgMatrix.inverted().map(imgPos);
1431
1432 QPoint xy(qFloor(imgPos.x()), qFloor(imgPos.y()));
1433
1434 if (xy.x() < 0 || xy.y() < 0 || xy.x() >= getImageSize().width() || xy.y() >= getImageSize().height())
1435 return QPoint(-1,-1);
1436
1437 return xy;
1438 }
1439
getPixelInfo(const QPoint & pos)1440 void DkViewPort::getPixelInfo(const QPoint& pos) {
1441
1442 if (mImgStorage.isEmpty())
1443 return;
1444
1445 QPoint xy = mapToImage(pos);
1446
1447 if (xy.x() == -1 || xy.y() == -1)
1448 return;
1449
1450 QColor col = getImage().pixel(xy);
1451
1452 QString msg = "x: " + QString::number(xy.x()) + " y: " + QString::number(xy.y()) +
1453 " | r: " + QString::number(col.red()) + " g: " + QString::number(col.green()) + " b: " + QString::number(col.blue());
1454
1455 if (mImgStorage.image().hasAlphaChannel())
1456 msg += " a: " + QString::number(col.alpha());
1457
1458 msg += " | " + col.name().toUpper();
1459
1460 DkStatusBarManager::instance().setMessage(msg, DkStatusBar::status_pixel_info);
1461 }
1462
getCurrentPixelHexValue()1463 QString DkViewPort::getCurrentPixelHexValue() {
1464
1465 if (mImgStorage.isEmpty() || mCurrentPixelPos.isNull())
1466 return QString();
1467
1468 QPointF imgPos = mWorldMatrix.inverted().map(QPointF(mCurrentPixelPos));
1469 imgPos = mImgMatrix.inverted().map(imgPos);
1470
1471 QPoint xy(qFloor(imgPos.x()), qFloor(imgPos.y()));
1472
1473 if (xy.x() < 0 || xy.y() < 0 || xy.x() >= getImageSize().width() || xy.y() >= getImageSize().height())
1474 return QString();
1475
1476 QColor col = getImage().pixel(xy);
1477
1478 return col.name().toUpper().remove(0,1);
1479 }
1480
1481 // Copy & Paste --------------------------------------------------------
copyPixelColorValue()1482 void DkViewPort::copyPixelColorValue() {
1483
1484 if (getImage().isNull())
1485 return;
1486
1487 QMimeData* mimeData = new QMimeData;
1488
1489 if (!getImage().isNull())
1490 mimeData->setText(getCurrentPixelHexValue());
1491
1492 QClipboard* clipboard = QApplication::clipboard();
1493 clipboard->setMimeData(mimeData);
1494 }
1495
copyImage()1496 void DkViewPort::copyImage() {
1497
1498 QMimeData* mimeData = createMime();
1499
1500 QClipboard* clipboard = QApplication::clipboard();
1501 clipboard->setMimeData(mimeData);
1502 }
1503
createMime() const1504 QMimeData * DkViewPort::createMime() const {
1505
1506 if (getImage().isNull() || !mLoader)
1507 return 0;
1508
1509 // NOTE: if we do the file:/// thingy, we will get into problems with mounted drives (e.g. //hermes...)
1510 QUrl fileUrl = QUrl::fromLocalFile(mLoader->filePath());
1511
1512 QList<QUrl> urls;
1513 urls.append(fileUrl);
1514
1515 QMimeData* mimeData = new QMimeData;
1516
1517 if (QFileInfo(mLoader->filePath()).exists() && !mLoader->isEdited()) {
1518 mimeData->setUrls(urls);
1519 mimeData->setText(fileUrl.toLocalFile());
1520 }
1521 else if (!getImage().isNull())
1522 mimeData->setImageData(getImage());
1523
1524 return mimeData;
1525 }
1526
copyImageBuffer()1527 void DkViewPort::copyImageBuffer() {
1528
1529 if (getImage().isNull())
1530 return;
1531
1532 QMimeData* mimeData = new QMimeData;
1533
1534 if (!getImage().isNull())
1535 mimeData->setImageData(getImage());
1536
1537 QClipboard* clipboard = QApplication::clipboard();
1538 clipboard->setMimeData(mimeData);
1539 }
1540
animateFade()1541 void DkViewPort::animateFade() {
1542
1543 mAnimationValue = 1.0f-(float)(mAnimationTime.elapsed()/1000.0)/DkSettingsManager::param().display().animationDuration;
1544
1545 // slow in - slow out
1546 double speed = mAnimationValue > 0.5 ? fabs(1.0 - mAnimationValue) : fabs(mAnimationValue);
1547 speed *= .05;
1548
1549 mAnimationValue += (float)speed;
1550
1551 if (mAnimationValue <= 0) {
1552 mAnimationBuffer = QImage();
1553 mAnimationTimer->stop();
1554 mAnimationValue = 0;
1555 }
1556
1557 update();
1558 }
1559
togglePattern(bool show)1560 void DkViewPort::togglePattern(bool show) {
1561
1562 emit infoSignal((show) ? tr("Transparency Pattern Enabled") : tr("Transparency Pattern Disabled"));
1563 DkBaseViewPort::togglePattern(show);
1564 }
1565
1566 // edit image --------------------------------------------------------------------
rotateCW()1567 void DkViewPort::rotateCW() {
1568
1569 if (!mController->applyPluginChanges(true))
1570 return;
1571
1572 if (mLoader)
1573 mLoader->rotateImage(90);
1574 }
1575
rotateCCW()1576 void DkViewPort::rotateCCW() {
1577
1578 if (!mController->applyPluginChanges(true))
1579 return;
1580
1581 if (mLoader)
1582 mLoader->rotateImage(-90);
1583
1584 }
1585
rotate180()1586 void DkViewPort::rotate180() {
1587
1588 if (!mController->applyPluginChanges(true))
1589 return;
1590
1591 if (mLoader)
1592 mLoader->rotateImage(180);
1593
1594 }
1595
1596 // file handling --------------------------------------------------------------------
loadLena()1597 void DkViewPort::loadLena() {
1598
1599 bool ok;
1600 QString text = QInputDialog::getText(this, tr("Lenna"), tr("A remarkable woman"), QLineEdit::Normal, 0, &ok);
1601
1602 // pass phrase
1603 if (ok && !text.isEmpty() && text == "lenna") {
1604 mTestLoaded = true;
1605 toggleLena(DkUtils::getMainWindow()->isFullScreen());
1606 }
1607 else if (!ok) {
1608 QMessageBox warningDialog(DkUtils::getMainWindow());
1609 warningDialog.setIcon(QMessageBox::Warning);
1610 warningDialog.setText(tr("you cannot cancel this"));
1611 warningDialog.exec();
1612 loadLena();
1613 }
1614 else {
1615 QApplication::beep();
1616
1617 if (text.isEmpty())
1618 mController->setInfo(tr("did you understand the brainteaser?"));
1619 else
1620 mController->setInfo(tr("%1 is wrong...").arg(text));
1621 }
1622 }
1623
toggleLena(bool fullscreen)1624 void DkViewPort::toggleLena(bool fullscreen) {
1625
1626 if (!mTestLoaded)
1627 return;
1628
1629 if (mLoader) {
1630 if (fullscreen)
1631 mLoader->downloadFile(QUrl("http://www.lenna.org/lena_std.tif"));
1632 else
1633 mLoader->load(":/nomacs/img/we.jpg");
1634 }
1635 }
1636
settingsChanged()1637 void DkViewPort::settingsChanged() {
1638
1639 reloadFile();
1640
1641 mAltMod = DkSettingsManager::param().global().altMod;
1642 mCtrlMod = DkSettingsManager::param().global().ctrlMod;
1643
1644 mController->settingsChanged();
1645 }
1646
setEditedImage(const QImage & newImg,const QString & editName)1647 void DkViewPort::setEditedImage(const QImage& newImg, const QString& editName) {
1648
1649 if (!mController->applyPluginChanges(true)) // user wants to first apply the plugin
1650 return;
1651
1652 if (newImg.isNull()) {
1653 emit infoSignal(tr("Attempted to set NULL image")); // not sure if users understand that
1654 return;
1655 }
1656
1657 if (mManipulatorWatcher.isRunning())
1658 mManipulatorWatcher.cancel();
1659
1660 QSharedPointer<DkImageContainerT> imgC = mLoader->getCurrentImage();
1661
1662 if (!imgC)
1663 imgC = QSharedPointer<DkImageContainerT>(new DkImageContainerT(""));
1664
1665 if (!imgC)
1666 imgC = QSharedPointer<DkImageContainerT>();
1667 imgC->setImage(newImg, editName);
1668 unloadImage(false);
1669 mLoader->setImage(imgC);
1670 qDebug() << "mLoader gets this size: " << newImg.size();
1671 }
1672
setEditedImage(QSharedPointer<DkImageContainerT> img)1673 void DkViewPort::setEditedImage(QSharedPointer<DkImageContainerT> img) {
1674
1675 if (!img) {
1676 emit infoSignal(tr("Attempted to set NULL image")); // not sure if users understand that
1677 return;
1678 }
1679
1680 unloadImage(false);
1681 mLoader->setImage(img);
1682 }
1683
unloadImage(bool fileChange)1684 bool DkViewPort::unloadImage(bool fileChange) {
1685
1686 if (DkSettingsManager::param().display().animationDuration > 0 &&
1687 (mController->getPlayer()->isPlaying() ||
1688 DkUtils::getMainWindow()->isFullScreen() ||
1689 DkSettingsManager::param().display().alwaysAnimate)) {
1690 mAnimationBuffer = mImgStorage.image((float)(mImgMatrix.m11()*mWorldMatrix.m11()));
1691 mFadeImgViewRect = mImgViewRect;
1692 mFadeImgRect = mImgRect;
1693 mAnimationValue = 1.0f;
1694 }
1695
1696 int success = true;
1697
1698 if (!mController->applyPluginChanges(true)) // user wants to apply changes first
1699 return false;
1700
1701 if (fileChange)
1702 success = mLoader->unloadFile(); // returns false if the user cancels
1703
1704 // notify controller
1705 mController->updateImage(imageContainer());
1706
1707 if (mMovie && success) {
1708 mMovie->stop();
1709 mMovie = QSharedPointer<QMovie>();
1710 }
1711
1712 if (mSvg && success)
1713 mSvg = QSharedPointer<QSvgRenderer>();
1714
1715 return success != 0;
1716 }
1717
deactivate()1718 void DkViewPort::deactivate() {
1719
1720 setImage(QImage());
1721 }
1722
loadFile(const QString & filePath)1723 void DkViewPort::loadFile(const QString& filePath) {
1724
1725 if (!unloadImage())
1726 return;
1727
1728 mTestLoaded = false;
1729
1730 if (mLoader && !filePath.isEmpty() && QFileInfo(filePath).isDir()) {
1731 mLoader->setDir(filePath);
1732 }
1733 else if (mLoader)
1734 mLoader->load(filePath);
1735
1736 // diem: I removed this line for a) we don't support remote displays anymore and be: https://github.com/nomacs/nomacs/issues/219
1737 //qDebug() << "sync mode: " << (DkSettingsManager::param().sync().syncMode == DkSettings::sync_mode_remote_display);
1738 //if ((qApp->keyboardModifiers() == mAltMod || DkSettingsManager::param().sync().syncMode == DkSettings::sync_mode_remote_display) &&
1739 // (hasFocus() || mController->hasFocus()) &&
1740 // mLoader->hasFile())
1741 // tcpLoadFile(0, filePath);
1742 }
1743
reloadFile()1744 void DkViewPort::reloadFile() {
1745
1746 if (mLoader) {
1747
1748 if (unloadImage())
1749 mLoader->reloadImage();
1750 }
1751 }
1752
loadFile(int skipIdx)1753 void DkViewPort::loadFile(int skipIdx) {
1754
1755 if (!unloadImage())
1756 return;
1757
1758 if (mLoader && !mTestLoaded)
1759 mLoader->changeFile(skipIdx);
1760
1761 // alt mod
1762 if ((qApp->keyboardModifiers() == mAltMod || DkSettingsManager::param().sync().syncActions) && (hasFocus() || mController->hasFocus())) {
1763 emit sendNewFileSignal((qint16)skipIdx);
1764 qDebug() << "emitting load next";
1765 }
1766 }
1767
loadPrevFileFast()1768 void DkViewPort::loadPrevFileFast() {
1769
1770 loadFileFast(-1);
1771 }
1772
loadNextFileFast()1773 void DkViewPort::loadNextFileFast() {
1774
1775 loadFileFast(1);
1776 }
1777
1778
loadFileFast(int skipIdx)1779 void DkViewPort::loadFileFast(int skipIdx) {
1780
1781 if (!unloadImage())
1782 return;
1783
1784 mNextSwipe = skipIdx > 0;
1785
1786 QApplication::sendPostedEvents();
1787
1788 int sIdx = skipIdx;
1789 QSharedPointer<DkImageContainerT> lastImg;
1790
1791 for (int idx = 0; idx < mLoader->getImages().size(); idx++) {
1792
1793 QSharedPointer<DkImageContainerT> imgC = mLoader->getSkippedImage(sIdx);
1794
1795 if (!imgC)
1796 break;
1797
1798 mLoader->setCurrentImage(imgC);
1799
1800 if (imgC && imgC->getLoadState() != DkImageContainer::exists_not) {
1801 mLoader->load(imgC);
1802 break;
1803 }
1804 else if (lastImg == imgC) {
1805 sIdx += skipIdx; // get me out of endless loops (self referencing shortcuts)
1806 }
1807 else {
1808 qDebug() << "image does not exist - skipping";
1809 }
1810
1811 lastImg = imgC;
1812 }
1813
1814 if ((qApp->keyboardModifiers() == mAltMod || DkSettingsManager::param().sync().syncActions) && (hasFocus() || mController->hasFocus())) {
1815 emit sendNewFileSignal((qint16)skipIdx);
1816 QCoreApplication::sendPostedEvents();
1817 }
1818 }
1819
loadFirst()1820 void DkViewPort::loadFirst() {
1821
1822 if (!unloadImage())
1823 return;
1824
1825 if (mLoader && !mTestLoaded)
1826 mLoader->firstFile();
1827
1828 if ((qApp->keyboardModifiers() == mAltMod || DkSettingsManager::param().sync().syncActions) && (hasFocus() || mController->hasFocus()))
1829 emit sendNewFileSignal(SHRT_MIN);
1830 }
1831
loadLast()1832 void DkViewPort::loadLast() {
1833
1834 if (!unloadImage())
1835 return;
1836
1837 if (mLoader && !mTestLoaded)
1838 mLoader->lastFile();
1839
1840 if ((qApp->keyboardModifiers() == mAltMod || DkSettingsManager::param().sync().syncActions) && (hasFocus() || mController->hasFocus()))
1841 emit sendNewFileSignal(SHRT_MAX);
1842
1843 }
1844
loadSkipPrev10()1845 void DkViewPort::loadSkipPrev10() {
1846
1847 loadFileFast(-DkSettingsManager::param().global().skipImgs);
1848 //unloadImage();
1849
1850 //if (mLoader && !testLoaded)
1851 // mLoader->changeFile(-DkSettingsManager::param().global().skipImgs, (parent->isFullScreen() && DkSettingsManager::param().slideShow().silentFullscreen));
1852
1853 if (qApp->keyboardModifiers() == mAltMod && (hasFocus() || mController->hasFocus()))
1854 emit sendNewFileSignal((qint16)-DkSettingsManager::param().global().skipImgs);
1855 }
1856
loadSkipNext10()1857 void DkViewPort::loadSkipNext10() {
1858
1859 loadFileFast(DkSettingsManager::param().global().skipImgs);
1860 //unloadImage();
1861
1862 //if (mLoader && !testLoaded)
1863 // mLoader->changeFile(DkSettingsManager::param().global().skipImgs, (parent->isFullScreen() && DkSettingsManager::param().slideShow().silentFullscreen));
1864
1865 if (qApp->keyboardModifiers() == mAltMod && (hasFocus() || mController->hasFocus()))
1866 emit sendNewFileSignal((qint16)DkSettingsManager::param().global().skipImgs);
1867 }
1868
tcpLoadFile(qint16 idx,QString filename)1869 void DkViewPort::tcpLoadFile(qint16 idx, QString filename) {
1870
1871 qDebug() << "I got a file request??";
1872
1873 // some hack: set the mode to default in order to prevent loops (if both are auto connected)
1874 // should be mostly harmless
1875 //int oldMode = DkSettingsManager::param().sync().syncMode;
1876 //DkSettingsManager::param().sync().syncMode = DkSettings::sync_mode_receiveing_command;
1877
1878 if (filename.isEmpty()) {
1879
1880 // change the file idx according to my brother
1881 switch (idx) {
1882 case SHRT_MIN:
1883 loadFirst();
1884 break;
1885 case SHRT_MAX:
1886 loadLast();
1887 break;
1888 //case 1:
1889 // loadNextFileFast();
1890 // break;
1891 //case -1:
1892 // loadPrevFileFast();
1893 // break;
1894 default:
1895 loadFileFast(idx);
1896 //if (mLoader) mLoader->loadFileAt(idx);
1897 }
1898 }
1899 else
1900 loadFile(filename);
1901
1902 qDebug() << "loading file: " << filename;
1903
1904 //DkSettingsManager::param().sync().syncMode = oldMode;
1905 }
1906
imageContainer() const1907 QSharedPointer<DkImageContainerT> DkViewPort::imageContainer() const {
1908
1909 if (!mLoader)
1910 return QSharedPointer<DkImageContainerT>();
1911
1912 return mLoader->getCurrentImage();
1913 }
1914
setImageLoader(QSharedPointer<DkImageLoader> newLoader)1915 void DkViewPort::setImageLoader(QSharedPointer<DkImageLoader> newLoader) {
1916
1917 mLoader = newLoader;
1918 connectLoader(newLoader);
1919
1920 if (mLoader)
1921 mLoader->activate();
1922 }
1923
connectLoader(QSharedPointer<DkImageLoader> loader,bool connectSignals)1924 void DkViewPort::connectLoader(QSharedPointer<DkImageLoader> loader, bool connectSignals) {
1925
1926 assert(mController);
1927
1928 if (!loader)
1929 return;
1930
1931 if (connectSignals) {
1932 //connect(mLoader.data(), SIGNAL(imageLoadedSignal(QSharedPointer<DkImageContainerT>, bool)), this, SLOT(updateImage(QSharedPointer<DkImageContainerT>, bool)), Qt::UniqueConnection);
1933 connect(loader.data(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), this, SLOT(updateImage(QSharedPointer<DkImageContainerT>)), Qt::UniqueConnection);
1934
1935 connect(loader.data(), SIGNAL(updateDirSignal(QVector<QSharedPointer<DkImageContainerT> >)), mController->getFilePreview(), SLOT(updateThumbs(QVector<QSharedPointer<DkImageContainerT> >)), Qt::UniqueConnection);
1936 connect(loader.data(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), mController->getFilePreview(), SLOT(setFileInfo(QSharedPointer<DkImageContainerT>)), Qt::UniqueConnection);
1937 connect(loader.data(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), mController->getMetaDataWidget(), SLOT(updateMetaData(QSharedPointer<DkImageContainerT>)), Qt::UniqueConnection);
1938 connect(loader.data(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), mController, SLOT(updateImage(QSharedPointer<DkImageContainerT>)), Qt::UniqueConnection);
1939
1940 connect(loader.data(), SIGNAL(showInfoSignal(const QString&, int, int)), mController, SLOT(setInfo(const QString&, int, int)), Qt::UniqueConnection);
1941
1942 connect(loader.data(), SIGNAL(setPlayer(bool)), mController->getPlayer(), SLOT(play(bool)), Qt::UniqueConnection);
1943
1944 connect(loader.data(), SIGNAL(updateDirSignal(QVector<QSharedPointer<DkImageContainerT> >)), mController->getScroller(), SLOT(updateDir(QVector<QSharedPointer<DkImageContainerT> >)), Qt::UniqueConnection);
1945 connect(loader.data(), SIGNAL(imageUpdatedSignal(int)), mController->getScroller(), SLOT(updateFile(int)), Qt::UniqueConnection);
1946 connect(mController->getScroller(), SIGNAL(valueChanged(int)), loader.data(), SLOT(loadFileAt(int)));
1947 }
1948 else {
1949 //connect(mLoader.data(), SIGNAL(imageLoadedSignal(QSharedPointer<DkImageContainerT>, bool)), this, SLOT(updateImage(QSharedPointer<DkImageContainerT>, bool)), Qt::UniqueConnection);
1950 disconnect(loader.data(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), this, SLOT(updateImage(QSharedPointer<DkImageContainerT>)));
1951
1952 disconnect(loader.data(), SIGNAL(updateDirSignal(QVector<QSharedPointer<DkImageContainerT> >)), mController->getFilePreview(), SLOT(updateThumbs(QVector<QSharedPointer<DkImageContainerT> >)));
1953 disconnect(loader.data(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), mController->getFilePreview(), SLOT(setFileInfo(QSharedPointer<DkImageContainerT>)));
1954 disconnect(loader.data(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), mController->getMetaDataWidget(), SLOT(updateMetaData(QSharedPointer<DkImageContainerT>)));
1955 disconnect(loader.data(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), mController, SLOT(setFileInfo(QSharedPointer<DkImageContainerT>)));
1956
1957 disconnect(loader.data(), SIGNAL(showInfoSignal(const QString&, int, int)), mController, SLOT(setInfo(const QString&, int, int)));
1958 disconnect(loader.data(), SIGNAL(updateSpinnerSignalDelayed(bool, int)), mController, SLOT(setSpinnerDelayed(bool, int)));
1959
1960 disconnect(loader.data(), SIGNAL(setPlayer(bool)), mController->getPlayer(), SLOT(play(bool)));
1961
1962 disconnect(loader.data(), SIGNAL(updateDirSignal(QVector<QSharedPointer<DkImageContainerT> >)), mController->getScroller(), SLOT(updateDir(QVector<QSharedPointer<DkImageContainerT> >)));
1963 disconnect(loader.data(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), mController->getScroller(), SLOT(updateFile(QSharedPointer<DkImageContainerT>)));
1964 }
1965 }
1966
1967
getController()1968 DkControlWidget* DkViewPort::getController() {
1969
1970 return mController;
1971 }
1972
cropImage(const DkRotatingRect & rect,const QColor & bgCol,bool cropToMetaData)1973 void DkViewPort::cropImage(const DkRotatingRect& rect, const QColor& bgCol, bool cropToMetaData) {
1974
1975 QSharedPointer<DkImageContainerT> imgC = mLoader->getCurrentImage();
1976
1977 if (!imgC) {
1978 qWarning() << "cannot crop NULL image...";
1979 return;
1980 }
1981
1982 imgC->cropImage(rect, bgCol, cropToMetaData);
1983 setEditedImage(imgC);
1984 }
1985
1986 // DkViewPortFrameless --------------------------------------------------------------------
DkViewPortFrameless(QWidget * parent)1987 DkViewPortFrameless::DkViewPortFrameless(QWidget *parent) : DkViewPort(parent) {
1988
1989 #ifdef Q_OS_MAC
1990 parent->setAttribute(Qt::WA_MacNoShadow);
1991 #endif
1992
1993 setAttribute(Qt::WA_TranslucentBackground, true);
1994 mImgBg.load(QFileInfo(QApplication::applicationDirPath(), "bgf.png").absoluteFilePath());
1995
1996 if (mImgBg.isNull())
1997 mImgBg.load(":/nomacs/img/splash-screen.png");
1998
1999 DkActionManager& am = DkActionManager::instance();
2000 mStartActions.append(am.action(DkActionManager::menu_file_open));
2001 mStartActions.append(am.action(DkActionManager::menu_file_open_dir));
2002
2003 mStartIcons.append(am.icon(DkActionManager::icon_file_open_large));
2004 mStartIcons.append(am.icon(DkActionManager::icon_file_dir_large));
2005 }
2006
~DkViewPortFrameless()2007 DkViewPortFrameless::~DkViewPortFrameless() {
2008 }
2009
zoom(double factor,const QPointF & center,bool force)2010 void DkViewPortFrameless::zoom(double factor, const QPointF& center, bool force) {
2011
2012 if (mImgStorage.isEmpty() || mBlockZooming)
2013 return;
2014
2015 //limit zoom out ---
2016 if (mWorldMatrix.m11()*factor <= mMinZoom && factor < 1)
2017 return;
2018
2019 // reset view & block if we pass the 'image fit to screen' on zoom out
2020 if (mWorldMatrix.m11() > 1 && mWorldMatrix.m11()*factor < 1 && !force) {
2021
2022 mBlockZooming = true;
2023 mZoomTimer->start(500);
2024 }
2025
2026 //limit zoom in ---
2027 if (mWorldMatrix.m11()*mImgMatrix.m11() > mMaxZoom && factor > 1)
2028 return;
2029
2030 QRectF viewRect = mWorldMatrix.mapRect(mImgViewRect);
2031 QPointF pos = center;
2032
2033 // if no center assigned: zoom in at the image center
2034 if (pos.x() == -1 || pos.y() == -1)
2035 pos = viewRect.center();
2036
2037 if (pos.x() < viewRect.left()) pos.setX(viewRect.left());
2038 else if (pos.x() > viewRect.right()) pos.setX(viewRect.right());
2039 if (pos.y() < viewRect.top()) pos.setY(viewRect.top());
2040 else if (pos.y() > viewRect.bottom()) pos.setY(viewRect.bottom());
2041
2042 zoomToPoint(factor, pos, mWorldMatrix);
2043
2044 controlImagePosition();
2045 showZoom();
2046 changeCursor();
2047
2048 update();
2049
2050 tcpSynchronize();
2051 emit zoomSignal(mWorldMatrix.m11()*mImgMatrix.m11()*100);
2052 }
2053
resetView()2054 void DkViewPortFrameless::resetView() {
2055
2056 // maybe we can delete this function...
2057 DkViewPort::resetView();
2058 }
2059
paintEvent(QPaintEvent * event)2060 void DkViewPortFrameless::paintEvent(QPaintEvent* event) {
2061
2062 if (!DkUtils::getMainWindow()->isFullScreen()) {
2063
2064 QPainter painter(viewport());
2065 painter.setWorldTransform(mWorldMatrix);
2066 drawFrame(painter);
2067 painter.end();
2068 }
2069
2070 DkViewPort::paintEvent(event);
2071 }
2072
draw(QPainter & painter,double)2073 void DkViewPortFrameless::draw(QPainter & painter, double) {
2074
2075 if (DkUtils::getMainWindow()->isFullScreen()) {
2076 QColor col = QColor(0,0,0);
2077 col.setAlpha(150);
2078 painter.setWorldMatrixEnabled(false);
2079 painter.fillRect(QRect(QPoint(), size()), col);
2080 painter.setWorldMatrixEnabled(true);
2081 }
2082
2083 if (mSvg && mSvg->isValid()) {
2084 mSvg->render(&painter, mImgViewRect);
2085 }
2086 else if (mMovie && mMovie->isValid()) {
2087 painter.drawPixmap(mImgViewRect, mMovie->currentPixmap(), mMovie->frameRect());
2088 }
2089 else {
2090 QImage img = mImgStorage.image((float)(mImgMatrix.m11()*mWorldMatrix.m11()));
2091
2092 // opacity == 1.0f -> do not show pattern if we crossfade two images
2093 if (DkSettingsManager::param().display().tpPattern && img.hasAlphaChannel())
2094 drawPattern(painter);
2095
2096 painter.drawImage(mImgViewRect, img, QRect(QPoint(), img.size()));
2097 }
2098
2099 }
2100
drawBackground(QPainter & painter)2101 void DkViewPortFrameless::drawBackground(QPainter & painter) {
2102
2103 painter.setWorldTransform(mImgMatrix);
2104 painter.setBrush(QColor(127, 144, 144, 200));
2105 painter.setPen(QColor(100, 100, 100, 255));
2106
2107 QRectF initialRect = rect();
2108 QPointF oldCenter = initialRect.center();
2109
2110 QTransform cT;
2111 cT.scale(400/initialRect.width(), 400/initialRect.width());
2112 initialRect = cT.mapRect(initialRect);
2113 initialRect.moveCenter(oldCenter);
2114
2115 // fit to mViewport
2116 QSize s = mImgBg.size();
2117
2118 QRectF bgRect(QPoint(), s);
2119 bgRect.moveCenter(initialRect.center());//moveTopLeft(QPointF(size().width(), size().height())*0.5 - initialRect.bottomRight()*0.5);
2120
2121 //painter.drawRect(initialRect);
2122 painter.drawImage(bgRect, mImgBg, QRect(QPoint(), mImgBg.size()));
2123
2124 if (mStartActions.isEmpty())
2125 return;
2126
2127 // first time?
2128 if (mStartActionsRects.isEmpty()) {
2129 float margin = 40;
2130 float iconSizeMargin = (float)((initialRect.width()-3*margin)/mStartActions.size());
2131 QSize iconSize = QSize(qRound(iconSizeMargin - margin), qRound(iconSizeMargin - margin));
2132 QPointF offset = QPointF(bgRect.left() + 50, initialRect.center().y()+iconSizeMargin*0.25f);
2133
2134 for (int idx = 0; idx < mStartActions.size(); idx++) {
2135
2136 QRectF iconRect = QRectF(offset, iconSize);
2137 QPixmap ci = !mStartIcons[idx].isNull() ? mStartIcons[idx].pixmap(iconSize) : mStartActions[idx]->icon().pixmap(iconSize);
2138 mStartActionsRects.push_back(iconRect);
2139 mStartActionsIcons.push_back(ci);
2140
2141 offset.setX(offset.x() + margin + iconSize.width());
2142 }
2143 }
2144
2145 // draw start actions
2146 for (int idx = 0; idx < mStartActions.size(); idx++) {
2147
2148 if (!mStartIcons[idx].isNull())
2149 painter.drawPixmap(mStartActionsRects[idx], mStartActionsIcons[idx], QRect(QPoint(), mStartActionsIcons[idx].size()));
2150 else
2151 painter.drawPixmap(mStartActionsRects[idx], mStartActionsIcons[idx], QRect(QPoint(), mStartActionsIcons[idx].size()));
2152
2153 QRectF tmpRect = mStartActionsRects[idx];
2154 QString text = mStartActions[idx]->text().remove("&");
2155 tmpRect.moveTop(tmpRect.bottom()+10);
2156 painter.drawText(tmpRect, text);
2157 }
2158
2159 QString infoText = tr("Press F10 to exit Frameless view");
2160 QRectF tmpRect(bgRect.left()+50, bgRect.bottom()-60, bgRect.width()-100, 20);
2161 painter.drawText(tmpRect, infoText);
2162 }
2163
drawFrame(QPainter & painter)2164 void DkViewPortFrameless::drawFrame(QPainter & painter) {
2165
2166 // TODO: replace hasAlphaChannel with has alphaBorder
2167 if ((!mImgStorage.isEmpty() && mImgStorage.image().hasAlphaChannel()) || !DkSettingsManager::param().display().showBorder) // braces
2168 return;
2169
2170 painter.setBrush(QColor(255, 255, 255, 200));
2171 painter.setPen(QColor(100, 100, 100, 255));
2172
2173 QRectF frameRect;
2174
2175 float fs = qMin((float)mImgViewRect.width(), (float)mImgViewRect.height())*0.1f;
2176
2177 // looks pretty bad if the frame is too small
2178 if (fs < 4)
2179 return;
2180
2181 frameRect = mImgViewRect;
2182 frameRect.setSize(frameRect.size() + QSize(qRound(fs), qRound(fs)));
2183 frameRect.moveCenter(mImgViewRect.center());
2184
2185 painter.drawRect(frameRect);
2186 }
2187
mousePressEvent(QMouseEvent * event)2188 void DkViewPortFrameless::mousePressEvent(QMouseEvent *event) {
2189
2190 // move the window - todo: NOT full screen, window inside...
2191 setCursor(Qt::ClosedHandCursor);
2192 mPosGrab = event->pos();
2193
2194 DkViewPort::mousePressEvent(event);
2195 }
2196
mouseReleaseEvent(QMouseEvent * event)2197 void DkViewPortFrameless::mouseReleaseEvent(QMouseEvent *event) {
2198
2199 if (mImgStorage.isEmpty()) {
2200
2201 QPointF pos = mImgMatrix.inverted().map(event->pos());
2202
2203 for (int idx = 0; idx < mStartActionsRects.size(); idx++) {
2204
2205 if (mStartActionsRects[idx].contains(pos)) {
2206
2207 mStartActions[idx]->trigger();
2208 break;
2209 }
2210 }
2211 }
2212
2213 unsetCursor();
2214 //setCursor(Qt::OpenHandCursor);
2215 DkViewPort::mouseReleaseEvent(event);
2216 }
2217
2218
mouseMoveEvent(QMouseEvent * event)2219 void DkViewPortFrameless::mouseMoveEvent(QMouseEvent *event) {
2220
2221 if (mImgStorage.isEmpty()) {
2222
2223 QPointF pos = mImgMatrix.inverted().map(event->pos());
2224
2225 int idx;
2226 for (idx = 0; idx < mStartActionsRects.size(); idx++) {
2227
2228 if (mStartActionsRects[idx].contains(pos)) {
2229 setCursor(Qt::PointingHandCursor);
2230 break;
2231 }
2232 }
2233
2234 //// TODO: change if closed hand cursor is present...
2235 //if (idx == startActionsRects.size())
2236 // setCursor(Qt::OpenHandCursor);
2237 }
2238
2239 if (DkStatusBarManager::instance().statusbar()->isVisible())
2240 getPixelInfo(event->pos());
2241
2242 if (event->buttons() == Qt::LeftButton) {
2243
2244 QPointF cPos = event->pos();
2245 QPointF dxy = (cPos - mPosGrab);
2246 mPosGrab = cPos;
2247 moveView(dxy/mWorldMatrix.m11());
2248 }
2249
2250 QGraphicsView::mouseMoveEvent(event);
2251 }
2252
moveView(QPointF delta)2253 void DkViewPortFrameless::moveView(QPointF delta) {
2254
2255 // if no zoom is present -> the translation is like a move window
2256 if (mWorldMatrix.m11() == 1.0f) {
2257 float s = (float)mImgMatrix.m11();
2258 mImgMatrix.translate(delta.x()/s, delta.y()/s);
2259 mImgViewRect = mImgMatrix.mapRect(mImgRect);
2260 }
2261 else
2262 mWorldMatrix.translate(delta.x(), delta.y());
2263
2264 controlImagePosition();
2265 update();
2266 }
2267
2268
controlImagePosition(float,float)2269 void DkViewPortFrameless::controlImagePosition(float, float) {
2270 // dummy method
2271 }
2272
centerImage()2273 void DkViewPortFrameless::centerImage() {
2274 }
2275
updateImageMatrix()2276 void DkViewPortFrameless::updateImageMatrix() {
2277
2278 if (mImgStorage.isEmpty())
2279 return;
2280
2281 QRectF oldImgRect = mImgViewRect;
2282 QTransform oldImgMatrix = mImgMatrix;
2283
2284 mImgMatrix.reset();
2285
2286 QSize imgSize = getImageSize();
2287
2288 // if the image is smaller or zoom is active: paint the image as is
2289 if (!mViewportRect.contains(mImgRect.toRect())) {
2290 mImgMatrix = getScaledImageMatrix(size()*0.9f);
2291 QSize shift = size()*0.1f;
2292 mImgMatrix.translate(shift.width(), shift.height());
2293 }
2294 else {
2295 mImgMatrix.translate((float)(getMainGeometry().width()-imgSize.width())*0.5f, (float)(getMainGeometry().height()-imgSize.height())*0.5f);
2296 mImgMatrix.scale(1.0f, 1.0f);
2297 }
2298
2299 mImgViewRect = mImgMatrix.mapRect(mImgRect);
2300
2301 // update world matrix
2302 if (mWorldMatrix.m11() != 1) {
2303
2304 float scaleFactor = (float)(oldImgMatrix.m11()/mImgMatrix.m11());
2305 double dx = oldImgRect.x()/scaleFactor-mImgViewRect.x();
2306 double dy = oldImgRect.y()/scaleFactor-mImgViewRect.y();
2307
2308 mWorldMatrix.scale(scaleFactor, scaleFactor);
2309 mWorldMatrix.translate(dx, dy);
2310 }
2311 }
2312
2313 // DkViewPortContrast --------------------------------------------------------------------
DkViewPortContrast(QWidget * parent)2314 DkViewPortContrast::DkViewPortContrast(QWidget *parent) : DkViewPort(parent) {
2315
2316 mColorTable = QVector<QRgb>(256);
2317 for (int i = 0; i < mColorTable.size(); i++)
2318 mColorTable[i] = qRgb(i, i, i);
2319
2320 // connect
2321 auto ttb = DkToolBarManager::inst().transferToolBar();
2322 connect(ttb, SIGNAL(colorTableChanged(QGradientStops)), this, SLOT(changeColorTable(QGradientStops)));
2323 connect(ttb, SIGNAL(channelChanged(int)), this, SLOT(changeChannel(int)));
2324 connect(ttb, SIGNAL(pickColorRequest(bool)), this, SLOT(pickColor(bool)));
2325 connect(ttb, SIGNAL(tFEnabled(bool)), this, SLOT(enableTF(bool)));
2326 connect(this, SIGNAL(tFSliderAdded(qreal)), ttb, SLOT(insertSlider(qreal)));
2327 connect(this, SIGNAL(imageModeSet(int)), ttb, SLOT(setImageMode(int)));
2328 }
2329
~DkViewPortContrast()2330 DkViewPortContrast::~DkViewPortContrast() {
2331 }
2332
changeChannel(int channel)2333 void DkViewPortContrast::changeChannel(int channel) {
2334
2335 if (channel < 0 || channel >= mImgs.size())
2336 return;
2337
2338 if (!mImgStorage.isEmpty()) {
2339
2340 mFalseColorImg = mImgs[channel];
2341 mFalseColorImg.setColorTable(mColorTable);
2342 mDrawFalseColorImg = true;
2343
2344 update();
2345
2346 drawImageHistogram();
2347 }
2348 }
2349
changeColorTable(QGradientStops stops)2350 void DkViewPortContrast::changeColorTable(QGradientStops stops) {
2351
2352 qreal fac;
2353
2354 qreal actPos, leftStop, rightStop;
2355 QColor tmp;
2356
2357 int rLeft, gLeft, bLeft, rRight, gRight, bRight;
2358 int rAct, gAct, bAct;
2359
2360 // At least one stop has to be set:
2361 tmp = stops.at(0).second;
2362 tmp.getRgb(&rLeft, &gLeft, &bLeft);
2363 leftStop = stops.at(0).first;
2364
2365 // If just one stop is set, we can speed things up:
2366 if (stops.size() == 1) {
2367 for (int i = 0; i < mColorTable.size(); i++)
2368 mColorTable[i] = qRgb(rLeft, gLeft, bLeft);
2369 }
2370 // Otherwise interpolate:
2371 else {
2372
2373 int rightStopIdx = 1;
2374 tmp = stops.at(rightStopIdx).second;
2375 tmp.getRgb(&rRight, &gRight, &bRight);
2376 rightStop = stops.at(rightStopIdx).first;
2377
2378 for (int i = 0; i < mColorTable.size(); i++) {
2379 actPos = (qreal) i / mColorTable.size();
2380
2381 if (actPos > rightStop) {
2382 leftStop = rightStop;
2383
2384 rLeft = rRight;
2385 gLeft = gRight;
2386 bLeft = bRight;
2387
2388 if (stops.size() > rightStopIdx + 1) {
2389 rightStopIdx++;
2390 rightStop = stops.at(rightStopIdx).first;
2391 tmp = stops.at(rightStopIdx).second;
2392 tmp.getRgb(&rRight, &gRight, &bRight);
2393 }
2394
2395 }
2396
2397 if (actPos <= leftStop)
2398 mColorTable[i] = qRgb(rLeft, gLeft, bLeft);
2399 else if (actPos >= rightStop)
2400 mColorTable[i] = qRgb(rRight, gRight, bRight);
2401 else {
2402 fac = (actPos - leftStop) / (rightStop - leftStop);
2403 rAct = qRound(rLeft + (rRight - rLeft) * fac);
2404 gAct = qRound(gLeft + (gRight - gLeft) * fac);
2405 bAct = qRound(bLeft + (bRight - bLeft) * fac);
2406 mColorTable[i] = qRgb(rAct, gAct, bAct);
2407 }
2408 }
2409 }
2410
2411
2412 mFalseColorImg.setColorTable(mColorTable);
2413
2414 update();
2415
2416 }
2417
draw(QPainter & painter,double opacity)2418 void DkViewPortContrast::draw(QPainter & painter, double opacity) {
2419
2420 if (!mDrawFalseColorImg || mSvg || mMovie) {
2421 DkBaseViewPort::draw(painter, opacity);
2422 return;
2423 }
2424
2425 if (DkUtils::getMainWindow()->isFullScreen())
2426 painter.setBackground(DkSettingsManager::param().slideShow().backgroundColor);
2427
2428 QImage img = mImgStorage.image((float)(mImgMatrix.m11()*mWorldMatrix.m11()));
2429
2430 // opacity == 1.0f -> do not show pattern if we crossfade two images
2431 if (DkSettingsManager::param().display().tpPattern && img.hasAlphaChannel() && opacity == 1.0)
2432 drawPattern(painter);
2433
2434 if (mDrawFalseColorImg)
2435 painter.drawImage(mImgViewRect, mFalseColorImg, mImgRect);
2436 }
2437
setImage(QImage newImg)2438 void DkViewPortContrast::setImage(QImage newImg) {
2439
2440 DkViewPort::setImage(newImg);
2441
2442 if (newImg.isNull())
2443 return;
2444
2445 if (mImgStorage.image().format() == QImage::Format_Indexed8) {
2446 mImgs = QVector<QImage>(1);
2447 mImgs[0] = mImgStorage.image();
2448 mActiveChannel = 0;
2449 }
2450 #ifdef WITH_OPENCV
2451
2452 else {
2453
2454 mImgs = QVector<QImage>(4);
2455 std::vector<cv::Mat> planes;
2456
2457 cv::Mat imgUC3 = DkImage::qImage2Mat(mImgStorage.image());
2458 //int format = imgQt.format();
2459 //if (format == QImage::Format_RGB888)
2460 // imgUC3 = Mat(imgQt.height(), imgQt.width(), CV_8UC3, (uchar*)imgQt.bits(), imgQt.bytesPerLine());
2461 //else
2462 // imgUC3 = Mat(imgQt.height(), imgQt.width(), CV_8UC4, (uchar*)imgQt.bits(), imgQt.bytesPerLine());
2463 split(imgUC3, planes);
2464 // Store the 3 channels in a QImage Vector.
2465 //Be aware that OpenCV 'swaps' the rgb triplet, hence process it in a descending way:
2466 int idx = 1;
2467 for (int i = 2; i >= 0; i--) {
2468
2469 // dirty hack
2470 if (i >= (int)planes.size()) i = 0;
2471 mImgs[idx] = QImage((const unsigned char*)planes[i].data, (int)planes[i].cols, (int)planes[i].rows, (int)planes[i].step, QImage::Format_Indexed8);
2472 mImgs[idx] = mImgs[idx].copy();
2473 idx++;
2474
2475 }
2476 // The first element in the vector contains the gray scale 'average' of the 3 channels:
2477 cv::Mat grayMat;
2478 cv::cvtColor(imgUC3, grayMat, CV_BGR2GRAY);
2479 mImgs[0] = QImage((const unsigned char*)grayMat.data, (int)grayMat.cols, (int)grayMat.rows, (int)grayMat.step, QImage::Format_Indexed8);
2480 mImgs[0] = mImgs[0].copy();
2481 planes.clear();
2482
2483 }
2484 #else
2485
2486 else {
2487 mDrawFalseColorImg = false;
2488 emit imageModeSet(mode_invalid_format);
2489 return;
2490 }
2491
2492 #endif
2493
2494 mFalseColorImg = mImgs[mActiveChannel];
2495 mFalseColorImg.setColorTable(mColorTable);
2496
2497 // images with valid color table return img.isGrayScale() false...
2498 if (mSvg || mMovie)
2499 emit imageModeSet(mode_invalid_format);
2500 else if (mImgs.size() == 1)
2501 emit imageModeSet(mode_gray);
2502 else
2503 emit imageModeSet(mode_rgb);
2504
2505 update();
2506
2507
2508 }
2509
pickColor(bool enable)2510 void DkViewPortContrast::pickColor(bool enable) {
2511
2512 mIsColorPickerActive = enable;
2513 this->setCursor(Qt::CrossCursor);
2514
2515 }
2516
enableTF(bool enable)2517 void DkViewPortContrast::enableTF(bool enable) {
2518
2519 mDrawFalseColorImg = enable;
2520 update();
2521
2522 drawImageHistogram();
2523
2524 }
2525
mousePressEvent(QMouseEvent * event)2526 void DkViewPortContrast::mousePressEvent(QMouseEvent *event) {
2527
2528 if (!mIsColorPickerActive)
2529 DkViewPort::mousePressEvent(event); // just propagate events, if the color picker is not active
2530 }
2531
mouseMoveEvent(QMouseEvent * event)2532 void DkViewPortContrast::mouseMoveEvent(QMouseEvent *event) {
2533
2534 if (!mIsColorPickerActive)
2535 DkViewPort::mouseMoveEvent(event); // just propagate events, if the color picker is not active
2536 else if (DkStatusBarManager::instance().statusbar()->isVisible())
2537 getPixelInfo(event->pos());
2538 }
2539
mouseReleaseEvent(QMouseEvent * event)2540 void DkViewPortContrast::mouseReleaseEvent(QMouseEvent *event) {
2541
2542 if (mIsColorPickerActive) {
2543
2544 QPointF imgPos = mWorldMatrix.inverted().map(event->pos());
2545 imgPos = mImgMatrix.inverted().map(imgPos);
2546
2547 QPoint xy = imgPos.toPoint();
2548
2549 bool isPointValid = true;
2550
2551 if (xy.x() < 0 || xy.y() < 0 || xy.x() >= getImageSize().width() || xy.y() >= getImageSize().height())
2552 isPointValid = false;
2553
2554 if (isPointValid) {
2555
2556 int colorIdx = mImgs[mActiveChannel].pixelIndex(xy);
2557 qreal normedPos = (qreal) colorIdx / 255;
2558 emit tFSliderAdded(normedPos);
2559 }
2560
2561 //unsetCursor();
2562 //isColorPickerActive = false;
2563 }
2564 else
2565 DkViewPort::mouseReleaseEvent(event);
2566 }
2567
keyPressEvent(QKeyEvent * event)2568 void DkViewPortContrast::keyPressEvent(QKeyEvent* event) {
2569
2570 if ((event->key() == Qt::Key_Escape) && mIsColorPickerActive) {
2571 unsetCursor();
2572 mIsColorPickerActive = false;
2573 update();
2574 return;
2575 }
2576 else
2577 DkViewPort::keyPressEvent(event);
2578 }
2579
getImage() const2580 QImage DkViewPortContrast::getImage() const {
2581
2582 if (mDrawFalseColorImg)
2583 return mFalseColorImg;
2584 else
2585 return imageContainer() ? imageContainer()->image() : QImage();
2586
2587 }
2588
2589 // in contrast mode: if the histogram widget is visible redraw the histogram from the selected image channel data
drawImageHistogram()2590 void DkViewPortContrast::drawImageHistogram() {
2591
2592 if (mController->getHistogram() && mController->getHistogram()->isVisible()) {
2593 if (mDrawFalseColorImg)
2594 mController->getHistogram()->drawHistogram(mFalseColorImg);
2595 else
2596 mController->getHistogram()->drawHistogram(getImage());
2597 }
2598 }
2599
2600 }
2601