forked from kleintom/Cstitch
-
Notifications
You must be signed in to change notification settings - Fork 0
/
imageCompareBase.h
464 lines (441 loc) · 19 KB
/
imageCompareBase.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
//
// Copyright 2010, 2011 Tom Klein.
//
// This file is part of cstitch.
//
// cstitch is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef IMAGECOMPAREBASE_H
#define IMAGECOMPAREBASE_H
#include <QtWidgets/QLabel>
#include "imageSaverWindow.h"
#include "utility.h"
#include "triC.h"
#include "imageContainer.h"
class comboBox;
class imageLabelBase;
class imageLabel;
class windowManager;
class leftRightAccessor;
class imageCompareBase;
class QScrollArea;
class QSplitter;
// a text label with knowledge of a "buddy" label and three states:
// off (disabled), on (enabled), and current (enabled, bold, and
// underlined). Only one of the two buddies can be current at a time.
// Each label in the pair has to call setBuddy to establish the buddy
// connection.
// Note: the label text will be appended with ':'
class stateLabel : public QLabel {
public:
explicit stateLabel(const QString& label) : label_(label) {
setText(label_ + ":");
setEnabled(false);
const QFontMetrics metric(font());
setFixedWidth(metric.boundingRect(label_ + ":").width());
}
// enable the label
void on() { setText(label_ + ":"); setEnabled(true); on_ = true; }
// disable the label
void off() { setText(label_ + ":"); setEnabled(false); on_ = false; }
// enable, underline, and bold the label
// if the buddy is currently cur, it will become just enabled, otherwise
// it stays as is
void cur() {
setEnabled(true); on_ = true;
setText("<b><u><font color='green'>" + label_ + "</font></u>:</b>");
buddy_->buddyIsCur();
}
// save a pointer to the buddy label for this label
void setBuddy(stateLabel* buddyLabel) { buddy_ = buddyLabel; }
// called for notification that this label's buddy is now cur, i.e.
// we're not cur, our buddy is
// we drop to simply enabled if we were cur
void buddyIsCur() { if ( on_ ) { on(); } }
private:
QString label_;
// we're on if we're on() or cur(); there's no way to tell which one
// we're in
bool on_;
stateLabel* buddy_;
};
//// As a protest against C++ function pointer yuckiness, I've decided to
//// cut off my nose to spite my face (or something like that)
class zoomHelperFunction {
public:
explicit zoomHelperFunction(imageCompareBase* parent) : parent_(parent) {}
virtual void operator()(const leftRightAccessor& lra,
const QSize& scrollSize) const = 0;
protected:
imageCompareBase* parent() const { return parent_; }
private:
imageCompareBase* parent_;
};
class zoomToWidthHelperFunction : public zoomHelperFunction {
public:
explicit zoomToWidthHelperFunction(imageCompareBase* parent)
: zoomHelperFunction(parent) {}
inline void operator()(const leftRightAccessor& lra,
const QSize& scrollSize) const;
};
class zoomToHeightHelperFunction : public zoomHelperFunction {
public:
explicit zoomToHeightHelperFunction(imageCompareBase* parent)
: zoomHelperFunction(parent) {}
inline void operator()(const leftRightAccessor& lra,
const QSize& scrollSize) const;
};
class zoomToImageHelperFunction : public zoomHelperFunction {
public:
explicit zoomToImageHelperFunction(imageCompareBase* parent)
: zoomHelperFunction(parent) {}
inline void operator()(const leftRightAccessor& lra,
const QSize& scrollSize) const;
};
class zoomToOriginalHelperFunction : public zoomHelperFunction {
public:
explicit zoomToOriginalHelperFunction(imageCompareBase* parent)
: zoomHelperFunction(parent) {}
inline void operator()(const leftRightAccessor& lra,
const QSize& scrollSize) const;
};
// class imageCompareBase
//
// imageCompareBase provides common functionality for the widgets that
// provide side by side image splitter capabilities (colorCompare and
// squareWindow). One or two images can be viewed at a time. The two
// sides can never show the same image at once. The user is provided
// a left and a right image menu from which to choose the image to show,
// and to hide a given side or delete the image in a given side. Images
// can also be chosen from pull down combo boxes on the toolbar.
//
// Several user-generated actions are context dependent (originalSize(),
// for example, but see derived functionality for many other examples).
// Context is either left side or right side, and is determined (usually)
// by a mouse click on the desired side or clicking a left or right side
// button on the toolbar or by shift-left or -right arrow. Current
// context is indicated by a black frame around the current side as well
// as via a highlighted "L:" or "R:" on the toolbar.
//
// By default, both sides scroll and zoom together so that each side
// is showing the same part of the image at all times (up to scaling),
// but the user can turn off dual scrolling and/or zooming from the Image
// menu.
//
////
// Implementation Notes
//
// The sides come from a QSplitter, which has two sides, each holding a
// scroll area. Each scroll area holds an imageLabel, which maintains
// the actual image at the current scale.
// This class tracks which image is on which side using leftImage_,
// rightImage_, and curImage_ - curImage_ points to either leftImage_ or
// rightImage_, whichever is the active image.
//
// After construction there is always a curImage_.
// after construction, there is always a leftImage_ and a rightImage_
// (with the exception that if all images except original are deleted
// then there is only a left or a right, but the widget is not visible,
// and will not become visible again until a new image is added).
//
// The image pointers actually point to imageContainers which contain
// extra information and functionality for the image. Since the desired
// functionality depends on what type of image is being stored (and
// access to that functionality requires knowledge of the type), the
// image container type should have been templated into this class.
// Unfortunately Qt does not support templated QObject classes
// http://doc.trolltech.com/qq/qq15-academic.html
// The workaround used here is as follows:
//
// 1) This class provides virtual pointer-to-image-container
// setters which MUST be used to assign leftImage_, rightImage_,
// and curImage_ so that derived classes can handle such changes
// as appropriate.
//
// 2) This class NEVER creates its own imageContainers - only derived
// classes create imageContainers (so that they can actually use
// derivations of imageContainers).
//
// 3) The same situation applies to left and right imageLabels (users of
// this class will want extra functionality for their labels depending on
// what type of image it is they're putting in their label). The labels
// are only created at construction time, BY DERIVED CLASSES, so this is
// less of an issue than with imageContainers.
//
// In practice, colorCompare actually does use imageContainers and
// imageLabels, while squareWindow uses squareImageContainers and
// squareImageLabels, which are derived types.
//
// All of the left and right processing code is identical, so to avoid
// code duplication for left and right functions, left and right
// processor functions simply create a leftRightAccessor object for the
// given side which hides the explicit side but provides access to any
// desired pointers for that side, and then pass the accessor and other
// required non-side-specific information to a common processing function
// for that path (see, for example, hideLeft and hideRight, which both
// call hideLR ("LR" for Left/Right) to do the actual processing).
//
class imageCompareBase : public imageSaverWindow {
// the accessor classes (only leftRightAccessor is directly used here)
// simply provide access to "left" or "right" pointers (such as
// leftImage, leftLabel, leftScroll, etc.) without their user needing
// to know which side they actually came from
friend class baseAccessor;
friend class leftAccessor;
friend class rightAccessor;
friend class leftRightAccessor;
// implementing polymorphic functors for calling private member functions
// requires friendship
friend class zoomToOriginalHelperFunction;
friend class zoomToWidthHelperFunction;
friend class zoomToHeightHelperFunction;
friend class zoomToImageHelperFunction;
Q_OBJECT
protected:
// the active scroll area gets an extra highlight frame of width
// ACTIVE_LINE_WIDTH
enum {ACTIVE_LINE_WIDTH = 3, INACTIVE_LINE_WIDTH = 0};
// a "left" or "right" enum for passing around sidedness
enum icLR {IC_LEFT, IC_RIGHT};
public:
explicit imageCompareBase(windowManager* windowMgr);
// delete the image with index <imageIndex>
void removeImage(int imageIndex);
protected:
// set the current image to <container> and update subwidgets to reflect
// the new active side
virtual void setCur(imagePtr container);
// update the active image label to view curImage's image
virtual void updateImageLabelImage() = 0;
virtual imageLabelBase* leftLabel() const = 0;
virtual imageLabelBase* rightLabel() const = 0;
virtual void setLeftImage(imagePtr container) = 0;
virtual imagePtr leftImage() const = 0;
virtual void setRightImage(imagePtr container) = 0;
virtual imagePtr rightImage() const = 0;
virtual void setCurImage(imagePtr container) = 0;
virtual imagePtr curImage() const = 0;
// return a list of all current images
QList<imagePtr> images() const;
QScrollArea* leftScroll() const { return leftScroll_; }
QScrollArea* rightScroll() const { return rightScroll_; }
// add an image to the left image menu
void addLeftImageMenuAction(QAction* action);
// add an image to the right image menu
void addRightImageMenuAction(QAction* action);
// if <b> then enable actions that operate on both sides of the splitter
void bothScrollsVisible(bool b);
// update the left and right image menus to reflect the current left
// and right images - the left image appears grayed out on the right
// menu and vice versa
// may update show/hide/delete options as well
void updateImageLists();
// set the left side image to Original and make it cur
void setLeftToOriginalImage();
imageLabelBase* activeLabel() {
return (curImage() == leftImage()) ? leftLabel() : rightLabel();
}
imageLabelBase* inactiveLabel() {
return (curImage() == leftImage()) ? rightLabel() : leftLabel();
}
QScrollArea* activeScroll() {
return (curImage() == leftImage()) ? leftScroll_ : rightScroll_;
}
virtual bool eventFilter(QObject* watched, QEvent* event);
virtual bool horizontalWheelScrollEvent(QObject* watched,
QWheelEvent* event) const;
virtual void keyPressEvent(QKeyEvent* event);
virtual bool filterKeyEvent(QKeyEvent* event);
// add the left and right toolbar image boxes and labels
// intended for use by derived classes
void addLeftRightToolBarBoxes();
// add the left and right toolbar focus buttons
// intended for use by derived classes
void addLeftRightFocusButtons();
// return the imageContainer corresponding to the given <imageName>;
// returns NULL if image isn't found
imagePtr getImageFromName(const QString& imageName) const;
// return the imageContainer corresponding to the given <index>,
// where <index> is as in the image name "Image <index>" or the original
// image if index is 0;
// returns NULL if image isn't found
imagePtr getImageFromIndex(int index) const;
// return the scroll value for a scroll with max <newMax> corresponding
// to a scroll with <oldMax> and <oldValue>
int convertScrollValue(int oldMax, int newMax, int oldValue) {
return qRound(qreal(newMax)/oldMax * oldValue); // newValue
}
//// base methods for both Left and Right slot processing
// delete the image referenced by <lra> and replace it with another image
// (or notify windowManager if no other is available)
void deleteLR(const leftRightAccessor& lra);
void hideLR(const leftRightAccessor& lra);
void showLR(const leftRightAccessor& lra);
void focusLR(const leftRightAccessor& lra);
// process a selection of the image <name> by the user from an image
// list box
void processBoxLR(const leftRightAccessor& lra, const QString& name);
// process a selection of <action> by the user from an image menu list
void processImageMenuTriggerLR(QAction* action,
const leftRightAccessor& lra);
// update the image lists to reflect selection of lra.image()
void updateImageListsLR(const leftRightAccessor& lra);
// let it be known that the given image (index) has been deleted
virtual void imageDeleted(int imageIndex) = 0;
bool dualZoomingActive() const { return dualZoomingAction_->isChecked(); }
// zoom setting changes can change both sides, so update data that track
// both sides
void updateZoomValues() {
setSavedImageSize();
updateScrollValues();
}
// generic zoom to helper function that implements common zoom
// to functionality using <helperFunction>
virtual void zoomToHelper(const zoomHelperFunction& helperFunction);
protected slots:
// process selection of <imageName> from the left image list box
void processLeftBox(const QString& imageName);
void processRightBox(const QString& imageName);
// process selection of <action> from the left image menu list
void processLeftImageMenuTrigger(QAction* action);
void processRightImageMenuTrigger(QAction* action);
void focusLeft();
void focusRight();
void showHideLeft(bool show);
void showHideRight(bool show);
void deleteLeft();
void deleteRight();
// switch the left and right images (if there are both left and right)
void switchSides();
// center the splitter so that the left and right images have the same
// width
void centerSplitter();
// zoom in or out on the image by the given pixel amount
virtual void zoom(int zoomIncrement);
// make the current image its original size
void originalSize();
void zoomToWidth();
void zoomToHeight();
void zoomToImage();
// do any processing required for a dual zoom setting change
virtual void updateDualZooming(bool dualZoomOn);
/* the following are for dual scrolling */
// the Left Horizontal Scroll Bar moved, so move the right as well
void processLHSBsliderMoved(int value);
// the Left Horizontal Scroll Bar value changed, so change the right as
// well
void processLHSBValue(int value);
void processLVSBsliderMoved(int value);
void processLVSBValue(int value);
void processRHSBsliderMoved(int value);
void processRHSBValue(int value);
void processRVSBsliderMoved(int value);
void processRVSBValue(int value);
// the dual scrolling checkbox has been changed
void processDualScrollingChange(bool dualScrollingOn);
private:
// construction helper
void constructScrolling();
// construction helper - setup signal/slot connections
void setConnections();
void setScrollingConnections(bool checked);
// return the scaled size of the current image
// implements imageSaverWindow::
QSize curImageViewSize() const;
// set the <label> and the <image> to <size>, and update anything that
// needs to know about the changes
void setScaledImageSize(const QSize size, imageLabelBase* label,
imagePtr image);
virtual void setScaledImageWidth(int width, imageLabelBase* label,
imagePtr image);
virtual void setScaledImageHeight(int height, imageLabelBase* label,
imagePtr image);
void originalSizeHelper(const leftRightAccessor& lra,
const QSize& scrollSize);
void zoomToWidthHelper(const leftRightAccessor& lra,
const QSize& scrollSize);
void zoomToHeightHelper(const leftRightAccessor& lra,
const QSize& scrollSize);
void zoomToImageHelper(const leftRightAccessor& lra,
const QSize& scrollSize);
imagePtr inactiveImage() {
return (curImage() == leftImage()) ? rightImage() : leftImage();
}
// save the sizes of the current left and right side images
void setSavedImageSize();
// return the stored last image size for the current side, using
// <newImage> to seed it if it hasn't already been set
QSize getSavedImageSize(const imagePtr newImage);
// let it be known that the only image left is the original
// this MUST result in this window being hidden until a new image gets
// added
virtual void imageListEmpty() = 0;
// delete the menu actions associated with the given image and call
// imageDeleted to notify super classes
void deleteImageActions(const QString& imageName);
// if we're doing dual scrolling then synchronize the scroll values
void updateScrollValues();
// implements imageZoomWindow::processFirstShow
void processFirstShow();
private:
QScrollArea* leftScroll_;
QScrollArea* rightScroll_;
QSplitter* splitter_;
// a label showing "L:" in the toolbar that indicates the state of the
// left side
stateLabel* LLabel_;
QMenu* leftImageMenu_;
// a combo box for the toolbar listing possible left images
comboBox* leftImageListBox_;
// checkable - checked means show
QAction* leftShowHide_;
QAction* leftDelete_;
// remember the size of the most current image on this side
QSize leftImageSavedSize_;
stateLabel* RLabel_;
QMenu* rightImageMenu_;
comboBox* rightImageListBox_;
QAction* rightShowHide_;
QAction* rightDelete_;
QSize rightImageSavedSize_;
// let the user switch the images
QAction* switchAction_;
// let the user equalize the image widths in the splitter
QAction* centerSplitterAction_;
// let the user turn on/off dual scrolling for left and right images
QAction* dualScrollingAction_;
// let the user turn on/off dual zooming for left and right iamges
QAction* dualZoomingAction_;
// let the user click a toolbar button to set focus to the left
QAction* leftFocusAction_;
QAction* rightFocusAction_;
};
void zoomToWidthHelperFunction::operator()(const leftRightAccessor& lra,
const QSize& scrollSize) const {
parent()->zoomToWidthHelper(lra, scrollSize);
}
void zoomToHeightHelperFunction::operator()(const leftRightAccessor& lra,
const QSize& scrollSize) const {
parent()->zoomToHeightHelper(lra, scrollSize);
}
void zoomToImageHelperFunction::operator()(const leftRightAccessor& lra,
const QSize& scrollSize) const {
parent()->zoomToImageHelper(lra, scrollSize);
}
void
zoomToOriginalHelperFunction::operator()(const leftRightAccessor& lra,
const QSize& scrollSize) const {
parent()->originalSizeHelper(lra, scrollSize);
}
#endif