1 /*******************************************************************************
2 **
3 ** Photivo
4 **
5 ** Copyright (C) 2009-2011 Michael Munzert <mail@mm-log.com>
6 ** Copyright (C) 2011 Bernd Schoeler <brjohn@brother-john.net>
7 ** Copyright (C) 2013 Alexander Tzyganenko <tz@fast-report.com>
8 **
9 ** This file is part of Photivo.
10 **
11 ** Photivo is free software: you can redistribute it and/or modify
12 ** it under the terms of the GNU General Public License version 3
13 ** as published by the Free Software Foundation.
14 **
15 ** Photivo is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ** GNU General Public License for more details.
19 **
20 ** You should have received a copy of the GNU General Public License
21 ** along with Photivo. If not, see <http://www.gnu.org/licenses/>.
22 **
23 *******************************************************************************/
24
25 #include "ptDefines.h"
26 #include "ptSettings.h"
27 #include "ptTheme.h"
28 #include "ptViewWindow.h"
29 #include "ptConstants.h"
30 #include "ptImage.h"
31
32 #include <QScrollBar>
33
34 #include <cassert>
35
36 extern ptTheme* Theme;
37
38 //==============================================================================
39
ptViewWindow(QWidget * Parent,ptMainWindow * mainWin)40 ptViewWindow::ptViewWindow(QWidget* Parent, ptMainWindow* mainWin)
41 : QGraphicsView(Parent),
42 // constants
43 MinZoom(0.05), MaxZoom(4.0),
44 // member variables
45 FCtrlIsPressed(0),
46 FInteractionHasMousePress(false),
47 FCurrentInteraction(nullptr),
48 FDrawLine(nullptr),
49 FSpotTuning(nullptr),
50 FSelectRect(nullptr),
51 FCrop(nullptr),
52 FInteraction(iaNone),
53 FLeftMousePressed(0),
54 FZoomIsSaved(0),
55 FZoomFactor(1.0),
56 FZoomFactorSav(0.0),
57 FZoomModeSav(ptZoomMode_Fit),
58 FPixelReading(prNone),
59 // always keep this at the end of the initializer list
60 FMainWindow(mainWin)
61 {
62 assert(FMainWindow != NULL); // Main window must exist before view window
63 assert(Theme != NULL);
64 assert(Settings != NULL);
65
66 ZoomFactors << MinZoom << 0.08 << 0.10 << 0.15 << 0.20 << 0.25 << 0.33 << 0.50 << 0.66 << 1.00
67 << 1.50 << 2.00 << 3.00 << MaxZoom;
68
69 FDragDelta = new QLine();
70 FStatusOverlay = new ptReportOverlay(this, "", QColor(), QColor(),
71 0, Qt::AlignLeft, 20);
72 FZoomSizeOverlay = new ptReportOverlay(this, "", QColor(75,150,255), QColor(190,220,255),
73 1000, Qt::AlignRight, 20);
74
75 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
76 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
77 setMouseTracking(true); // Move events without pressed button. Needed for crop cursor change.
78
79 // Layout to always fill the complete image pane with ViewWindow
80 QGridLayout* Layout = new QGridLayout(Parent);
81 Layout->setContentsMargins(0,0,0,0);
82 Layout->setSpacing(0);
83 Layout->addWidget(this);
84 this->setStyleSheet("QGraphicsView { border: none; }");
85
86 // Init the scene
87 FImageScene = new QGraphicsScene(0, 0, 0, 0, Parent);
88 F8bitImageItem = FImageScene->addPixmap(QPixmap());
89 F8bitImageItem->setPos(0, 0);
90 this->setScene(FImageScene);
91
92 FGrid = new ptGridInteraction(this); // scene must already be alive
93 FPixelReader = NULL;
94 ConstructContextMenu();
95
96 FPReadTimer = new QTimer();
97 FPReadTimer->setSingleShot(true);
98 }
99
100 //==============================================================================
101
~ptViewWindow()102 ptViewWindow::~ptViewWindow() {
103 DelAndNull(FDragDelta);
104 DelAndNull(FStatusOverlay);
105 DelAndNull(FZoomSizeOverlay);
106 DelAndNull(FDrawLine);
107 DelAndNull(FSelectRect);
108 DelAndNull(FGrid);
109
110 DelAndNull(ac_PRead_None);
111 DelAndNull(ac_PRead_Linear);
112 DelAndNull(ac_PRead_Preview);
113 DelAndNull(ac_PReadGroup);
114
115 DelAndNull(FPReadTimer);
116 }
117
118 //==============================================================================
119
120 // Convert a 16bit ptImage to an 8bit QPixmap. Mind R<->B. Also update the
121 // graphics scene and the viewport.
UpdateImage(const ptImage * relatedImage)122 void ptViewWindow::UpdateImage(const ptImage* relatedImage) {
123 if (relatedImage) {
124 this->blockSignals(1);
125 QImage* Img8bit = new QImage(relatedImage->m_Width, relatedImage->m_Height, QImage::Format_RGB32);
126 uint32_t Size = relatedImage->m_Height * relatedImage->m_Width;
127 uint32_t* ImagePtr = (QRgb*) Img8bit->scanLine(0);
128
129 #pragma omp parallel for schedule(static)
130 for (uint32_t i = 0; i < Size; i++) {
131 uint8_t* Pixel = (uint8_t*) ImagePtr + (i<<2);
132 for (short c=0; c<3; c++) {
133 // Mind the R<->B swap !
134 Pixel[2-c] = relatedImage->m_Image[i][c]>>8;
135 }
136 Pixel[3] = 0xff;
137 }
138
139 F8bitImageItem->setPixmap(QPixmap::fromImage(*Img8bit, Qt::ColorOnly));
140 DelAndNull(Img8bit);
141 FImageScene->setSceneRect(0, 0,
142 F8bitImageItem->pixmap().width(),
143 F8bitImageItem->pixmap().height());
144
145 if (Settings->GetInt("ZoomMode") == ptZoomMode_Fit) {
146 ZoomToFit(0);
147 }
148
149 this->blockSignals(0);
150 }
151 }
152
153 //==============================================================================
154
155 // ZoomTo() is also called by wheelEvent() for mouse wheel zoom.
ZoomTo(float factor)156 void ptViewWindow::ZoomTo(float factor) {
157 Settings->SetValue("ZoomMode",ptZoomMode_NonFit);
158 factor = qBound(MinZoom, factor, MaxZoom);
159
160 if(((uint)(factor * 10000) % 10000) < 1) {
161 // nearest neighbour resize for 200%, 300%, 400% zoom
162 F8bitImageItem->setTransformationMode(Qt::FastTransformation);
163 } else {
164 // bilinear resize for all others
165 F8bitImageItem->setTransformationMode(Qt::SmoothTransformation);
166 }
167 setTransform(QTransform(factor, 0, 0, factor, 0, 0));
168
169 FZoomFactor = transform().m11();
170 int z = qRound(FZoomFactor * 100);
171 Settings->SetValue("Zoom", z);
172 FZoomSizeOverlay->exec(QString::number(z) + "%");
173 }
174
175 //==============================================================================
176
ZoomToFit(const short withMsg)177 int ptViewWindow::ZoomToFit(const short withMsg /*= 1*/) {
178 Settings->SetValue("ZoomMode",ptZoomMode_Fit);
179
180 if (!F8bitImageItem->pixmap().isNull()) {
181 // Always smooth scaling because we don't know the zoom factor in advance.
182 F8bitImageItem->setTransformationMode(Qt::SmoothTransformation);
183 fitInView(F8bitImageItem, Qt::KeepAspectRatio);
184 FZoomFactor = transform().m11();
185
186 if (withMsg) {
187 FZoomSizeOverlay->exec(tr("Fit"));
188 }
189 }
190
191 Settings->SetValue("Zoom",qRound(FZoomFactor * 100));
192 return FZoomFactor;
193 }
194
195 //==============================================================================
196
ZoomStep(int direction)197 void ptViewWindow::ZoomStep(int direction) {
198 int ZoomIdx = -1;
199
200 // zoom larger
201 if (direction > 0) {
202 for (int i = 0; i < ZoomFactors.size(); i++) {
203 if (ZoomFactors[i] > FZoomFactor) {
204 ZoomIdx = i;
205 break;
206 }
207 }
208
209 // zoom smaller
210 } else if (direction < 0) {
211 for (int i = ZoomFactors.size() - 1; i >= 0; i--) {
212 if (ZoomFactors[i] < FZoomFactor) {
213 ZoomIdx = i;
214 break;
215 }
216 }
217 }
218
219 if (ZoomIdx != -1) {
220 ZoomTo(ZoomFactors[ZoomIdx]);
221 }
222 }
223
224 //==============================================================================
225
RestoreZoom()226 void ptViewWindow::RestoreZoom() {
227 if (FZoomIsSaved) {
228 if (FZoomModeSav == ptZoomMode_Fit) {
229 ZoomToFit();
230 } else {
231 ZoomTo(FZoomFactorSav);
232 }
233
234 FZoomIsSaved = 0;
235 }
236 }
237
238 //==============================================================================
239
SaveZoom()240 void ptViewWindow::SaveZoom() {
241 FZoomFactorSav = FZoomFactor;
242 FZoomModeSav = Settings->GetValue("ZoomMode").toInt();
243 FZoomIsSaved = 1;
244 }
245
246 //==============================================================================
247
setGrid(const short enabled,const uint linesX,const uint linesY)248 void ptViewWindow::setGrid(const short enabled, const uint linesX, const uint linesY) {
249 if (enabled) {
250 FGrid->show(linesX, linesY);
251 } else {
252 FGrid->hide();
253 }
254 }
255
256 //==============================================================================
257
paintEvent(QPaintEvent * event)258 void ptViewWindow::paintEvent(QPaintEvent* event) {
259 // Fill viewport with background colour
260 QPainter Painter(viewport());
261 Painter.fillRect(0, 0, viewport()->width(), viewport()->height(), palette().color(QPalette::Window));
262 Painter.end();
263
264 // takes care of updating the scene
265 QGraphicsView::paintEvent(event);
266 }
267
268 //==============================================================================
269
mousePressEvent(QMouseEvent * event)270 void ptViewWindow::mousePressEvent(QMouseEvent* event) {
271 if (event->button() == Qt::LeftButton) {
272 event->accept();
273 FLeftMousePressed = true;
274 FDragDelta->setPoints(event->pos(), event->pos());
275 }
276
277 // Broadcast event to possible interaction handlers
278 if (FInteraction != iaNone) {
279 emit mouseChanged(event);
280 }
281 }
282
283 //==============================================================================
284
mouseReleaseEvent(QMouseEvent * event)285 void ptViewWindow::mouseReleaseEvent(QMouseEvent* event) {
286 if (event->button() == Qt::LeftButton && FLeftMousePressed) {
287 FLeftMousePressed = false;
288 event->accept();
289 } else {
290 event->ignore();
291 }
292
293 // Broadcast event to possible interaction handlers
294 if (FInteraction != iaNone) {
295 emit mouseChanged(event);
296 }
297 }
298
299 //==============================================================================
300
mouseDoubleClickEvent(QMouseEvent * event)301 void ptViewWindow::mouseDoubleClickEvent(QMouseEvent* event) {
302 // Broadcast event to possible interaction handlers
303 if (FInteraction != iaNone) {
304 emit mouseChanged(event);
305 } else {
306 event->ignore();
307 }
308 }
309
310 //==============================================================================
311
mouseMoveEvent(QMouseEvent * event)312 void ptViewWindow::mouseMoveEvent(QMouseEvent* event) {
313 // We broadcast the pixel location
314 if (FPixelReading != prNone && !FPReadTimer->isActive()) {
315 FPReadTimer->start(10);
316 QPointF Point = mapToScene(event->pos());
317 if (Point.x() >= 0 && Point.x() < F8bitImageItem->pixmap().width() &&
318 Point.y() >= 0 && Point.y() < F8bitImageItem->pixmap().height()) {
319 if (FPixelReader) FPixelReader(Point, FPixelReading);
320 } else {
321 if (FPixelReader) FPixelReader(Point, prNone);
322 }
323 }
324
325 if (this->isImgDragging()) {
326 // drag move visible image area
327 FDragDelta->setP2(event->pos());
328
329 horizontalScrollBar()->setValue(horizontalScrollBar()->value() -
330 FDragDelta->x2() +
331 FDragDelta->x1());
332 verticalScrollBar()->setValue(verticalScrollBar()->value() -
333 FDragDelta->y2() +
334 FDragDelta->y1());
335 FDragDelta->setP1(event->pos());
336 event->accept();
337
338 } else {
339 event->ignore();
340 // Broadcast event to possible interaction handlers
341 if (FInteraction != iaNone) {
342 emit mouseChanged(event);
343 }
344 }
345 }
346
347 //==============================================================================
348
wheelEvent(QWheelEvent * event)349 void ptViewWindow::wheelEvent(QWheelEvent* event) {
350 ZoomStep(event->delta());
351 }
352
353 //==============================================================================
354
leaveEvent(QEvent *)355 void ptViewWindow::leaveEvent(QEvent*) {
356 if (FPixelReader)
357 FPixelReader(QPoint(), prNone);
358
359 // When mouse cursor leaves the viewwindow ctrl key should not be handled anymore.
360 // Reset related stuff to avoid problems when re-entering viewwindow. Qt mouse grabbing
361 // makes sure this event does not fire accidentally when the cursor leaves the window
362 // while dragging.
363 if (FCtrlIsPressed > 0){
364 FCtrlIsPressed = 0;
365 this->setCursor(Qt::ArrowCursor);
366 }
367 }
368
369 //==============================================================================
370
isImgDragging()371 bool ptViewWindow::isImgDragging() {
372 bool hResult = FLeftMousePressed;
373
374 // Handle special cases first
375 if (FInteraction != iaNone) {
376 if (FCurrentInteraction->mouseActions().testFlag(maDragDrop)) {
377 // Interaction handles d&d. Test if Ctrl key is available
378 if (FCurrentInteraction->modifiers().testFlag(Qt::ControlModifier)) {
379 // Interaction handles d&d and Ctrl key. Dragging image not possible.
380 return false;
381 } else {
382 // image dragging by Ctrl+Drag
383 hResult = FLeftMousePressed && FCtrlIsPressed;
384 if (hResult) FCurrentInteraction->abortMouseAction(maClick);
385 }
386
387 } else {
388 if (hResult) FCurrentInteraction->abortMouseAction(maClick);
389 }
390 }
391
392 // usual case: no interaction or interaction doesn’t handle d&d
393 return hResult;
394 }
395
396 //==============================================================================
397
keyPressEvent(QKeyEvent * event)398 void ptViewWindow::keyPressEvent(QKeyEvent* event) {
399 // FCtrlIsPressed is not a simple bool flag to account for keyboards with
400 // multiple ctrl keys. There's a lot of those. ;-) For each ctrl press the
401 // variable is increased, for each release it's decreased. This is necessary
402 // because in cases like press left ctrl - press right ctrl - release left ctrl
403 // Photivo should still recognise the ctrl key as being held down.
404 if (event->key() == Qt::Key_Control) {
405 FCtrlIsPressed++;
406 if (FInteraction == iaNone || FInteraction == iaCrop || FInteraction == iaSpotRepair) {
407 setCursor(Qt::ClosedHandCursor);
408 }
409 } else {
410 event->ignore(); // necessary to forward unhandled keys to main window
411 }
412
413 if (FInteraction != iaNone) {
414 emit keyChanged(event);
415 }
416 }
417
418 //==============================================================================
419
keyReleaseEvent(QKeyEvent * event)420 void ptViewWindow::keyReleaseEvent(QKeyEvent* event) {
421 if (event->key() == Qt::Key_Control) {
422 if (FCtrlIsPressed > 0)
423 --FCtrlIsPressed;
424
425 if (FCtrlIsPressed == 0 &&
426 (FInteraction == iaNone || FInteraction == iaCrop || FInteraction == iaSpotRepair))
427 {
428 this->setCursor(Qt::ArrowCursor);
429 }
430 } else {
431 event->ignore(); // necessary to forward unhandled keys to main window
432 }
433
434 if (FInteraction != iaNone) {
435 emit keyChanged(event);
436 }
437 }
438
439 //==============================================================================
440 // The two functions are necessary to enable d&d over the view window.
441 // The actual d&d action is handled by the resp. main window events.
442
dragEnterEvent(QDragEnterEvent * event)443 void ptViewWindow::dragEnterEvent(QDragEnterEvent* event) {
444 event->ignore();
445 }
446
dropEvent(QDropEvent * event)447 void ptViewWindow::dropEvent(QDropEvent* event) {
448 event->ignore();
449 }
450
451 //==============================================================================
452
resizeEvent(QResizeEvent * event)453 void ptViewWindow::resizeEvent(QResizeEvent* event) {
454 if (Settings->GetInt("ZoomMode") == ptZoomMode_Fit) {
455 event->accept();
456 ZoomToFit(0);
457 } else {
458 // takes care of positioning the scene inside the viewport on non-fit zooms
459 QGraphicsView::resizeEvent(event);
460 }
461 }
462
463 //==============================================================================
464
465 // Top left corner overlay for the processing status
ShowStatus(short mode)466 void ptViewWindow::ShowStatus(short mode) {
467 switch (mode) {
468 case ptStatus_Done:
469 FStatusOverlay->setColors(QColor(0,130,0), QColor(120,170,120)); // green
470 FStatusOverlay->setDuration(1500);
471 FStatusOverlay->exec(QObject::tr("Done"));
472 break;
473
474 case ptStatus_Updating:
475 FStatusOverlay->setColors(QColor(255,140,0), QColor(255,200,120)); // orange
476 FStatusOverlay->setDuration(0);
477 FStatusOverlay->exec(QObject::tr("Updating"));
478 break;
479
480 case ptStatus_Processing:
481 FStatusOverlay->setColors(QColor(255,75,75), QColor(255,190,190)); // red
482 FStatusOverlay->setDuration(0);
483 FStatusOverlay->exec(QObject::tr("Processing"));
484 break;
485
486 default: // should not happen
487 FStatusOverlay->stop();
488 break;
489 }
490 }
491
492 //==============================================================================
493
ShowStatus(const QString text)494 void ptViewWindow::ShowStatus(const QString text) {
495 FStatusOverlay->setColors(QColor(75,150,255), QColor(190,220,255)); // blue
496 FStatusOverlay->setDuration(1500);
497 FStatusOverlay->exec(text);
498 }
499
500 //==============================================================================
501
502 // Start draw line interaction to determine rotation angle.
StartLine()503 void ptViewWindow::StartLine() {
504 if (FInteraction == iaNone) {
505 FDrawLine = new ptLineInteraction(this);
506 connect(FDrawLine, SIGNAL(finished(ptStatus)), this, SLOT(finishInteraction(ptStatus)));
507 connect(this, SIGNAL(mouseChanged(QMouseEvent*)), FDrawLine, SLOT(mouseAction(QMouseEvent*)));
508 connect(this, SIGNAL(keyChanged(QKeyEvent*)), FDrawLine, SLOT(keyAction(QKeyEvent*)));
509 FInteraction = iaDrawLine;
510 FCurrentInteraction = FDrawLine;
511 }
512 }
513
514 //==============================================================================
515
516 // Start simple selection interaction for spot WB and histogram "crop".
StartSimpleRect(void (* CB_SimpleRect)(const ptStatus,QRect))517 void ptViewWindow::StartSimpleRect(void (*CB_SimpleRect)(const ptStatus, QRect)) {
518 if (FInteraction == iaNone) {
519 assert(CB_SimpleRect != NULL);
520 FCB_SimpleRect = CB_SimpleRect;
521 FSelectRect = new ptSimpleRectInteraction(this);
522
523 connect(FSelectRect, SIGNAL(finished(ptStatus)),
524 this, SLOT (finishInteraction(ptStatus)));
525 connect(this, SIGNAL(mouseChanged(QMouseEvent*)),
526 FSelectRect, SLOT (mouseAction(QMouseEvent*)));
527 connect(this, SIGNAL(keyChanged(QKeyEvent*)),
528 FSelectRect, SLOT (keyAction(QKeyEvent*)));
529
530 FInteraction = iaSelectRect;
531 FCurrentInteraction = FSelectRect;
532 }
533 }
534
535 //==============================================================================
536
StartCrop()537 void ptViewWindow::StartCrop()
538 {
539 if (FInteraction != iaNone) {
540 return;
541 }
542
543 // Get crop rect values from settings. The bit shift converts from 1:1 to
544 // current pipe size.
545 int x = Settings->GetInt("CropX") >> Settings->GetInt("Scaled");
546 int y = Settings->GetInt("CropY") >> Settings->GetInt("Scaled");
547 int width = Settings->GetInt("CropW") >> Settings->GetInt("Scaled");
548 int height = Settings->GetInt("CropH") >> Settings->GetInt("Scaled");
549
550 // If any value is outside the allowed range reset the inital crop
551 // rectangle to the whole image.
552 if(x < 0 || x >= F8bitImageItem->pixmap().width() ||
553 y < 0 || y >= F8bitImageItem->pixmap().height() ||
554 width <= 0 || width > F8bitImageItem->pixmap().width() ||
555 height <= 0 || height > F8bitImageItem->pixmap().height() )
556 {
557 x = 0;
558 y = 0;
559 width = F8bitImageItem->pixmap().width();
560 height = F8bitImageItem->pixmap().height();
561 }
562
563
564 FCrop = new ptRichRectInteraction(this, x, y, width, height,
565 Settings->GetInt("FixedAspectRatio"),
566 Settings->GetInt("AspectRatioW"),
567 Settings->GetInt("AspectRatioH"),
568 Settings->GetInt("CropGuidelines") );
569
570 connect(FCrop, SIGNAL(finished(ptStatus)),
571 this, SLOT (finishInteraction(ptStatus)));
572 connect(this, SIGNAL(mouseChanged(QMouseEvent*)),
573 FCrop, SLOT (mouseAction(QMouseEvent*)));
574 connect(this, SIGNAL(keyChanged(QKeyEvent*)),
575 FCrop, SLOT (keyAction(QKeyEvent*)));
576
577 FInteraction = iaCrop;
578 FCurrentInteraction = FCrop;
579 }
580
581 //==============================================================================
582
StartLocalAdjust(std::function<void ()> ACleanupFunc)583 void ptViewWindow::StartLocalAdjust(std::function<void()> ACleanupFunc) {
584 if (FInteraction != iaNone) {
585 return;
586 }
587 FSpotTuning = new ptSpotInteraction(this);
588
589 connect(FSpotTuning, SIGNAL(finished(ptStatus)),
590 this, SLOT (finishInteraction(ptStatus)));
591 connect(this, SIGNAL(mouseChanged(QMouseEvent*)),
592 FSpotTuning, SLOT (mouseAction(QMouseEvent*)));
593
594 FInteraction = iaSpotTuning;
595 FSpotTuningCleanupFunc = ACleanupFunc;
596 FCurrentInteraction = FSpotTuning;
597 }
598
599 //==============================================================================
600
601 void RotateAngleDetermined(const ptStatus ExitStatus, double RotateAngle);
602 void CleanupAfterCrop(const ptStatus CropStatus, const QRect CropRect);
603
finishInteraction(ptStatus ExitStatus)604 void ptViewWindow::finishInteraction(ptStatus ExitStatus) {
605 switch (FInteraction) {
606 case iaDrawLine: {
607 double Angle = FDrawLine->angle();
608 DelAndNull(FDrawLine); // also disconnects all signals/slots
609 FInteraction = iaNone;
610 RotateAngleDetermined(ExitStatus, Angle);
611 break;
612 }
613
614 case iaSelectRect: {
615 QRect sr = FSelectRect->rect();
616 DelAndNull(FSelectRect); // also disconnects all signals/slots
617 FInteraction = iaNone;
618 FCB_SimpleRect(ExitStatus, sr);
619 break;
620 }
621
622 case iaCrop: {
623 QRect cr = FCrop->rect();
624 DelAndNull(FCrop); // also disconnects all signals/slots
625 FInteraction = iaNone;
626 CleanupAfterCrop(ExitStatus, cr);
627 break;
628 }
629
630 case iaSpotTuning: {
631 DelAndNull(FSpotTuning);
632 FInteraction = iaNone;
633 FSpotTuningCleanupFunc();
634 break;
635 }
636
637 default:
638 assert(!"Unknown FInteraction");
639 break;
640 }
641
642 FCurrentInteraction = nullptr;
643 }
644
645 //==============================================================================
646
647 // Convenience function to keep constructor short. Is only called once from the
648 // constructor.
ConstructContextMenu()649 void ptViewWindow::ConstructContextMenu() {
650 // Create actions for context menu
651
652 ac_Copy = new QAction(tr("Copy settings") + "\t" + tr("Ctrl+Shift+C"), this);
653 connect(ac_Copy, SIGNAL(triggered()), this, SLOT(Menu_Copy()));
654
655 ac_Paste = new QAction(tr("Paste settings") + "\t" + tr("Ctrl+Shift+V"), this);
656 connect(ac_Paste, SIGNAL(triggered()), this, SLOT(Menu_Paste()));
657
658 ac_Reset = new QAction(tr("Reset settings") + "\t" + tr("Ctrl+Shift+R"), this);
659 connect(ac_Reset, SIGNAL(triggered()), this, SLOT(Menu_Reset()));
660
661 ac_UserReset = new QAction(tr("Reset settings to last saved") + "\t" + tr("Ctrl+Shift+U"), this);
662 connect(ac_UserReset, SIGNAL(triggered()), this, SLOT(Menu_UserReset()));
663
664 ac_ZoomIn = new QAction(tr("Zoom &in") + "\t" + tr("1"), this);
665 ac_ZoomIn->setIcon(QIcon(QString::fromUtf8(":/dark/icons/zoom-in.png")));
666 connect(ac_ZoomIn, SIGNAL(triggered()), this, SLOT(Menu_ZoomIn()));
667
668 ac_Zoom100 = new QAction(tr("Zoom &100%") + "\t" + tr("2"), this);
669 connect(ac_Zoom100, SIGNAL(triggered()), this, SLOT(Menu_Zoom100()));
670 ac_Zoom100->setIcon(QIcon(QString::fromUtf8(":/dark/icons/zoom-original.png")));
671
672 ac_ZoomOut = new QAction(tr("Zoom &out") + "\t" + tr("3"), this);
673 ac_ZoomOut->setIcon(QIcon(QString::fromUtf8(":/dark/icons/zoom-out.png")));
674 connect(ac_ZoomOut, SIGNAL(triggered()), this, SLOT(Menu_ZoomOut()));
675
676 ac_ZoomFit = new QAction(tr("Zoom &fit") + "\t" + tr("4"), this);
677 connect(ac_ZoomFit, SIGNAL(triggered()), this, SLOT(Menu_ZoomFit()));
678 ac_ZoomFit->setIcon(QIcon(QString::fromUtf8(":/dark/icons/zoom-fit.png")));
679
680
681 ac_Mode_RGB = new QAction(tr("&RGB") + "\t" + tr("0"), this);
682 ac_Mode_RGB->setCheckable(true);
683 connect(ac_Mode_RGB, SIGNAL(triggered()), this, SLOT(Menu_Mode()));
684
685 ac_Mode_Structure = new QAction(tr("&Structure") + "\t" + tr("9"), this);
686 ac_Mode_Structure->setCheckable(true);
687 connect(ac_Mode_Structure, SIGNAL(triggered()), this, SLOT(Menu_Mode()));
688
689 ac_Mode_L = new QAction(tr("&L*") + "\t" + tr("8"), this);
690 ac_Mode_L->setCheckable(true);
691 connect(ac_Mode_L, SIGNAL(triggered()), this, SLOT(Menu_Mode()));
692
693 ac_Mode_A = new QAction(tr("&a*") + "\t" + tr("7"), this);
694 ac_Mode_A->setCheckable(true);
695 connect(ac_Mode_A, SIGNAL(triggered()), this, SLOT(Menu_Mode()));
696
697 ac_Mode_B = new QAction(tr("&b*") + "\t" + tr("6"), this);
698 ac_Mode_B->setCheckable(true);
699 connect(ac_Mode_B, SIGNAL(triggered()), this, SLOT(Menu_Mode()));
700
701 ac_Mode_Gradient = new QAction(tr("&Gradient") + "\t" + tr("5"), this);
702 ac_Mode_Gradient->setCheckable(true);
703 connect(ac_Mode_Gradient, SIGNAL(triggered()), this, SLOT(Menu_Mode()));
704
705 ac_ModeGroup = new QActionGroup(this);
706 ac_ModeGroup->addAction(ac_Mode_RGB);
707 ac_ModeGroup->addAction(ac_Mode_L);
708 ac_ModeGroup->addAction(ac_Mode_A);
709 ac_ModeGroup->addAction(ac_Mode_B);
710 ac_ModeGroup->addAction(ac_Mode_Gradient);
711 ac_ModeGroup->addAction(ac_Mode_Structure);
712
713 ac_PRead_None = new QAction(tr("&disabled"), this);
714 ac_PRead_None->setCheckable(true);
715 connect(ac_PRead_None, SIGNAL(triggered()), this, SLOT(Menu_PixelReading()));
716
717 ac_PRead_Linear = new QAction(tr("&linear"), this);
718 ac_PRead_Linear->setCheckable(true);
719 connect(ac_PRead_Linear, SIGNAL(triggered()), this, SLOT(Menu_PixelReading()));
720
721 ac_PRead_Preview = new QAction(tr("&preview"), this);
722 ac_PRead_Preview->setCheckable(true);
723 connect(ac_PRead_Preview, SIGNAL(triggered()), this, SLOT(Menu_PixelReading()));
724
725 ac_PReadGroup = new QActionGroup(this);
726 ac_PReadGroup->addAction(ac_PRead_None);
727 ac_PReadGroup->addAction(ac_PRead_Linear);
728 ac_PReadGroup->addAction(ac_PRead_Preview);
729 switch (Settings->GetInt("PixelReader")) {
730 case (int)prLinear: ac_PRead_Linear->setChecked(true); break;
731 case (int)prPreview: ac_PRead_Preview->setChecked(true); break;
732 default: ac_PRead_None->setChecked(true);
733 };
734 Menu_PixelReading();
735
736 ac_Clip_Indicate = new QAction(tr("Highlight &clipped pixels") + "\t" + tr("C"), this);
737 ac_Clip_Indicate->setCheckable(true);
738 ac_Clip_Indicate->setChecked(Settings->GetInt("ExposureIndicator"));
739 connect(ac_Clip_Indicate, SIGNAL(triggered()), this, SLOT(Menu_Clip_Indicate()));
740
741 ac_Clip_Over = new QAction(tr("&Over exposure"), this);
742 ac_Clip_Over->setCheckable(true);
743 ac_Clip_Over->setChecked(Settings->GetInt("ExposureIndicatorOver"));
744 connect(ac_Clip_Over, SIGNAL(triggered()), this, SLOT(Menu_Clip_Over()));
745
746 ac_Clip_Under = new QAction(tr("&Under exposure"), this);
747 ac_Clip_Under->setCheckable(true);
748 ac_Clip_Under->setChecked(Settings->GetInt("ExposureIndicatorUnder"));
749 connect(ac_Clip_Under, SIGNAL(triggered()), this, SLOT(Menu_Clip_Under()));
750
751 ac_Clip_R = new QAction(tr("&R"), this);
752 ac_Clip_R->setCheckable(true);
753 ac_Clip_R->setChecked(Settings->GetInt("ExposureIndicatorR"));
754 connect(ac_Clip_R, SIGNAL(triggered()), this, SLOT(Menu_Clip_R()));
755
756 ac_Clip_G = new QAction(tr("&G"), this);
757 ac_Clip_G->setCheckable(true);
758 ac_Clip_G->setChecked(Settings->GetInt("ExposureIndicatorG"));
759 connect(ac_Clip_G, SIGNAL(triggered()), this, SLOT(Menu_Clip_G()));
760
761 ac_Clip_B = new QAction(tr("&B"), this);
762 ac_Clip_B->setCheckable(true);
763 ac_Clip_B->setChecked(Settings->GetInt("ExposureIndicatorB"));
764 connect(ac_Clip_B, SIGNAL(triggered()), this, SLOT(Menu_Clip_B()));
765
766 ac_SensorClip = new QAction(tr("&Sensor"), this);
767 ac_SensorClip->setCheckable(true);
768 ac_SensorClip->setChecked(Settings->GetInt("ExposureIndicatorSensor"));
769 connect(ac_SensorClip, SIGNAL(triggered()), this, SLOT(Menu_SensorClip()));
770 ac_SensorClipSep = new QAction(this);
771 ac_SensorClipSep->setSeparator(true);
772
773 ac_ShowZoomBar = new QAction(tr("Show &bottom bar"), this);
774 ac_ShowZoomBar->setCheckable(true);
775 ac_ShowZoomBar->setChecked(Settings->GetInt("ShowBottomContainer"));
776 connect(ac_ShowZoomBar, SIGNAL(triggered()), this, SLOT(Menu_ShowZoomBar()));
777
778 ac_ShowTools = new QAction(tr("Show &tool pane") + "\t" + tr("Space"), this);
779 ac_ShowTools->setCheckable(true);
780 ac_ShowTools->setChecked(Settings->GetInt("ShowToolContainer"));
781 connect(ac_ShowTools, SIGNAL(triggered()), this, SLOT(Menu_ShowTools()));
782
783 #ifndef PT_WITHOUT_FILEMGR
784 ac_OpenFileMgr = new QAction(tr("Open file m&anager") + "\t" + tr("Ctrl+M"), this);
785 connect(ac_OpenFileMgr, SIGNAL(triggered()), this, SLOT(Menu_OpenFileMgr()));
786 #endif
787
788 ac_OpenBatch = new QAction(tr("Open &batch processing") + "\t" + tr("Ctrl+B"), this);
789 connect(ac_OpenBatch, SIGNAL(triggered()), this, SLOT(Menu_OpenBatch()));
790
791 ac_Fullscreen = new QAction(tr("Full&screen") + "\t" + tr("F11"), this);
792 ac_Fullscreen->setCheckable(true);
793 ac_Fullscreen->setChecked(0);
794 connect(ac_Fullscreen, SIGNAL(triggered()), this, SLOT(Menu_Fullscreen()));
795 }
796
797 //==============================================================================
798
contextMenuEvent(QContextMenuEvent * event)799 void ptViewWindow::contextMenuEvent(QContextMenuEvent* event) {
800 if (FInteraction == iaSelectRect || FInteraction == iaDrawLine) {
801 return;
802 }
803
804 // Create the menus themselves
805 // Note: Menus cannot be created with new. That breaks the theming.
806 QMenu Menu_Mode(tr("Display &mode"), this);
807 Menu_Mode.setPalette(Theme->menuPalette());
808 Menu_Mode.setStyle(Theme->style());
809 Menu_Mode.addAction(ac_Mode_RGB);
810 Menu_Mode.addAction(ac_Mode_Structure);
811 Menu_Mode.addAction(ac_Mode_L);
812 Menu_Mode.addAction(ac_Mode_A);
813 Menu_Mode.addAction(ac_Mode_B);
814 Menu_Mode.addAction(ac_Mode_Gradient);
815
816 QMenu Menu_Clip(tr("Show &clipping"), this);
817 Menu_Clip.setPalette(Theme->menuPalette());
818 Menu_Clip.setStyle(Theme->style());
819 Menu_Clip.addAction(ac_Clip_Indicate);
820 Menu_Clip.addSeparator();
821 Menu_Clip.addAction(ac_Clip_Over);
822 Menu_Clip.addAction(ac_Clip_Under);
823 Menu_Clip.addSeparator();
824 Menu_Clip.addAction(ac_Clip_R);
825 Menu_Clip.addAction(ac_Clip_G);
826 Menu_Clip.addAction(ac_Clip_B);
827
828 QMenu Menu_PRead(tr("Pixel values"), this);
829 Menu_PRead.setPalette(Theme->menuPalette());
830 Menu_PRead.setStyle(Theme->style());
831 Menu_PRead.addAction(ac_PRead_None);
832 Menu_PRead.addAction(ac_PRead_Linear);
833 Menu_PRead.addAction(ac_PRead_Preview);
834
835 QMenu Menu(this);
836 Menu.setPalette(Theme->menuPalette());
837 Menu.setStyle(Theme->style());
838 Menu.addAction(ac_Copy);
839 Menu.addAction(ac_Paste);
840 Menu.addAction(ac_Reset);
841 Menu.addAction(ac_UserReset);
842 Menu.addSeparator();
843 Menu.addAction(ac_ZoomIn);
844 Menu.addAction(ac_Zoom100);
845 Menu.addAction(ac_ZoomOut);
846 Menu.addAction(ac_ZoomFit);
847 Menu.addSeparator();
848 if (FInteraction == iaNone) {
849 Menu.addMenu(&Menu_Mode);
850 Menu.addMenu(&Menu_Clip);
851 Menu.addMenu(&Menu_PRead);
852 Menu.addSeparator();
853 Menu.addAction(ac_SensorClip);
854 Menu.addAction(ac_SensorClipSep);
855 }
856 Menu.addAction(ac_ShowTools);
857 Menu.addAction(ac_ShowZoomBar);
858 #ifndef PT_WITHOUT_FILEMGR
859 Menu.addAction(ac_OpenFileMgr);
860 #endif
861 Menu.addAction(ac_OpenBatch);
862 Menu.addSeparator();
863 Menu.addAction(ac_Fullscreen);
864
865 if (Settings->GetInt("SpecialPreview") == ptSpecialPreview_Structure) {
866 ac_Mode_Structure->setChecked(true);
867 } else if (Settings->GetInt("SpecialPreview") == ptSpecialPreview_Gradient) {
868 ac_Mode_Gradient->setChecked(true);
869 } else if (Settings->GetInt("SpecialPreview") == ptSpecialPreview_L) {
870 ac_Mode_L->setChecked(true);
871 } else if (Settings->GetInt("SpecialPreview") == ptSpecialPreview_A) {
872 ac_Mode_A->setChecked(true);
873 } else if (Settings->GetInt("SpecialPreview") == ptSpecialPreview_B) {
874 ac_Mode_B->setChecked(true);
875 } else {
876 ac_Mode_RGB->setChecked(true);
877 }
878
879 ac_ShowTools->setChecked(Settings->GetInt("ShowToolContainer"));
880 ac_ShowZoomBar->setChecked(Settings->GetInt("ShowBottomContainer"));
881 ac_Fullscreen->setChecked(Settings->GetInt("FullscreenActive"));
882 ac_Clip_Indicate->setChecked(Settings->GetInt("ExposureIndicator"));
883
884 if (Settings->GetInt("ShowExposureIndicatorSensor") == 1) {
885 ac_SensorClip->setEnabled(Settings->GetInt("ShowExposureIndicatorSensor"));
886 ac_SensorClip->setVisible(true);
887 ac_SensorClipSep->setVisible(true);
888 } else {
889 ac_SensorClip->setVisible(false);
890 ac_SensorClipSep->setVisible(false);
891 }
892
893 Menu.exec(event->globalPos());
894 }
895
896 //==============================================================================
897 // slots for the context menu
898
899 void Update(short Phase, short SubPhase = -1, short WithIdentify = 1, short ProcessorMode = ptProcessorMode_Preview);
Menu_Clip_Indicate()900 void ptViewWindow::Menu_Clip_Indicate() {
901 Settings->SetValue("ExposureIndicator",(int)ac_Clip_Indicate->isChecked());
902 Update(ptProcessorPhase_Preview);
903 }
904
Menu_Clip_Over()905 void ptViewWindow::Menu_Clip_Over() {
906 Settings->SetValue("ExposureIndicatorOver",(int)ac_Clip_Over->isChecked());
907 if (Settings->GetInt("ExposureIndicator"))
908 Update(ptProcessorPhase_Preview);
909 }
910
Menu_Clip_Under()911 void ptViewWindow::Menu_Clip_Under() {
912 Settings->SetValue("ExposureIndicatorUnder",(int)ac_Clip_Under->isChecked());
913 if (Settings->GetInt("ExposureIndicator"))
914 Update(ptProcessorPhase_Preview);
915 }
916
Menu_Clip_R()917 void ptViewWindow::Menu_Clip_R() {
918 Settings->SetValue("ExposureIndicatorR",(int)ac_Clip_R->isChecked());
919 if (Settings->GetInt("ExposureIndicator"))
920 Update(ptProcessorPhase_Preview);
921 }
922
Menu_Clip_G()923 void ptViewWindow::Menu_Clip_G() {
924 Settings->SetValue("ExposureIndicatorG",(int)ac_Clip_G->isChecked());
925 if (Settings->GetInt("ExposureIndicator"))
926 Update(ptProcessorPhase_Preview);
927 }
928
Menu_Clip_B()929 void ptViewWindow::Menu_Clip_B() {
930 Settings->SetValue("ExposureIndicatorB",(int)ac_Clip_B->isChecked());
931 if (Settings->GetInt("ExposureIndicator"))
932 Update(ptProcessorPhase_Preview);
933 }
934
Menu_SensorClip()935 void ptViewWindow::Menu_SensorClip() {
936 Settings->SetValue("ExposureIndicatorSensor",(int)ac_SensorClip->isChecked());
937 Update(ptProcessorPhase_Preview);
938 }
939
Menu_ShowZoomBar()940 void ptViewWindow::Menu_ShowZoomBar() {
941 Settings->SetValue("ShowBottomContainer",(int)ac_ShowZoomBar->isChecked());
942 FMainWindow->UpdateSettings();
943 }
944
Menu_ShowTools()945 void ptViewWindow::Menu_ShowTools() {
946 Settings->SetValue("ShowToolContainer",(int)ac_ShowTools->isChecked());
947 FMainWindow->UpdateSettings();
948 }
949
Menu_OpenFileMgr()950 void ptViewWindow::Menu_OpenFileMgr() {
951 FCtrlIsPressed = 0;
952 emit openFileMgr();
953 }
954
Menu_OpenBatch()955 void ptViewWindow::Menu_OpenBatch() {
956 FCtrlIsPressed = 0;
957 emit openBatch();
958 }
959
960 void CB_FullScreenButton(const int State);
Menu_Fullscreen()961 void ptViewWindow::Menu_Fullscreen() {
962 CB_FullScreenButton((int)ac_Fullscreen->isChecked());
963 }
964
Menu_ZoomFit()965 void ptViewWindow::Menu_ZoomFit() {
966 ZoomToFit();
967 }
968
Menu_Zoom100()969 void ptViewWindow::Menu_Zoom100() {
970 ZoomTo(1.0);
971 }
972
Menu_ZoomIn()973 void ptViewWindow::Menu_ZoomIn() {
974 ZoomStep(1);
975 }
976
Menu_ZoomOut()977 void ptViewWindow::Menu_ZoomOut() {
978 ZoomStep(-1);
979 }
980
Menu_Mode()981 void ptViewWindow::Menu_Mode() {
982 if (ac_Mode_RGB->isChecked())
983 Settings->SetValue("SpecialPreview", ptSpecialPreview_RGB);
984 else if (ac_Mode_L->isChecked())
985 Settings->SetValue("SpecialPreview", ptSpecialPreview_L);
986 else if (ac_Mode_A->isChecked())
987 Settings->SetValue("SpecialPreview", ptSpecialPreview_A);
988 else if (ac_Mode_B->isChecked())
989 Settings->SetValue("SpecialPreview", ptSpecialPreview_B);
990 else if (ac_Mode_Gradient->isChecked())
991 Settings->SetValue("SpecialPreview", ptSpecialPreview_Gradient);
992 else if (ac_Mode_Structure->isChecked())
993 Settings->SetValue("SpecialPreview", ptSpecialPreview_Structure);
994
995 Update(ptProcessorPhase_Preview);
996 }
997
Menu_PixelReading()998 void ptViewWindow::Menu_PixelReading() {
999 if (ac_PRead_None->isChecked()) {
1000 if (FPixelReader) FPixelReader(QPoint(), prNone);
1001 FPixelReading = prNone;
1002 }
1003 else if (ac_PRead_Linear->isChecked()) FPixelReading = prLinear;
1004 else if (ac_PRead_Preview->isChecked()) FPixelReading = prPreview;
1005
1006 Settings->SetValue("PixelReader", (int)FPixelReading);
1007 }
1008
1009 void ptCopySettingsToClipboard();
Menu_Copy()1010 void ptViewWindow::Menu_Copy() {
1011 ptCopySettingsToClipboard();
1012 }
1013
1014 void ptPasteSettingsFromClipboard();
Menu_Paste()1015 void ptViewWindow::Menu_Paste() {
1016 ptPasteSettingsFromClipboard();
1017 }
1018
1019 void ptResetSettingsToDefault();
Menu_Reset()1020 void ptViewWindow::Menu_Reset() {
1021 ptResetSettingsToDefault();
1022 }
1023
1024 void ptMakeFullUndo();
Menu_UserReset()1025 void ptViewWindow::Menu_UserReset() {
1026 ptMakeFullUndo();
1027 }
1028
1029 //==============================================================================
1030