1 
2 
3 #include "toonzqt/flipconsole.h"
4 
5 // TnzQt includes
6 #include "toonzqt/menubarcommand.h"
7 #include "toonzqt/dvscrollwidget.h"
8 #include "toonzqt/gutil.h"
9 #include "toonzqt/flipconsoleowner.h"
10 
11 // TnzLib includes
12 #include "toonz/preferences.h"
13 #include "toonz/tframehandle.h"
14 
15 // TnzBase includes
16 #include "tenv.h"
17 
18 // TnzCore includes
19 #include "tconvert.h"
20 #include "timagecache.h"
21 #include "trop.h"
22 
23 #include "../toonz/tapp.h"
24 
25 // Qt includes
26 #include <QVBoxLayout>
27 #include <QHBoxLayout>
28 #include <QToolBar>
29 #include <QLabel>
30 #include <QFrame>
31 #include <QSlider>
32 #include <QTimerEvent>
33 #include <QToolButton>
34 #include <QPainter>
35 #include <QMouseEvent>
36 #include <QIcon>
37 #include <QAction>
38 #include <QWidgetAction>
39 #include <QStyle>
40 #include <QStylePainter>
41 #include <QStyleOption>
42 #include <QStyleOptionFrameV3>
43 #include <QSettings>
44 #include <QPushButton>
45 #include <QScrollBar>
46 
47 using namespace DVGui;
48 
49 //==========================================================================================
50 //    Preliminary stuff - local namespace
51 //==========================================================================================
52 TEnv::IntVar FlipBookWhiteBgToggle("FlipBookWhiteBgToggle", 1);
53 TEnv::IntVar FlipBookBlackBgToggle("FlipBookBlackBgToggle", 0);
54 TEnv::IntVar FlipBookCheckBgToggle("FlipBookCheckBgToggle", 0);
55 namespace {
56 // Please refer to the "qss/standard/standard.qss" file for explanations of the
57 // following properties.
58 
59 int PBHeight;
60 
61 QImage PBOverlay;
62 QImage PBMarker;
63 
64 int PBColorMarginLeft   = 0;
65 int PBColorMarginTop    = 0;
66 int PBColorMarginRight  = 0;
67 int PBColorMarginBottom = 0;
68 
69 int PBMarkerMarginLeft  = 0;
70 int PBMarkerMarginRight = 0;
71 
72 QColor PBBaseColor       = QColor(235, 235, 235);
73 QColor PBNotStartedColor = QColor(210, 40, 40);
74 QColor PBStartedColor    = QColor(220, 160, 160);
75 QColor PBFinishedColor   = QColor(235, 235, 235);
76 }  // namespace
77 
78 //-----------------------------------------------------------------------------
79 
getPBHeight() const80 int FlipSlider::getPBHeight() const { return PBHeight; }
setPBHeight(int height)81 void FlipSlider::setPBHeight(int height) {
82   setFixedHeight(height);
83   PBHeight = height;
84 }
85 
getPBOverlay() const86 QImage FlipSlider::getPBOverlay() const { return PBOverlay; }
setPBOverlay(const QImage & img)87 void FlipSlider::setPBOverlay(const QImage &img) { PBOverlay = img; }
88 
getPBMarker() const89 QImage FlipSlider::getPBMarker() const { return PBMarker; }
setPBMarker(const QImage & img)90 void FlipSlider::setPBMarker(const QImage &img) { PBMarker = img; }
91 
getPBColorMarginLeft() const92 int FlipSlider::getPBColorMarginLeft() const { return PBColorMarginLeft; }
setPBColorMarginLeft(int margin)93 void FlipSlider::setPBColorMarginLeft(int margin) {
94   PBColorMarginLeft = margin;
95 }
96 
getPBColorMarginTop() const97 int FlipSlider::getPBColorMarginTop() const { return PBColorMarginTop; }
setPBColorMarginTop(int margin)98 void FlipSlider::setPBColorMarginTop(int margin) { PBColorMarginTop = margin; }
99 
getPBColorMarginRight() const100 int FlipSlider::getPBColorMarginRight() const { return PBColorMarginRight; }
setPBColorMarginRight(int margin)101 void FlipSlider::setPBColorMarginRight(int margin) {
102   PBColorMarginRight = margin;
103 }
104 
getPBColorMarginBottom() const105 int FlipSlider::getPBColorMarginBottom() const { return PBColorMarginBottom; }
setPBColorMarginBottom(int margin)106 void FlipSlider::setPBColorMarginBottom(int margin) {
107   PBColorMarginBottom = margin;
108 }
109 
getPBMarkerMarginLeft() const110 int FlipSlider::getPBMarkerMarginLeft() const { return PBMarkerMarginLeft; }
setPBMarkerMarginLeft(int margin)111 void FlipSlider::setPBMarkerMarginLeft(int margin) {
112   PBMarkerMarginLeft = margin;
113 }
114 
getPBMarkerMarginRight() const115 int FlipSlider::getPBMarkerMarginRight() const { return PBMarkerMarginRight; }
setPBMarkerMarginRight(int margin)116 void FlipSlider::setPBMarkerMarginRight(int margin) {
117   PBMarkerMarginRight = margin;
118 }
119 
getBaseColor() const120 QColor FlipSlider::getBaseColor() const { return PBBaseColor; }
setBaseColor(const QColor & color)121 void FlipSlider::setBaseColor(const QColor &color) { PBBaseColor = color; }
122 
getNotStartedColor() const123 QColor FlipSlider::getNotStartedColor() const { return PBNotStartedColor; }
setNotStartedColor(const QColor & color)124 void FlipSlider::setNotStartedColor(const QColor &color) {
125   PBNotStartedColor = color;
126 }
127 
getStartedColor() const128 QColor FlipSlider::getStartedColor() const { return PBStartedColor; }
setStartedColor(const QColor & color)129 void FlipSlider::setStartedColor(const QColor &color) {
130   PBStartedColor = color;
131 }
132 
getFinishedColor() const133 QColor FlipSlider::getFinishedColor() const { return PBFinishedColor; }
setFinishedColor(const QColor & color)134 void FlipSlider::setFinishedColor(const QColor &color) {
135   PBFinishedColor = color;
136 }
137 
138 FlipConsole *FlipConsole::m_currentConsole = 0;
139 QList<FlipConsole *> FlipConsole::m_visibleConsoles;
140 bool FlipConsole::m_isLinkedPlaying = false;
141 bool FlipConsole::m_areLinked       = false;
142 
143 //==========================================================================================
144 
PlaybackExecutor()145 PlaybackExecutor::PlaybackExecutor() : m_fps(25), m_abort(false) {}
146 
147 //-----------------------------------------------------------------------------
148 
resetFps(int fps)149 void PlaybackExecutor::resetFps(int fps) { m_fps = fps; }
150 
151 //-----------------------------------------------------------------------------
152 
run()153 void PlaybackExecutor::run() {
154   // (Daniele)
155   // We'll build the fps considering an interval of roughly 1 second (the last
156   // one).
157   // However, the fps should be sampled at a faster rate. Each sample is taken
158   // at
159   // 1/4 second, and the last 4 samples data are stored to keep trace of the
160   // last
161   // second of playback.
162 
163   TStopWatch timer;
164   timer.start();
165 
166   TUINT32 timeResolution =
167       250;  // Use a sufficient sampling resolution (currently 1/4 sec).
168             // Fps calculation is made once per sample.
169 
170   int fps = m_fps, currSample = 0;
171   TUINT32 playedFramesCount = 0;
172   TUINT32 loadedInstant, nextSampleInstant = timeResolution;
173   TUINT32 sampleTotalLoadingTime = 0;
174 
175   TUINT32 lastFrameCounts[4]    = {0, 0, 0,
176                                 0};  // Keep the last 4 'played frames' counts.
177   TUINT32 lastSampleInstants[4] = {0, 0, 0,
178                                    0};  // Same for the last sampling instants
179   TUINT32 lastLoadingTimes[4]   = {0, 0, 0,
180                                  0};  // Same for total sample loading times
181 
182   double targetFrameTime =
183       1000.0 / abs(m_fps);  // User-required time between frames
184 
185   TUINT32 emissionInstant = 0;  // Effective instant in which loading is invoked
186   double emissionInstantD = 0.0;  // Double precision version of the above
187 
188   double lastLoadingTime = 0.0;  // Mean frame loading time in the last sample
189 
190   while (!m_abort) {
191     emissionInstant = timer.getTotalTime();
192 
193     // Draw the next frame
194     if (playedFramesCount)
195       emit nextFrame(fps);  // Show the next frame, telling
196                             // currently measured fps
197 
198     if (FlipConsole::m_areLinked) {
199       // In case there are linked consoles, update them too.
200       // Their load time must be included in the fps calculation.
201       int i, consolesCount = FlipConsole::m_visibleConsoles.size();
202       for (i = 0; i < consolesCount; ++i) {
203         FlipConsole *console = FlipConsole::m_visibleConsoles.at(i);
204         if (console->isLinkable() && console != FlipConsole::m_currentConsole)
205           console->playbackExecutor().emitNextFrame(m_fps < 0 ? -fps : fps);
206       }
207     }
208 
209     //-------- Each nextFrame() blocks until the frame has been shown ---------
210 
211     ++playedFramesCount;
212     loadedInstant = timer.getTotalTime();
213     sampleTotalLoadingTime += (loadedInstant - emissionInstant);
214 
215     // Recalculate data only after the specified time resolution has passed.
216     if (loadedInstant > nextSampleInstant) {
217       // Sampling instant. Perform calculations.
218 
219       // Store values
220       TUINT32 framesCount = playedFramesCount - lastFrameCounts[currSample];
221       TUINT32 elapsedTime = loadedInstant - lastSampleInstants[currSample];
222       double loadingTime =
223           (sampleTotalLoadingTime - lastLoadingTimes[currSample]) /
224           (double)framesCount;
225 
226       lastFrameCounts[currSample]    = playedFramesCount;
227       lastSampleInstants[currSample] = loadedInstant;
228       lastLoadingTimes[currSample]   = sampleTotalLoadingTime;
229 
230       currSample        = (currSample + 1) % 4;
231       nextSampleInstant = loadedInstant + timeResolution;
232 
233       // Rebuild current fps
234       fps             = troundp((1000 * framesCount) / (double)elapsedTime);
235       targetFrameTime = 1000.0 / abs(m_fps);  // m_fps could have changed...
236 
237       // In case the playback is too slow to keep the required pace, reset the
238       // emission timeline.
239       // Otherwise, it should be kept as the difference needs to be compensated
240       // to get the required fps.
241       if ((int)emissionInstant - (int)emissionInstantD >
242           20)  // Reset beyond, say, 20 msecs tolerance.
243         emissionInstantD = (double)loadedInstant - loadingTime;
244       else
245         emissionInstantD +=
246             lastLoadingTime -
247             loadingTime;  // Otherwise, just adapt to the new loading time
248 
249       lastLoadingTime = loadingTime;
250     }
251 
252     // Calculate the new emission instant
253     emissionInstant = std::max((int)(emissionInstantD += targetFrameTime), 0);
254 
255     // Sleep until the next emission instant has been reached
256     while (timer.getTotalTime() < emissionInstant) msleep(1);
257   }
258 
259   m_abort = false;
260 }
261 
262 //==========================================================================================
263 
FlipSlider(QWidget * parent)264 FlipSlider::FlipSlider(QWidget *parent)
265     : QAbstractSlider(parent), m_enabled(false), m_progressBarStatus(0) {
266   setObjectName("FlipSlider");
267   setOrientation(Qt::Horizontal);
268   setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
269 }
270 
271 //-----------------------------------------------------------------------------
272 
paintEvent(QPaintEvent * ev)273 void FlipSlider::paintEvent(QPaintEvent *ev) {
274   QPainter p(this);
275 
276   // Draw the progress status colorbar
277   QRect sliderRect(QPoint(), size());
278   QRect colorRect(sliderRect.adjusted(PBMarkerMarginLeft, PBColorMarginTop,
279                                       -PBMarkerMarginRight,
280                                       -PBColorMarginBottom));
281 
282   int val, maxValuePlusStep = maximum() + singleStep();
283   int colorWidth = colorRect.width(), colorHeight = colorRect.height();
284 
285   p.setPen(Qt::NoPen);
286   int currPos = PBColorMarginLeft, nextPos;
287 
288   // paint the base of slider
289   if (m_enabled && m_progressBarStatus && !m_progressBarStatus->empty()) {
290     unsigned int i, pbStatusSize = m_progressBarStatus->size();
291     for (i = 0, val = minimum() + singleStep(); i < pbStatusSize;
292          ++i, val += singleStep()) {
293       nextPos = sliderPositionFromValue(minimum(), maxValuePlusStep, val,
294                                         colorWidth) +
295                 PBMarkerMarginLeft;
296       if (i == pbStatusSize - 1) nextPos += PBMarkerMarginRight;
297       p.fillRect(currPos, PBColorMarginTop, nextPos - currPos, colorHeight,
298                  ((*m_progressBarStatus)[i] == PBFrameStarted)
299                      ? PBStartedColor
300                      : ((*m_progressBarStatus)[i] == PBFrameFinished)
301                            ? PBFinishedColor
302                            : PBNotStartedColor);
303       currPos = nextPos;
304     }
305 
306     // Draw frames outside the pb
307     if (val < maximum())
308       p.fillRect(currPos, PBColorMarginTop,
309                  width() - PBColorMarginRight - currPos, colorHeight,
310                  PBNotStartedColor);
311   } else
312     p.fillRect(PBColorMarginLeft, PBColorMarginTop,
313                sliderRect.width() - PBColorMarginLeft - PBColorMarginRight,
314                colorHeight, PBBaseColor);
315 
316   // Draw the PB Overlay
317   int overlayInnerWidth =
318       PBOverlay.width() - PBColorMarginLeft - PBColorMarginRight;
319   int markerInnerWidth =
320       PBMarker.width() - PBMarkerMarginLeft - PBMarkerMarginRight;
321 
322   p.drawImage(QRect(0, 0, PBColorMarginLeft, height()), PBOverlay,
323               QRect(0, 0, PBColorMarginLeft, PBOverlay.height()));
324   p.drawImage(
325       QRect(PBColorMarginLeft, 0,
326             sliderRect.width() - PBColorMarginLeft - PBColorMarginRight,
327             height()),
328       PBOverlay,
329       QRect(PBColorMarginLeft, 0, overlayInnerWidth, PBOverlay.height()));
330   p.drawImage(
331       QRect(width() - PBColorMarginRight, 0, PBColorMarginRight, height()),
332       PBOverlay,
333       QRect(PBOverlay.width() - PBColorMarginRight, 0, PBColorMarginRight,
334             PBOverlay.height()));
335 
336   // Draw the position marker
337   currPos = sliderPositionFromValue(minimum(), maxValuePlusStep, value(),
338                                     colorWidth) +
339             PBMarkerMarginLeft;
340   nextPos = sliderPositionFromValue(minimum(), maxValuePlusStep,
341                                     value() + singleStep(), colorWidth) +
342             PBMarkerMarginLeft;
343 
344   p.drawImage(
345       QRect(currPos - PBMarkerMarginLeft, 0, PBMarkerMarginLeft, height()),
346       PBMarker, QRect(0, 0, PBMarkerMarginLeft, PBMarker.height()));
347   p.drawImage(
348       QRect(currPos, 0, nextPos - currPos, height()), PBMarker,
349       QRect(PBMarkerMarginLeft, 0, markerInnerWidth, PBMarker.height()));
350   p.drawImage(QRect(nextPos, 0, PBMarkerMarginRight, height()), PBMarker,
351               QRect(PBMarker.width() - PBMarkerMarginRight, 0,
352                     PBMarkerMarginRight, PBMarker.height()));
353 }
354 
355 //-----------------------------------------------------------------------------
356 
sliderPositionFromValue(int min,int max,int val,int span)357 inline int FlipSlider::sliderPositionFromValue(int min, int max, int val,
358                                                int span) {
359   return tceil(span * ((val - min) / (double)(max - min)));
360 }
361 
362 //-----------------------------------------------------------------------------
363 
sliderValueFromPosition(int min,int max,int step,int pos,int span)364 inline int FlipSlider::sliderValueFromPosition(int min, int max, int step,
365                                                int pos, int span) {
366   int colorBarPos     = pos - PBColorMarginLeft;
367   int colorSpan       = span - PBColorMarginLeft - PBColorMarginRight;
368   int tempRelativePos = (max - min + step) * (colorBarPos / (double)colorSpan);
369   return min + (tempRelativePos - tempRelativePos % step);
370 }
371 
372 //-----------------------------------------------------------------------------
373 
pageStepVal(int val)374 inline int FlipSlider::pageStepVal(int val) {
375   return tcrop(value() + pageStep() * tsign(val - value()), minimum(),
376                maximum());
377 }
378 
379 //-----------------------------------------------------------------------------
380 
381 // Mouse Press behaviour:
382 //  a) If middle button, just put frame to cursor position
383 //  b) If left button, and cursor on current frame pos, do like (a)
384 //  c) If left button, and cursor NOT on curr.. perform a page up/down on
385 //     the side of cursor pos
mousePressEvent(QMouseEvent * me)386 void FlipSlider::mousePressEvent(QMouseEvent *me) {
387   emit flipSliderPressed();
388   int cursorValue = sliderValueFromPosition(minimum(), maximum(), singleStep(),
389                                             me->pos().x(), width());
390   if (me->button() == Qt::MidButton)
391     if (cursorValue == value())
392       setSliderDown(true);
393     else {
394       // Move the page step
395       setValue(pageStepVal(cursorValue));
396     }
397 
398   else if (me->button() == Qt::LeftButton && cursorValue != value())
399     setValue(cursorValue);
400 }
401 
402 //-----------------------------------------------------------------------------
403 
mouseMoveEvent(QMouseEvent * me)404 void FlipSlider::mouseMoveEvent(QMouseEvent *me) {
405   if (isSliderDown() || me->buttons() & Qt::LeftButton) {
406     int cursorValue = sliderValueFromPosition(
407         minimum(), maximum(), singleStep(), me->pos().x(), width());
408     setValue(cursorValue);
409   }
410 }
411 
412 //-----------------------------------------------------------------------------
413 
mouseReleaseEvent(QMouseEvent * me)414 void FlipSlider::mouseReleaseEvent(QMouseEvent *me) {
415   setSliderDown(false);
416   emit flipSliderReleased();
417 }
418 
419 //=============================================================================
420 
421 enum {
422   eShowCompare         = 0x001,
423   eShowBg              = 0x002,
424   eShowFramerate       = 0x004,
425   eShowVcr             = 0x008,
426   eShowcolorFilter     = 0x010,
427   eShowCustom          = 0x020,
428   eShowHisto           = 0x040,
429   eShowSave            = 0x080,
430   eShowDefineSubCamera = 0x100,
431   eShowFilledRaster    = 0x200,
432   eShowDefineLoadBox   = 0x400,
433   eShowUseLoadBox      = 0x800,
434   eShowViewerControls  = 0x1000,
435   eShowSound           = 0x2000,
436   eShowLocator         = 0x4000,
437   eShowHowMany         = 0x8000
438 };
439 
FlipConsole(QVBoxLayout * mainLayout,std::vector<int> gadgetsMask,bool isLinkable,QWidget * customWidget,const QString & customizeId,FlipConsoleOwner * consoleOwner,bool enableBlanks)440 FlipConsole::FlipConsole(QVBoxLayout *mainLayout, std::vector<int> gadgetsMask,
441                          bool isLinkable, QWidget *customWidget,
442                          const QString &customizeId,
443                          FlipConsoleOwner *consoleOwner, bool enableBlanks)
444     : m_gadgetsMask(gadgetsMask)
445     , m_from(1)
446     , m_to(1)
447     , m_step(1)
448     , m_currentFrame(1)
449     , m_framesCount(1)
450     , m_settings()
451     , m_fps(24)
452     , m_sceneFps(24)
453     , m_isPlay(false)
454     , m_reverse(false)
455     , m_doubleRed(0)
456     , m_doubleGreen(0)
457     , m_doubleBlue(0)
458     , m_doubleRedAction(0)
459     , m_doubleGreenAction(0)
460     , m_doubleBlueAction(0)
461     , m_fpsSlider(0)
462     , m_markerFrom(0)
463     , m_markerTo(-1)
464     , m_playbackExecutor()
465     , m_drawBlanksEnabled(enableBlanks)
466     , m_blanksCount(0)
467     , m_blankColor(TPixel::Transparent)
468     , m_blanksToDraw(0)
469     , m_isLinkable(isLinkable)
470     , m_customAction(0)
471     , m_customizeMask(eShowHowMany - 1)
472     , m_fpsLabelAction(0)
473     , m_fpsSliderAction(0)
474     , m_fpsFieldAction(0)
475     , m_fpsField(0)
476     , m_customizeId(customizeId)
477     , m_histoSep(0)
478     , m_filledRasterSep(0)
479     , m_viewerSep(0)
480     , m_bgSep(0)
481     , m_vcrSep(0)
482     , m_compareSep(0)
483     , m_saveSep(0)
484     , m_colorFilterSep(0)
485     , m_subcamSep(0)
486     , m_playToolBar(0)
487     , m_colorFilterGroup(0)
488     , m_fpsLabel(0)
489     , m_consoleOwner(consoleOwner)
490     , m_enableBlankFrameButton(0) {
491   QString s = QSettings().value(m_customizeId).toString();
492   if (s != "") m_customizeMask = s.toUInt();
493 
494   if (m_gadgetsMask.size() == 0) return;
495 
496   // mainLayout->setMargin(1);
497   // mainLayout->setSpacing(0);
498 
499   // create toolbars other than frame slider
500   if (hasButton(m_gadgetsMask, eFrames)) {
501     createPlayToolBar(customWidget);
502 
503     m_playToolBarContainer = new ToolBarContainer();
504 
505     QHBoxLayout *hLayout = new QHBoxLayout;
506     hLayout->setMargin(0);
507     hLayout->setSpacing(0);
508     hLayout->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
509     {
510       DvScrollWidget *scrollableContainer = new DvScrollWidget;
511       scrollableContainer->setWidget(m_playToolBar);
512       hLayout->addWidget(scrollableContainer);
513 
514       // show fps
515       if (hasButton(m_gadgetsMask, eRate)) {
516         QFrame *fpsSliderFrame = createFpsSlider();
517         hLayout->addWidget(fpsSliderFrame, 1);
518       }
519     }
520     m_playToolBarContainer->setLayout(hLayout);
521 
522     mainLayout->addWidget(m_playToolBarContainer);
523   }
524 
525   // create frame slider
526   if (hasButton(m_gadgetsMask, eFrames)) {
527     m_frameSliderFrame = createFrameSlider();
528     mainLayout->addWidget(m_frameSliderFrame);
529   }
530 
531   applyCustomizeMask();
532 
533   bool ret = connect(&m_playbackExecutor, SIGNAL(nextFrame(int)), this,
534                      SLOT(onNextFrame(int)), Qt::BlockingQueuedConnection);
535 
536   assert(ret);
537 
538   // parent->setLayout(mainLayout);
539 }
540 
541 //-----------------------------------------------------------------------------
542 
showHideAllParts(bool isShow)543 void FlipConsole::showHideAllParts(bool isShow) {
544   m_playToolBarContainer->setVisible(isShow);
545   m_frameSliderFrame->setVisible(isShow);
546 }
547 
548 //-----------------------------------------------------------------------------
549 
showHidePlaybar(bool isShow)550 void FlipConsole::showHidePlaybar(bool isShow) {
551   m_playToolBarContainer->setVisible(isShow);
552 }
553 
554 //-----------------------------------------------------------------------------
555 
showHideFrameSlider(bool isShow)556 void FlipConsole::showHideFrameSlider(bool isShow) {
557   m_frameSliderFrame->setVisible(isShow);
558 }
559 
560 //-----------------------------------------------------------------------------
561 
562 void showEvent(QShowEvent *);
563 void hideEvent(QHideEvent *);
564 
makeCurrent()565 void FlipConsole::makeCurrent() {
566   if (m_currentConsole == this) return;
567   int i = m_visibleConsoles.indexOf(this);
568   if (i >= 0) m_visibleConsoles.takeAt(i);
569   m_visibleConsoles.append(this);
570   m_currentConsole = this;
571 }
572 
573 //-----------------------------------------------------------------------------
574 
setActive(bool active)575 void FlipConsole::setActive(bool active) {
576   if (active)
577     makeCurrent();
578   else {
579     pressButton(ePause);
580     int i = m_visibleConsoles.indexOf(this);
581     if (i >= 0) m_visibleConsoles.takeAt(i);
582     if (m_currentConsole == this) {
583       if (!m_visibleConsoles.empty())
584         m_currentConsole = m_visibleConsoles.last();
585       else
586         m_currentConsole = 0;
587     }
588   }
589 }
590 
591 //-----------------------------------------------------------------------------
592 
593 #define LX 22
594 #define LY 22
595 class DoubleButton final : public QToolButton {
596   QAction *m_firstAction, *m_secondAction;
597   QIcon::Mode m_firstMode, m_secondMode;
598   QIcon::State m_firstState, m_secondState;
599   bool m_enabled;
600 
601 public:
DoubleButton(QAction * firstAction,QAction * secondAction,QWidget * parent=0)602   DoubleButton(QAction *firstAction, QAction *secondAction, QWidget *parent = 0)
603       : QToolButton(parent)
604       , m_firstAction(firstAction)
605       , m_secondAction(secondAction)
606       , m_firstMode(QIcon::Normal)
607       , m_secondMode(QIcon::Normal)
608       , m_firstState(QIcon::Off)
609       , m_secondState(QIcon::Off)
610       , m_enabled(true) {
611     setFixedSize(LX, LY);
612     setMouseTracking(true);
613     setObjectName("flipDoubleButton");
614   }
setEnabledSecondButton(bool state)615   void setEnabledSecondButton(bool state) {
616     if (!state && m_secondAction->isChecked()) m_secondAction->trigger();
617     m_enabled = state;
618     update();
619   }
620 
621 protected:
paintEvent(QPaintEvent * e)622   void paintEvent(QPaintEvent *e) override {
623     QPainter p(this);
624 
625     p.drawPixmap(0, 0,
626                  m_firstAction->icon().pixmap(
627                      QSize(LX, LY / 2),
628                      m_firstAction->isChecked() ? QIcon::Normal : m_firstMode,
629                      m_firstAction->isChecked() ? QIcon::On : m_firstState));
630 
631     QIcon::Mode mode =
632         m_enabled ? (m_secondAction->isChecked() ? QIcon::Normal : m_secondMode)
633                   : QIcon::Disabled;
634     QIcon::State state =
635         m_enabled ? (m_secondAction->isChecked() ? QIcon::On : m_secondState)
636                   : QIcon::Off;
637 
638     p.drawPixmap(0, LY / 2 + 1,
639                  m_secondAction->icon().pixmap(QSize(LX, LY / 2), mode, state));
640   }
641 
mousePressEvent(QMouseEvent * e)642   void mousePressEvent(QMouseEvent *e) override {
643     QRect firstActionRect(0, 0, LX, LY / 2);
644     QRect secondActionRect(0, LY / 2 + 1, LX, LY / 2);
645 
646     QPoint pos = e->pos();
647     if (firstActionRect.contains(pos)) {
648       m_firstAction->trigger();
649     } else {
650       if (m_enabled) m_secondAction->trigger();
651     }
652     update();
653   }
654 
mouseMoveEvent(QMouseEvent * e)655   void mouseMoveEvent(QMouseEvent *e) override {
656     QPoint pos = e->pos();
657     QRect firstActionRect(0, 0, LX, LY / 2);
658     QRect secondActionRect(0, LY / 2 + 1, LX, LY / 2);
659 
660     m_firstState  = QIcon::Off;
661     m_secondState = QIcon::Off;
662     m_firstMode   = QIcon::Normal;
663     m_secondMode  = QIcon::Normal;
664 
665     if (firstActionRect.contains(pos)) {
666       m_firstMode = QIcon::Active;
667       setToolTip(m_firstAction->toolTip());
668     } else if (secondActionRect.contains(pos) && m_enabled) {
669       m_secondMode = QIcon::Active;
670       setToolTip(m_secondAction->toolTip());
671     }
672     update();
673   }
674 
leaveEvent(QEvent * e)675   void leaveEvent(QEvent *e) override {
676     m_firstMode   = QIcon::Normal;
677     m_firstState  = QIcon::Off;
678     m_secondMode  = QIcon::Normal;
679     m_secondState = QIcon::Off;
680 
681     update();
682 
683     QToolButton::leaveEvent(e);
684   }
685 };
686 
687 //-----------------------------------------------------------------------------
688 
enableButton(UINT button,bool enable,bool doShowHide)689 void FlipConsole::enableButton(UINT button, bool enable, bool doShowHide) {
690   if (!m_playToolBar) return;
691 
692   QList<QAction *> list = m_playToolBar->actions();
693   for (size_t i = 0; i < list.size(); i++)
694     if (list[i]->data().toUInt() == button) {
695       if (button == eSound) {
696         if (doShowHide) {
697           m_soundSep->setVisible(enable);
698         } else {
699           m_soundSep->setEnabled(enable);
700         }
701       }
702       if (button == eHisto) {
703         if (doShowHide) {
704           m_histoSep->setVisible(enable && m_customizeMask & eShowHisto);
705         } else {
706           m_histoSep->setEnabled(enable);
707         }
708       }
709       if (doShowHide) {
710         list[i]->setVisible(enable);
711       } else {
712         list[i]->setEnabled(enable);
713       }
714       if (!enable && list[i]->isChecked()) pressButton((EGadget)button);
715 
716       return;
717     }
718 
719   // double buttons are special, they are not accessible directly from the
720   // playtoolbar...
721   switch ((EGadget)button) {
722   case eGRed:
723     if (m_doubleRed) m_doubleRed->setEnabledSecondButton(enable);
724     break;
725   case eGGreen:
726     if (m_doubleGreen) m_doubleGreen->setEnabledSecondButton(enable);
727     break;
728   case eGBlue:
729     if (m_doubleBlue) m_doubleBlue->setEnabledSecondButton(enable);
730     break;
731   default:
732     break;
733   }
734 }
735 
736 //----------------------------------------------------------------------------
737 
toggleLinked()738 void FlipConsole::toggleLinked() {
739   m_areLinked = !m_areLinked;
740 
741   int i;
742   FlipConsole *playingConsole = 0;
743   for (i = 0; i < m_visibleConsoles.size(); i++) {
744     playingConsole = m_visibleConsoles.at(i);
745     if (playingConsole->m_isLinkable &&
746         playingConsole->m_playbackExecutor.isRunning())
747       break;
748   }
749 
750   if (i == m_visibleConsoles.size()) return;
751 
752   // if we are here, flip is playing!
753   m_isLinkedPlaying = m_areLinked;
754   int button =
755       m_areLinked ? (playingConsole->m_isPlay ? ePlay : eLoop) : ePause;
756 
757   for (i = 0; i < m_visibleConsoles.size(); i++) {
758     FlipConsole *console = m_visibleConsoles.at(i);
759     if (console->m_isLinkable && console != playingConsole) {
760       console->setChecked(button, true);
761       console->doButtonPressed(button);
762     }
763   }
764 }
765 
766 //----------------------------------------------------------------------------
767 
drawBlanks(int from,int to)768 bool FlipConsole::drawBlanks(int from, int to) {
769   if (m_blanksCount == 0 || m_isPlay || m_framesCount <= 1) return false;
770 
771   // enable blanks only when the blank button is pressed
772   if (m_enableBlankFrameButton && !m_enableBlankFrameButton->isChecked())
773     return false;
774 
775   if (m_blanksToDraw > 1 ||
776       (m_blanksToDraw == 0 &&
777        ((m_reverse && m_currentFrame - m_step < from) ||
778         (!m_reverse && m_currentFrame + m_step >
779                            to))))  // we are on the last frame of the loop
780   {
781     m_blanksToDraw = (m_blanksToDraw == 0 ? m_blanksCount : m_blanksToDraw - 1);
782     m_settings.m_blankColor     = m_blankColor;
783     m_settings.m_drawBlankFrame = true;
784     m_consoleOwner->onDrawFrame(from, m_settings);
785     m_settings.m_drawBlankFrame = false;
786     return true;
787   }
788 
789   m_blanksToDraw = 0;
790   return false;
791 }
792 
793 //----------------------------------------------------------------------------
794 
onNextFrame(int fps)795 void FlipConsole::onNextFrame(int fps) {
796   if (fps < 0)  // can be negative only if is a linked console; it means that
797                 // the master console is playing backward
798   {
799     bool reverse = m_reverse;
800     m_reverse    = true;
801     fps          = -fps;
802     playNextFrame();
803     m_reverse = reverse;
804   } else
805     playNextFrame();
806 
807   if (fps == -1) return;
808   if (m_fpsLabel)
809     m_fpsLabel->setText(tr(" FPS ") + QString::number(fps * tsign(m_fps)) +
810                         "/");
811   if (m_fpsField) {
812     if (fps == abs(m_fps))
813       m_fpsField->setLineEditBackgroundColor(Qt::green);
814     else
815       m_fpsField->setLineEditBackgroundColor(Qt::red);
816   }
817 }
818 
819 //----------------------------------------------------------------------------
820 
playNextFrame()821 void FlipConsole::playNextFrame() {
822   int from = m_from, to = m_to;
823   if (m_markerFrom <= m_markerTo) from = m_markerFrom, to = m_markerTo;
824 
825   if (m_framesCount == 0 ||
826       (m_isPlay && m_currentFrame == (m_reverse ? from : to))) {
827     doButtonPressed(ePause);
828     setChecked(m_isPlay ? ePlay : eLoop, false);
829     setChecked(ePause, true);
830     if (Preferences::instance()->rewindAfterPlaybackEnabled())
831       m_currentFrame = (m_reverse ? to : from);
832     emit playStateChanged(false);
833   } else {
834     if (drawBlanks(from, to)) return;
835 
836     if (m_reverse)
837       m_currentFrame =
838           ((m_currentFrame - m_step < from) ? to : m_currentFrame - m_step);
839     else
840       m_currentFrame =
841           ((m_currentFrame + m_step > to) ? from : m_currentFrame + m_step);
842   }
843 
844   m_currFrameSlider->setValue(m_currentFrame);
845   m_editCurrFrame->setText(QString::number(m_currentFrame));
846   m_settings.m_blankColor        = TPixel::Transparent;
847   m_settings.m_recomputeIfNeeded = true;
848   m_consoleOwner->onDrawFrame(m_currentFrame, m_settings);
849 }
850 
851 //-----------------------------------------------------------------------------
852 
updateCurrentFPS(int val)853 void FlipConsole::updateCurrentFPS(int val) {
854   setCurrentFPS(val);
855   m_fpsSlider->setValue(m_fps);
856 }
857 
858 //-----------------------------------------------------------------------------
859 
setFrameRate(int val,bool forceUpdate)860 void FlipConsole::setFrameRate(int val, bool forceUpdate) {
861   if (m_sceneFps != val || forceUpdate) {
862     if (!m_fpsSlider) return;
863     m_fpsSlider->setValue(val);
864     setCurrentFPS(val);
865   }
866   m_sceneFps = val;
867 }
868 
869 //-----------------------------------------------------------------------------
870 
setCurrentFPS(bool dragging)871 void FlipConsole::setCurrentFPS(bool dragging) {
872   setCurrentFPS(m_fpsField->getValue());
873   m_fpsSlider->setValue(m_fps);
874 }
875 
876 //-----------------------------------------------------------------------------
877 
setCurrentFPS(int val)878 void FlipConsole::setCurrentFPS(int val) {
879   if (m_fps == val) return;
880 
881   if (val == 0) val = 1;
882   m_fps = val;
883   m_fpsField->setValue(m_fps);
884 
885   if (m_playbackExecutor.isRunning() || m_isLinkedPlaying)
886     m_reverse = (val < 0);
887 
888   if (m_fpsLabel) m_fpsLabel->setText(tr(" FPS "));
889   if (m_fpsField) m_fpsField->setLineEditBackgroundColor(getFpsFieldColor());
890 
891   m_playbackExecutor.resetFps(m_fps);
892 }
893 
894 //-----------------------------------------------------------------------------
895 
createButton(UINT buttonMask,const char * iconStr,const QString & tip,bool checkable,QActionGroup * group)896 void FlipConsole::createButton(UINT buttonMask, const char *iconStr,
897                                const QString &tip, bool checkable,
898                                QActionGroup *group) {
899   QIcon icon      = createQIcon(iconStr);
900   QAction *action = new QAction(icon, tip, m_playToolBar);
901   action->setData(QVariant(buttonMask));
902   action->setCheckable(checkable);
903   if (group) group->addAction(action);
904   m_actions[(EGadget)buttonMask] = action;
905   m_playToolBar->addAction(action);
906 }
907 
908 //-----------------------------------------------------------------------------
909 
createCheckedButtonWithBorderImage(UINT buttonMask,const char * iconStr,const QString & tip,bool checkable,QActionGroup * group,const char * cmdId)910 QAction *FlipConsole::createCheckedButtonWithBorderImage(
911     UINT buttonMask, const char *iconStr, const QString &tip, bool checkable,
912     QActionGroup *group, const char *cmdId) {
913   QIcon icon            = createQIcon(iconStr);
914   QWidgetAction *action = new QWidgetAction(m_playToolBar);
915   action->setIcon(icon);
916   action->setToolTip(tip);
917   action->setData(QVariant(buttonMask));
918   action->setCheckable(checkable);
919   if (group) group->addAction(action);
920   QToolButton *button = new QToolButton(m_playToolBar);
921   button->setDefaultAction(action);
922 
923   m_buttons[(EGadget)buttonMask] = button;
924 
925   if (cmdId) {
926     QAction *a = CommandManager::instance()->getAction(cmdId);
927     if (a) button->addAction(a);
928   }
929 
930   action->setDefaultWidget(button);
931   button->setObjectName("chackableButtonWithImageBorder");
932   connect(button, SIGNAL(triggered(QAction *)), this,
933           SLOT(onButtonPressed(QAction *)));
934   // connect(action, SIGNAL(toggled(bool)), button, SLOT(setChecked(bool)));
935   m_playToolBar->addAction(action);
936   return action;
937 }
938 
939 //-----------------------------------------------------------------------------
940 
createDoubleButton(UINT buttonMask1,UINT buttonMask2,const char * iconStr1,const char * iconStr2,const QString & tip1,const QString & tip2,QActionGroup * group,DoubleButton * & widget)941 QAction *FlipConsole::createDoubleButton(
942     UINT buttonMask1, UINT buttonMask2, const char *iconStr1,
943     const char *iconStr2, const QString &tip1, const QString &tip2,
944     QActionGroup *group, DoubleButton *&widget) {
945   QAction *action1 =
946       new QAction(createQIcon(iconStr1, true), tip1, m_playToolBar);
947   QAction *action2 =
948       new QAction(createQIcon(iconStr2, true), tip2, m_playToolBar);
949   m_actions[(EGadget)buttonMask1] = action1;
950   m_actions[(EGadget)buttonMask2] = action2;
951 
952   action1->setData(QVariant(buttonMask1));
953   action1->setCheckable(true);
954   action2->setData(QVariant(buttonMask2));
955   action2->setCheckable(true);
956 
957   if (group) {
958     group->addAction(action1);
959     group->addAction(action2);
960   }
961 
962   widget = new DoubleButton(action1, action2, this);
963   return m_playToolBar->addWidget(widget);
964 
965   // m_playToolBar->addAction(action1);
966   // m_playToolBar->addAction(action2);
967 }
968 
969 //-----------------------------------------------------------------------------
970 
createOnOffButton(UINT buttonMask,const char * iconStr,const QString & tip,QActionGroup * group)971 void FlipConsole::createOnOffButton(UINT buttonMask, const char *iconStr,
972                                     const QString &tip, QActionGroup *group) {
973   QIcon icon      = createQIcon(iconStr);
974   QAction *action = new QAction(icon, tip, m_playToolBar);
975   action->setData(QVariant(buttonMask));
976   action->setCheckable(true);
977   if (group) group->addAction(action);
978   m_playToolBar->addAction(action);
979   m_actions[(EGadget)buttonMask] = action;
980 }
981 
982 //----------------------------------------------------------------------------------------------
983 
addMenuItem(UINT id,const QString & text,QMenu * menu)984 void FlipConsole::addMenuItem(UINT id, const QString &text, QMenu *menu) {
985   QAction *a = new QAction(text, menu);
986   a->setCheckable(true);
987   a->setChecked(id & m_customizeMask);
988   a->setData(QVariant(id));
989   menu->addAction(a);
990 }
991 
992 //----------------------------------------------------------------------------------------------
993 
onCustomizeButtonPressed(QAction * a)994 void FlipConsole::onCustomizeButtonPressed(QAction *a) {
995   UINT id = a->data().toUInt();
996   if (a->isChecked())
997     m_customizeMask = m_customizeMask | id;
998   else
999     m_customizeMask = m_customizeMask & (~id);
1000 
1001   QSettings().setValue(m_customizeId, QString::number(m_customizeMask));
1002 
1003   applyCustomizeMask();
1004 }
1005 
1006 //----------------------------------------------------------------------------------------------
applyCustomizeMask()1007 void FlipConsole::applyCustomizeMask() {
1008   enableButton(eSave, m_customizeMask & eShowSave);
1009   // if(m_saveSep)
1010   //  m_saveSep->setVisible(m_customizeMask&eShowSave);
1011 
1012   enableButton(eSaveImg, m_customizeMask & eShowCompare);
1013   enableButton(eCompare, m_customizeMask & eShowCompare);
1014   if (m_compareSep) m_compareSep->setVisible(m_customizeMask & eShowCompare);
1015 
1016   enableButton(eDefineSubCamera, m_customizeMask & eShowDefineSubCamera);
1017   enableButton(eDefineLoadBox, m_customizeMask & eShowDefineLoadBox);
1018   enableButton(eUseLoadBox, m_customizeMask & eShowUseLoadBox);
1019   if (m_subcamSep) {
1020     int count = m_gadgetsMask.size();
1021     bool hasDefineLoadBox =
1022         std::find(m_gadgetsMask.begin(), m_gadgetsMask.end(), eDefineLoadBox) ==
1023         m_gadgetsMask.end();
1024     bool hasUseLoadBox   = std::find(m_gadgetsMask.begin(), m_gadgetsMask.end(),
1025                                    eUseLoadBox) == m_gadgetsMask.end();
1026     bool hasDefineSubCam = std::find(m_gadgetsMask.begin(), m_gadgetsMask.end(),
1027                                      eDefineSubCamera) == m_gadgetsMask.end();
1028     m_subcamSep->setVisible(
1029         (hasDefineSubCam && m_customizeMask & eShowDefineSubCamera) ||
1030         (hasDefineLoadBox && m_customizeMask & eShowDefineLoadBox) ||
1031         (hasUseLoadBox && m_customizeMask & eShowUseLoadBox));
1032   }
1033 
1034   enableButton(eWhiteBg, m_customizeMask & eShowBg);
1035   enableButton(eBlackBg, m_customizeMask & eShowBg);
1036   enableButton(eCheckBg, m_customizeMask & eShowBg);
1037   if (m_bgSep) m_bgSep->setVisible(m_customizeMask & eShowBg);
1038 
1039   if (m_fpsLabel && m_fpsSlider && m_fpsField) {
1040     m_fpsLabel->setVisible(m_customizeMask & eShowFramerate);
1041     m_fpsSlider->setVisible(m_customizeMask & eShowFramerate);
1042     m_fpsField->setVisible(m_customizeMask & eShowFramerate);
1043   }
1044 
1045   enableButton(eFirst, m_customizeMask & eShowVcr);
1046   enableButton(ePrev, m_customizeMask & eShowVcr);
1047   enableButton(ePause, m_customizeMask & eShowVcr);
1048   enableButton(ePlay, m_customizeMask & eShowVcr);
1049   enableButton(eLoop, m_customizeMask & eShowVcr);
1050   enableButton(eNext, m_customizeMask & eShowVcr);
1051   enableButton(eLast, m_customizeMask & eShowVcr);
1052 
1053   enableButton(eSound, m_customizeMask & eShowSound);
1054   enableButton(eLocator, m_customizeMask & eShowLocator);
1055 
1056   if (m_vcrSep) m_vcrSep->setVisible(m_customizeMask & eShowVcr);
1057 
1058   enableButton(eMatte, m_customizeMask & eShowcolorFilter);
1059   enableButton(eHisto, m_customizeMask & eShowHisto);
1060   if (m_histoSep) m_histoSep->setVisible(m_customizeMask & eShowHisto);
1061 
1062   if (m_doubleRedAction) {
1063     m_doubleRedAction->setVisible(m_customizeMask & eShowcolorFilter);
1064     m_doubleGreenAction->setVisible(m_customizeMask & eShowcolorFilter);
1065     m_doubleBlueAction->setVisible(m_customizeMask & eShowcolorFilter);
1066   } else {
1067     enableButton(eRed, m_customizeMask & eShowcolorFilter);
1068     enableButton(eGreen, m_customizeMask & eShowcolorFilter);
1069     enableButton(eBlue, m_customizeMask & eShowcolorFilter);
1070   }
1071 
1072   if (m_colorFilterGroup)
1073     m_colorFilterGroup->setVisible(m_customizeMask & eShowcolorFilter);
1074 
1075   if (m_colorFilterSep)
1076     m_colorFilterSep->setVisible(m_customizeMask & eShowcolorFilter);
1077 
1078   if (m_customAction) {
1079     bool visible = bool(m_customizeMask & eShowCustom);
1080 
1081     m_customAction->setVisible(visible);
1082     m_customSep->setVisible(visible);
1083   }
1084 
1085   enableButton(eFilledRaster, m_customizeMask & eShowFilledRaster);
1086   if (m_filledRasterSep)
1087     m_filledRasterSep->setVisible(m_customizeMask & eShowFilledRaster);
1088 
1089   enableButton(eZoomIn, m_customizeMask & eShowViewerControls);
1090   enableButton(eZoomOut, m_customizeMask & eShowViewerControls);
1091   enableButton(eFlipHorizontal, m_customizeMask & eShowViewerControls);
1092   enableButton(eFlipVertical, m_customizeMask & eShowViewerControls);
1093   enableButton(eResetView, m_customizeMask & eShowViewerControls);
1094   if (m_viewerSep)
1095     m_viewerSep->setVisible(m_customizeMask & eShowViewerControls);
1096 
1097   update();
1098 }
1099 
1100 //----------------------------------------------------------------------------------------------
1101 
createCustomizeMenu(bool withCustomWidget)1102 void FlipConsole::createCustomizeMenu(bool withCustomWidget) {
1103   if (hasButton(m_gadgetsMask, eCustomize)) {
1104     QIcon icon          = createQIcon("menu");
1105     QToolButton *button = new QToolButton();
1106     button->setIcon(icon);
1107     button->setPopupMode(QToolButton::MenuButtonPopup);
1108     button->setObjectName("flipCustomize");
1109 
1110     QMenu *menu = new QMenu();
1111     button->setMenu(menu);
1112 
1113     m_playToolBar->addWidget(button);
1114     m_playToolBar->addSeparator();
1115 
1116     if (hasButton(m_gadgetsMask, eSave))
1117       addMenuItem(eShowSave, tr("Save"), menu);
1118 
1119     if (hasButton(m_gadgetsMask, eSaveImg) ||
1120         hasButton(m_gadgetsMask, eCompare))
1121       addMenuItem(eShowCompare, tr("Snapshot"), menu);
1122 
1123     if (hasButton(m_gadgetsMask, eDefineSubCamera))
1124       addMenuItem(eShowDefineSubCamera, tr("Define Sub-camera"), menu);
1125     if (hasButton(m_gadgetsMask, eDefineLoadBox))
1126       addMenuItem(eShowDefineLoadBox, tr("Define Loading Box"), menu);
1127     if (hasButton(m_gadgetsMask, eUseLoadBox))
1128       addMenuItem(eShowUseLoadBox, tr("Use Loading Box"), menu);
1129 
1130     if (hasButton(m_gadgetsMask, eWhiteBg) ||
1131         hasButton(m_gadgetsMask, eBlackBg) ||
1132         hasButton(m_gadgetsMask, eCheckBg))
1133       addMenuItem(eShowBg, tr("Background Colors"), menu);
1134 
1135     addMenuItem(eShowVcr, tr("Playback Controls"), menu);
1136 
1137     if (hasButton(m_gadgetsMask, eRed) || hasButton(m_gadgetsMask, eGreen) ||
1138         hasButton(m_gadgetsMask, eBlue) || hasButton(m_gadgetsMask, eMatte))
1139       addMenuItem(eShowcolorFilter, tr("Color Channels"), menu);
1140 
1141     if (hasButton(m_gadgetsMask, eSound))
1142       addMenuItem(eShowSound, tr("Sound"), menu);
1143 
1144     if (hasButton(m_gadgetsMask, eHisto))
1145       addMenuItem(eShowHisto, tr("Histogram"), menu);
1146 
1147     if (hasButton(m_gadgetsMask, eLocator))
1148       addMenuItem(eShowLocator, tr("Locator"), menu);
1149 
1150     if (withCustomWidget) addMenuItem(eShowCustom, tr("Set Key"), menu);
1151 
1152     if (hasButton(m_gadgetsMask, eFilledRaster))
1153       addMenuItem(eFilledRaster, tr("Display Areas as Filled"), menu);
1154 
1155     if (hasButton(m_gadgetsMask, eZoomIn) ||
1156         hasButton(m_gadgetsMask, eZoomOut) ||
1157         hasButton(m_gadgetsMask, eFlipHorizontal) ||
1158         hasButton(m_gadgetsMask, eFlipVertical) ||
1159         hasButton(m_gadgetsMask, eResetView))
1160       addMenuItem(eShowViewerControls, tr("Viewer Controls"), menu);
1161 
1162     if (hasButton(m_gadgetsMask, eRate))
1163       addMenuItem(eShowFramerate, tr("Framerate"), menu);
1164 
1165     bool ret = connect(menu, SIGNAL(triggered(QAction *)), this,
1166                        SLOT(onCustomizeButtonPressed(QAction *)));
1167     assert(ret);
1168   }
1169 }
1170 
1171 //-----------------------------------------------------------------------------
1172 
createPlayToolBar(QWidget * customWidget)1173 void FlipConsole::createPlayToolBar(QWidget *customWidget) {
1174   bool ret              = true;
1175   bool withCustomWidget = customWidget != 0;
1176 
1177   m_playToolBar = new QToolBar(this);
1178   m_playToolBar->setMovable(false);
1179   m_playToolBar->setObjectName("FlipConsolePlayToolBar");
1180   m_playToolBar->setIconSize(QSize(20, 20));
1181   //	m_playToolBar->setObjectName("chackableButtonToolBar");
1182 
1183   // m_playToolBar->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
1184 
1185   createCustomizeMenu(withCustomWidget);
1186 
1187   if (hasButton(m_gadgetsMask, eSave)) {
1188     createButton(eSave, "save", tr("&Save Images"), false);
1189     // m_saveSep = m_playToolBar->addSeparator();
1190   }
1191 
1192   // snapshot
1193   bool separator = false;
1194   if (hasButton(m_gadgetsMask, eSaveImg)) {
1195     createButton(eSaveImg, "snapshot", tr("&Snapshot"), false);
1196     separator = true;
1197   }
1198   if (hasButton(m_gadgetsMask, eCompare)) {
1199     createButton(eCompare, "compare", tr("&Compare to Snapshot"), true);
1200     separator = true;
1201   }
1202   if (separator) m_compareSep = m_playToolBar->addSeparator();
1203 
1204   // sub camera
1205   separator = false;
1206   if (hasButton(m_gadgetsMask, eDefineSubCamera)) {
1207     createButton(eDefineSubCamera, "define_subcamera_preview",
1208                  tr("&Define Sub-camera"), true);
1209     separator = true;
1210   }
1211   if (hasButton(m_gadgetsMask, eDefineLoadBox)) {
1212     createButton(eDefineLoadBox, "define_subcamera_preview",
1213                  tr("&Define Loading Box"), true);
1214     separator = true;
1215   }
1216   if (hasButton(m_gadgetsMask, eUseLoadBox)) {
1217     createButton(eUseLoadBox, "use_subcamera_preview", tr("&Use Loading Box"),
1218                  true);
1219     separator = true;
1220   }
1221   if (separator) m_subcamSep = m_playToolBar->addSeparator();
1222 
1223   // preview BGs
1224   QActionGroup *group = new QActionGroup(m_playToolBar);
1225   if (hasButton(m_gadgetsMask, eWhiteBg))
1226     createOnOffButton(eWhiteBg, "preview_white", tr("&White Background"),
1227                       group);
1228   if (hasButton(m_gadgetsMask, eBlackBg))
1229     createOnOffButton(eBlackBg, "preview_black", tr("&Black Background"),
1230                       group);
1231   if (hasButton(m_gadgetsMask, eCheckBg))
1232     createOnOffButton(eCheckBg, "preview_checkboard",
1233                       tr("&Checkered Background"), group);
1234   if (hasButton(m_gadgetsMask, eWhiteBg))
1235     m_bgSep = m_playToolBar->addSeparator();
1236 
1237   // VCR buttons
1238   QActionGroup *playGroup = new QActionGroup(m_playToolBar);
1239   if (hasButton(m_gadgetsMask, eFirst))
1240     createButton(eFirst, "framefirst", tr("&First Frame"), false);
1241   if (hasButton(m_gadgetsMask, ePrev))
1242     createButton(ePrev, "frameprev", tr("&Previous Frame"), false);
1243   if (hasButton(m_gadgetsMask, ePause))
1244     createCheckedButtonWithBorderImage(ePause, "pause", tr("Pause"), true,
1245                                        playGroup, "A_Flip_Pause");
1246   if (hasButton(m_gadgetsMask, ePlay))
1247     createCheckedButtonWithBorderImage(ePlay, "play", tr("Play"), true,
1248                                        playGroup, "A_Flip_Play");
1249   if (hasButton(m_gadgetsMask, eLoop))
1250     createCheckedButtonWithBorderImage(eLoop, "loop", tr("Loop"), true,
1251                                        playGroup, "A_Flip_Loop");
1252 
1253   if (hasButton(m_gadgetsMask, eNext))
1254     createButton(eNext, "framenext", tr("&Next Frame"), false);
1255   if (hasButton(m_gadgetsMask, eLast))
1256     createButton(eLast, "framelast", tr("&Last Frame"), false);
1257 
1258   // separator
1259   if (hasButton(m_gadgetsMask, ePlay)) m_vcrSep = m_playToolBar->addSeparator();
1260 
1261   // Channel Selector
1262   m_colorFilterGroup = new QActionGroup(m_playToolBar);
1263   m_colorFilterGroup->setExclusive(false);
1264   if (hasButton(m_gadgetsMask, eRed) && !hasButton(m_gadgetsMask, eGRed))
1265     createButton(eRed, "channelred", tr("Red Channel"), true);
1266   else if (hasButton(m_gadgetsMask, eRed) && hasButton(m_gadgetsMask, eGRed))
1267     m_doubleRedAction = createDoubleButton(
1268         eRed, eGRed, "half_R", "half_bw", tr("Red Channel"),
1269         tr("Red Channel in Grayscale"), m_colorFilterGroup, m_doubleRed);
1270 
1271   if (hasButton(m_gadgetsMask, eGreen) && !hasButton(m_gadgetsMask, eGGreen))
1272     createButton(eGreen, "channelgreen", tr("Green Channel"), true);
1273   else if (hasButton(m_gadgetsMask, eGreen) &&
1274            hasButton(m_gadgetsMask, eGGreen))
1275     m_doubleGreenAction = createDoubleButton(
1276         eGreen, eGGreen, "half_G", "half_bw", tr("Green Channel"),
1277         tr("Green Channel in Grayscale"), m_colorFilterGroup, m_doubleGreen);
1278 
1279   if (hasButton(m_gadgetsMask, eBlue) && !hasButton(m_gadgetsMask, eGBlue))
1280     createButton(eBlue, "channelblue", tr("Blue Channel"), true);
1281   else if (hasButton(m_gadgetsMask, eBlue) && hasButton(m_gadgetsMask, eGBlue))
1282     m_doubleBlueAction = createDoubleButton(
1283         eBlue, eGBlue, "half_B", "half_bw", tr("Blue Channel"),
1284         tr("Blue Channel in Grayscale"), m_colorFilterGroup, m_doubleBlue);
1285 
1286   ret = ret && connect(m_colorFilterGroup, SIGNAL(triggered(QAction *)), this,
1287                        SLOT(onButtonPressed(QAction *)));
1288 
1289   if (hasButton(m_gadgetsMask, eMatte))
1290     createButton(eMatte, "channelmatte", tr("Alpha Channel"), true);
1291 
1292   // separator
1293   if (hasButton(m_gadgetsMask, eRed) || hasButton(m_gadgetsMask, eGRed))
1294     m_colorFilterSep = m_playToolBar->addSeparator();
1295 
1296   // Sound & Histogram & Locator
1297   if (hasButton(m_gadgetsMask, eSound) || hasButton(m_gadgetsMask, eHisto) ||
1298       hasButton(m_gadgetsMask, eLocator)) {
1299     if (hasButton(m_gadgetsMask, eSound)) {
1300       createButton(eSound, "sound", tr("&Soundtrack "), true);
1301       m_soundSep = m_playToolBar->addSeparator();
1302     }
1303     if (hasButton(m_gadgetsMask, eHisto))
1304       createButton(eHisto, "histograms", tr("&Histogram"), false);
1305     if (hasButton(m_gadgetsMask, eLocator))
1306       createButton(eLocator, "locator", tr("&Locator"), false);
1307     if (hasButton(m_gadgetsMask, eHisto) || hasButton(m_gadgetsMask, eLocator))
1308       m_histoSep = m_playToolBar->addSeparator();
1309   }
1310 
1311   if (hasButton(m_gadgetsMask, eFilledRaster)) {
1312     createOnOffButton(eFilledRaster, "preview_white",
1313                       tr("&Display Areas as Filled"), 0);
1314     m_filledRasterSep = m_playToolBar->addSeparator();
1315   }
1316 
1317   if (withCustomWidget) {
1318     m_customAction = m_playToolBar->addWidget(customWidget);
1319     m_customSep    = m_playToolBar->addSeparator();
1320   }
1321 
1322   if (hasButton(m_gadgetsMask, eZoomIn) || hasButton(m_gadgetsMask, eZoomOut) ||
1323       hasButton(m_gadgetsMask, eFlipHorizontal) ||
1324       hasButton(m_gadgetsMask, eFlipVertical) ||
1325       hasButton(m_gadgetsMask, eResetView)) {
1326     if (hasButton(m_gadgetsMask, eZoomIn))
1327       createButton(eZoomIn, "zoomin", tr("&Zoom In"), false);
1328     if (hasButton(m_gadgetsMask, eZoomOut))
1329       createButton(eZoomOut, "zoomout", tr("&Zoom Out"), false);
1330     if (hasButton(m_gadgetsMask, eFlipHorizontal))
1331       createButton(eFlipHorizontal, "fliphoriz", tr("&Flip Horizontally"), 0);
1332     if (hasButton(m_gadgetsMask, eFlipVertical))
1333       createButton(eFlipVertical, "flipvert", tr("&Flip Vertically"), 0);
1334     if (hasButton(m_gadgetsMask, eResetView))
1335       createButton(eResetView, "reset", tr("&Reset View"), false);
1336     m_viewerSep = m_playToolBar->addSeparator();
1337   }
1338 
1339   // for all actions in this toolbar
1340   ret = ret && connect(m_playToolBar, SIGNAL(actionTriggered(QAction *)), this,
1341                        SLOT(onButtonPressed(QAction *)));
1342 
1343   setChecked(ePause, true);
1344   setChecked(eWhiteBg, FlipBookWhiteBgToggle);
1345   setChecked(eBlackBg, FlipBookBlackBgToggle);
1346   setChecked(eCheckBg, FlipBookCheckBgToggle);
1347   assert(ret);
1348 }
1349 
1350 //-----------------------------------------------------------------------------
1351 
enableBlanks(bool state)1352 void FlipConsole::enableBlanks(bool state) {
1353   m_drawBlanksEnabled = state;
1354   m_blankColor        = TPixel::Transparent;
1355   if (m_drawBlanksEnabled)
1356     Preferences::instance()->getBlankValues(m_blanksCount, m_blankColor);
1357   else {
1358     m_blanksCount = 0;
1359     m_blankColor  = TPixel::Transparent;
1360   }
1361 }
1362 
1363 //-----------------------------------------------------------------------------
1364 /*! call consoleOwner->onDrawFrame() intead of emitting drawFrame signal
1365  */
showCurrentFrame()1366 void FlipConsole::showCurrentFrame() {
1367   m_consoleOwner->onDrawFrame(m_currentFrame, m_settings);
1368 }
1369 
1370 //-----------------------------------------------------------------------------
1371 
setChecked(UINT button,bool state)1372 void FlipConsole::setChecked(UINT button, bool state) {
1373   int i;
1374   if (m_playToolBar) {
1375     QObjectList objectList = m_playToolBar->children();
1376     for (i = 0; i < (int)objectList.size(); i++) {
1377       QAction *action = dynamic_cast<QAction *>(objectList[i]);
1378       if (!action) {
1379         QToolButton *toolButton = dynamic_cast<QToolButton *>(objectList[i]);
1380         if (!toolButton) continue;
1381         action = toolButton->defaultAction();
1382       }
1383       if (action && action->data().toUInt() == button) {
1384         action->setChecked(state);
1385         return;
1386       }
1387     }
1388   }
1389 
1390   if (m_colorFilterGroup) {
1391     QList<QAction *> list = m_colorFilterGroup->actions();
1392     for (i = 0; i < (int)list.size(); i++)
1393       if (list[i]->data().toUInt() == button) {
1394         list[i]->setChecked(state);
1395         return;
1396       }
1397   }
1398 }
1399 
1400 //-----------------------------------------------------------------------------
1401 
isChecked(UINT button) const1402 bool FlipConsole::isChecked(UINT button) const {
1403   QList<QAction *> list;
1404   int i;
1405 
1406   if (m_playToolBar) {
1407     list = m_playToolBar->actions();
1408     for (i = 0; i < (int)list.size(); i++)
1409       if (list[i]->data().toUInt() == button) return list[i]->isChecked();
1410   }
1411 
1412   if (m_colorFilterGroup) {
1413     list = m_colorFilterGroup->actions();
1414     for (i = 0; i < (int)list.size(); i++)
1415       if (list[i]->data().toUInt() == button) return list[i]->isChecked();
1416   }
1417 
1418   return false;
1419 }
1420 
1421 //-----------------------------------------------------------------------------
1422 
pressLinkedConsoleButton(UINT button,FlipConsole * parent)1423 void FlipConsole::pressLinkedConsoleButton(UINT button, FlipConsole *parent) {
1424   int i;
1425   assert(parent);
1426 
1427   for (i = 0; i < m_visibleConsoles.size(); i++) {
1428     FlipConsole *console = m_visibleConsoles.at(i);
1429     if (console->m_isLinkable && console != parent) {
1430       console->setChecked(button, parent ? parent->isChecked(button) : true);
1431       console->doButtonPressed(button);
1432     }
1433   }
1434 }
1435 
1436 //-----------------------------------------------------------------------------
1437 
onButtonPressed(int button)1438 void FlipConsole::onButtonPressed(int button) {
1439   makeCurrent();
1440   if (m_playbackExecutor.isRunning() &&
1441       (button == FlipConsole::ePlay || button == FlipConsole::eLoop)) {
1442     pressButton(ePause);
1443   } else {
1444     // Sync playback state among all viewers & combo viewers.
1445     // Note that the property "m_isLinkable" is used for distinguishing the
1446     // owner between (viewer / combo viewer) and (flipbook / color model).
1447     if (!m_isLinkable &&
1448         (button == FlipConsole::ePlay || button == FlipConsole::eLoop)) {
1449       bool stoppedOther = false;
1450       for (auto playingConsole : m_visibleConsoles) {
1451         if (playingConsole == this || playingConsole->isLinkable()) continue;
1452         if (playingConsole->m_playbackExecutor.isRunning()) {
1453           playingConsole->doButtonPressed(ePause);
1454           playingConsole->setChecked(ePlay, false);
1455           playingConsole->setChecked(eLoop, false);
1456           playingConsole->setChecked(ePause, true);
1457           stoppedOther = true;
1458         }
1459       }
1460       if (stoppedOther) {
1461         setChecked(ePlay, false);
1462         setChecked(eLoop, false);
1463         setChecked(ePause, true);
1464         return;
1465       }
1466     }
1467 
1468     doButtonPressed(button);
1469   }
1470 
1471   if (m_areLinked) pressLinkedConsoleButton(button, this);
1472 }
1473 
1474 //-----------------------------------------------------------------------------
pressButton(EGadget buttonId)1475 void FlipConsole::pressButton(EGadget buttonId) {
1476   FlipConsole *console = this;
1477   if (m_visibleConsoles.indexOf(this) < 0 && m_visibleConsoles.size() > 0) {
1478     console = m_visibleConsoles.at(0);
1479     console->makeCurrent();
1480   }
1481   if (console->m_buttons.contains(buttonId) &&
1482       console->m_buttons[buttonId]->isEnabled())
1483     console->m_buttons[buttonId]->click();
1484   else if (console->m_actions.contains(buttonId) &&
1485            console->m_actions[buttonId]->isEnabled())
1486     console->m_actions[buttonId]->trigger();
1487 }
1488 
1489 //-----------------------------------------------------------------------------
1490 
onLoadBox(bool isDefine)1491 void FlipConsole::onLoadBox(bool isDefine) {
1492   int shrink, dummy;
1493 
1494   Preferences::instance()->getViewValues(shrink, dummy);
1495 
1496   if (shrink != 1) {
1497 #ifdef _WIN32
1498     MessageBox(0, "Cannot use loading box with a shrink factor! ", "Warning",
1499                MB_OK);
1500 #endif
1501     setChecked(eUseLoadBox, false);
1502     setChecked(eDefineLoadBox, false);
1503     m_settings.m_useLoadbox = m_settings.m_defineLoadbox = false;
1504     return;
1505   }
1506 
1507   if (isDefine)
1508     m_settings.m_defineLoadbox = !m_settings.m_defineLoadbox;
1509   else
1510     m_settings.m_useLoadbox = !m_settings.m_useLoadbox;
1511 
1512   if (m_settings.m_defineLoadbox && m_settings.m_useLoadbox) {
1513     setChecked(isDefine ? eUseLoadBox : eDefineLoadBox, false);
1514     if (isDefine)
1515       m_settings.m_useLoadbox = false;
1516     else
1517       m_settings.m_defineLoadbox = false;
1518   }
1519 
1520   m_consoleOwner->onDrawFrame(m_currentFrame, m_settings);
1521   return;
1522 }
1523 
1524 //-----------------------------------------------------------------------------
1525 
doButtonPressed(UINT button)1526 void FlipConsole::doButtonPressed(UINT button) {
1527   emit buttonPressed((FlipConsole::EGadget)button);
1528 
1529   int from = m_from, to = m_to;
1530   // When the level editing mode, ignore the preview frame range marker
1531   if (m_markerFrom <= m_markerTo && m_frameHandle &&
1532       m_frameHandle->isEditingScene())
1533     from = m_markerFrom, to = m_markerTo;
1534 
1535   bool linked = m_areLinked && m_isLinkable;
1536 
1537   switch (button) {
1538   case eFirst:
1539     m_currentFrame = from;
1540     break;
1541   case ePrev:
1542     m_currentFrame =
1543         (m_currentFrame - m_step < from) ? from : m_currentFrame - m_step;
1544     break;
1545   case eNext:
1546     m_currentFrame =
1547         (m_currentFrame + m_step > to) ? to : m_currentFrame + m_step;
1548     break;
1549   case eLast:
1550     m_currentFrame = to;
1551     break;
1552   case ePlay:
1553   case eLoop:
1554     // if (	  isChecked(ePlay,   false);
1555     // setChecked(eLoop,   false);
1556     m_editCurrFrame->disconnect();
1557     m_currFrameSlider->disconnect();
1558 
1559     m_isPlay = (button == ePlay);
1560 
1561     if (linked && m_isLinkedPlaying) return;
1562 
1563     if ((m_fps == 0 || m_framesCount == 0) && m_playbackExecutor.isRunning()) {
1564       doButtonPressed(ePause);
1565       if (m_fpsLabel) m_fpsLabel->setText(tr(" FPS ") + QString::number(m_fps));
1566       if (m_fpsField)
1567         m_fpsField->setLineEditBackgroundColor(getFpsFieldColor());
1568       return;
1569     }
1570     if (m_fpsLabel) m_fpsLabel->setText(tr(" FPS	") + "/");
1571     if (m_fpsField) m_fpsField->setLineEditBackgroundColor(Qt::red);
1572 
1573     m_playbackExecutor.resetFps(m_fps);
1574     if (!m_playbackExecutor.isRunning()) m_playbackExecutor.start();
1575     m_isLinkedPlaying = linked;
1576 
1577     m_reverse = (m_fps < 0);
1578 
1579     if (!linked) {
1580       // if the play button pressed at the end frame, then go back to the
1581       // start frame and play
1582       if (m_currentFrame <= from ||
1583           m_currentFrame >=
1584               to)  // the first frame of the playback is drawn right now
1585         m_currentFrame = m_reverse ? to : from;
1586       m_settings.m_recomputeIfNeeded = true;
1587       m_consoleOwner->onDrawFrame(m_currentFrame, m_settings);
1588     }
1589 
1590     emit playStateChanged(true);
1591     return;
1592 
1593   case ePause:
1594     if (!m_playbackExecutor.isRunning() && !m_isLinkedPlaying) {
1595       // Sync playback state among all viewers & combo viewers.
1596       // Note that the property "m_isLinkable" is used for distinguishing the
1597       // owner between (viewer / combo viewer) and (flipbook / color model).
1598       if (!m_isLinkable) {
1599         for (auto playingConsole : m_visibleConsoles) {
1600           if (playingConsole->isLinkable()) continue;
1601           if (playingConsole->m_playbackExecutor.isRunning())
1602             playingConsole->doButtonPressed(button);
1603           playingConsole->setChecked(ePlay, false);
1604           playingConsole->setChecked(eLoop, false);
1605           playingConsole->setChecked(ePause, true);
1606         }
1607       }
1608       return;
1609     }
1610 
1611     m_isLinkedPlaying = false;
1612 
1613     if (m_playbackExecutor.isRunning()) m_playbackExecutor.abort();
1614 
1615     m_isPlay       = false;
1616     m_blanksToDraw = 0;
1617 
1618     m_consoleOwner->swapBuffers();
1619     m_consoleOwner->changeSwapBehavior(true);
1620 
1621     if (m_settings.m_blankColor != TPixel::Transparent) {
1622       m_settings.m_blankColor        = TPixel::Transparent;
1623       m_settings.m_recomputeIfNeeded = true;
1624       m_consoleOwner->onDrawFrame(m_currentFrame, m_settings);
1625     }
1626     if (m_fpsLabel) m_fpsLabel->setText(tr(" FPS "));
1627     if (m_fpsField) m_fpsField->setLineEditBackgroundColor(getFpsFieldColor());
1628     // setChecked(ePlay,   false);
1629     // setChecked(eLoop,   false);
1630     connect(m_editCurrFrame, SIGNAL(editingFinished()), this,
1631             SLOT(OnSetCurrentFrame()));
1632     connect(m_currFrameSlider, SIGNAL(flipSliderReleased()), this,
1633             SLOT(OnFrameSliderRelease()));
1634     connect(m_currFrameSlider, SIGNAL(flipSliderPressed()), this,
1635             SLOT(OnFrameSliderPress()));
1636     connect(m_currFrameSlider, SIGNAL(valueChanged(int)), this,
1637             SLOT(OnSetCurrentFrame(int)));
1638     connect(m_currFrameSlider, SIGNAL(flipSliderReleased()), this,
1639             SLOT(onSliderRelease()));
1640     emit playStateChanged(false);
1641     return;
1642 
1643   case eGRed:
1644   case eGGreen:
1645   case eGBlue:
1646   case eRed:
1647   case eGreen:
1648   case eBlue:
1649   case eMatte: {
1650     if (button != eGRed) setChecked(eGRed, false);
1651     if (button != eGGreen) setChecked(eGGreen, false);
1652     if (button != eGBlue) setChecked(eGBlue, false);
1653 
1654     if (button == eGRed || button == eGGreen || button == eGBlue) {
1655       m_settings.m_greytones = isChecked(button);
1656       setChecked(eRed, false);
1657       setChecked(eGreen, false);
1658       setChecked(eBlue, false);
1659       setChecked(eMatte, false);
1660     } else
1661       m_settings.m_greytones = false;
1662 
1663     if (m_doubleRed) {
1664       m_doubleRed->update();
1665       m_doubleGreen->update();
1666       m_doubleBlue->update();
1667     }
1668 
1669     int colorMask = 0;
1670     if (isChecked(eRed) || isChecked(eGRed))
1671       colorMask = colorMask | TRop::RChan;
1672     if (isChecked(eGreen) || isChecked(eGGreen))
1673       colorMask = colorMask | TRop::GChan;
1674     if (isChecked(eBlue) || isChecked(eGBlue))
1675       colorMask = colorMask | TRop::BChan;
1676     if (isChecked(eMatte)) colorMask = colorMask | TRop::MChan;
1677 
1678     if (colorMask == (TRop::RChan | TRop::GChan | TRop::BChan) ||
1679         colorMask == (TRop::RChan | TRop::GChan | TRop::BChan | TRop::MChan))
1680       m_settings.m_colorMask = 0;
1681     else
1682       m_settings.m_colorMask = colorMask;
1683     break;
1684   }
1685   case eSound:
1686     // emit soundEnabled(isChecked(eSound));
1687     break;
1688 
1689   case eWhiteBg:
1690   case eBlackBg:
1691   case eCheckBg:
1692     m_settings.m_bg       = (EGadget)button;
1693     FlipBookWhiteBgToggle = isChecked(eWhiteBg);
1694     FlipBookBlackBgToggle = isChecked(eBlackBg);
1695     FlipBookCheckBgToggle = isChecked(eCheckBg);
1696     break;
1697 
1698   case FlipConsole::eCompare:
1699     m_settings.m_doCompare = !m_settings.m_doCompare;
1700     break;
1701 
1702   case eHisto:
1703   case eSaveImg:
1704   case eSave:
1705   case eLocator:
1706     // nothing to do
1707     return;
1708 
1709   case eDefineSubCamera:
1710     // nothing to do
1711     return;
1712 
1713   case eDefineLoadBox:
1714     onLoadBox(true);
1715     break;
1716 
1717   case eUseLoadBox:
1718     onLoadBox(false);
1719     break;
1720 
1721   case eFilledRaster:
1722     return;
1723 
1724   case eFlipHorizontal:
1725   case eFlipVertical:
1726   case eZoomIn:
1727   case eZoomOut:
1728   case eResetView:
1729     return;
1730 
1731   default:
1732     assert(false);
1733     break;
1734   }
1735 
1736   m_currFrameSlider->setValue(m_currentFrame);
1737   m_editCurrFrame->setText(QString::number(m_currentFrame));
1738 
1739   m_consoleOwner->onDrawFrame(m_currentFrame, m_settings);
1740 }
1741 
1742 //--------------------------------------------------------------------
1743 
createFrameSlider()1744 QFrame *FlipConsole::createFrameSlider() {
1745   QFrame *frameSliderFrame = new QFrame(this);
1746 
1747   m_editCurrFrame = new DVGui::IntLineEdit(frameSliderFrame, m_currentFrame);
1748   m_editCurrFrame->setToolTip(tr("Set the current frame"));
1749   m_editCurrFrame->setFixedWidth(40);
1750 
1751   m_currFrameSlider = new FlipSlider(frameSliderFrame);
1752   m_currFrameSlider->setToolTip(tr("Drag to play the animation"));
1753 
1754   m_currFrameSlider->setRange(0, 0);
1755   m_currFrameSlider->setValue(0);
1756 
1757   if (m_drawBlanksEnabled) {
1758     m_enableBlankFrameButton = new QPushButton(this);
1759     m_enableBlankFrameButton->setCheckable(true);
1760     m_enableBlankFrameButton->setChecked(true);
1761 
1762     m_enableBlankFrameButton->setFixedHeight(24);
1763     m_enableBlankFrameButton->setFixedWidth(66);
1764     m_enableBlankFrameButton->setObjectName("enableBlankFrameButton");
1765   }
1766 
1767   // layout
1768   QHBoxLayout *frameSliderLayout = new QHBoxLayout();
1769   frameSliderLayout->setSpacing(5);
1770   frameSliderLayout->setMargin(2);
1771   {
1772     frameSliderLayout->addWidget(m_editCurrFrame, 0);
1773     frameSliderLayout->addWidget(m_currFrameSlider, 1);
1774     if (m_drawBlanksEnabled)
1775       frameSliderLayout->addWidget(m_enableBlankFrameButton, 0);
1776   }
1777   frameSliderFrame->setLayout(frameSliderLayout);
1778 
1779   connect(m_editCurrFrame, SIGNAL(editingFinished()), this,
1780           SLOT(OnSetCurrentFrame()));
1781   connect(m_currFrameSlider, SIGNAL(valueChanged(int)), this,
1782           SLOT(OnSetCurrentFrame(int)));
1783   connect(m_currFrameSlider, SIGNAL(flipSliderReleased()), this,
1784           SLOT(OnFrameSliderRelease()));
1785 
1786   return frameSliderFrame;
1787 }
1788 
1789 //--------------------------------------------------------------------
1790 
createFpsSlider()1791 QFrame *FlipConsole::createFpsSlider() {
1792   QFrame *fpsSliderFrame = new QFrame(this);
1793   // frame per second
1794   m_fpsLabel  = new QLabel(QString(" FPS -- /"), fpsSliderFrame);
1795   m_fpsSlider = new QScrollBar(Qt::Horizontal, fpsSliderFrame);
1796   m_fpsField  = new DVGui::IntLineEdit(fpsSliderFrame, m_fps, -60, 60);
1797   m_fpsField->setFixedWidth(40);
1798 
1799   m_fpsLabel->setMinimumWidth(m_fpsLabel->fontMetrics().width("_FPS_24___"));
1800   m_fpsLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
1801   m_fpsSlider->setObjectName("ViewerFpsSlider");
1802   m_fpsSlider->setRange(-60, 60);
1803   m_fpsSlider->setValue(m_fps);
1804   m_fpsSlider->setToolTip(tr("Set the playback frame rate"));
1805   m_fpsSlider->setContextMenuPolicy(Qt::NoContextMenu);
1806 
1807   QHBoxLayout *hLay = new QHBoxLayout();
1808   hLay->setSpacing(0);
1809   hLay->setMargin(0);
1810   {
1811     hLay->addWidget(m_fpsLabel, 0);
1812     hLay->addWidget(m_fpsField, 0);
1813     hLay->addWidget(m_fpsSlider, 1);
1814   }
1815   fpsSliderFrame->setLayout(hLay);
1816 
1817   connect(m_fpsSlider, SIGNAL(valueChanged(int)), this,
1818           SLOT(setCurrentFPS(int)));
1819   connect(m_fpsField, SIGNAL(editingFinished()), this, SLOT(onFPSEdited()));
1820 
1821   return fpsSliderFrame;
1822 }
1823 
1824 //--------------------------------------------------------------------
1825 
onFPSEdited(void)1826 void FlipConsole::onFPSEdited(void) {
1827   // this will emit fpsSlider->ValueChanged as well
1828   m_fpsSlider->setValue(m_fpsField->getValue());
1829 }
1830 
1831 //--------------------------------------------------------------------
1832 
setFrameRange(int from,int to,int step,int current)1833 void FlipConsole::setFrameRange(int from, int to, int step, int current) {
1834   if (from != m_from || to != m_to || step != m_step) {
1835     m_from = from;
1836     m_to   = to;
1837     m_step = step;
1838     m_to -= (m_to - m_from) % m_step;
1839     m_framesCount = (m_to - m_from) / m_step + 1;
1840     m_currFrameSlider->blockSignals(true);
1841     // m_currFrameSlider->setRange(0, m_framesCount-1);
1842     m_currFrameSlider->setRange(m_from, m_to);
1843     m_currFrameSlider->setSingleStep(m_step);
1844     m_currFrameSlider->blockSignals(false);
1845   }
1846 
1847   if (m_playbackExecutor.isRunning() ||
1848       m_isLinkedPlaying)  // if in playing mode, the slider and the frame
1849                           // field are already set in the timer!
1850     return;
1851 
1852   // limit the current frame in the range from-to
1853   if (current < from)
1854     current = from;
1855   else if (current > to)
1856     current = to;
1857 
1858   m_currFrameSlider->blockSignals(true);
1859   setCurrentFrame(current);
1860   m_currFrameSlider->blockSignals(false);
1861 }
1862 
1863 //--------------------------------------------------------------------
1864 
incrementCurrentFrame(int delta)1865 void FlipConsole::incrementCurrentFrame(int delta) {
1866   m_currentFrame = m_currentFrame + delta;
1867   if (m_currentFrame < m_from)
1868     m_currentFrame += m_to - m_from + 1;
1869   else if (m_currentFrame > m_to)
1870     m_currentFrame -= m_to - m_from + 1;
1871 
1872   m_editCurrFrame->setText(QString::number(m_currentFrame));
1873   m_currFrameSlider->setValue(m_currentFrame);
1874 
1875   m_consoleOwner->onDrawFrame(m_currentFrame, m_settings);
1876 }
1877 
1878 //--------------------------------------------------------------------
1879 
OnSetCurrentFrame()1880 void FlipConsole::OnSetCurrentFrame() {
1881   int newFrame = m_editCurrFrame->text().toInt();
1882   if (m_step > 1) {
1883     newFrame -= ((newFrame - m_from) % m_step);
1884     m_editCurrFrame->setText(QString::number(newFrame));
1885   }
1886 
1887   int i, deltaFrame = newFrame - m_currentFrame;
1888 
1889   if (m_framesCount == 0) m_editCurrFrame->setText(QString::number(1));
1890 
1891   if (m_framesCount == 0 || newFrame == m_currentFrame || newFrame == 0) return;
1892 
1893   if (newFrame > m_to) {
1894     m_editCurrFrame->setText(QString::number(m_currentFrame));
1895     return;
1896   }
1897 
1898   m_currentFrame = newFrame;
1899   m_currFrameSlider->setValue(m_currentFrame);
1900 
1901   m_consoleOwner->onDrawFrame(m_currentFrame, m_settings);
1902 
1903   if (m_areLinked)
1904     for (i = 0; i < m_visibleConsoles.size(); i++) {
1905       FlipConsole *console = m_visibleConsoles.at(i);
1906       if (console->m_isLinkable && console != this)
1907         console->incrementCurrentFrame(deltaFrame);
1908     }
1909 }
1910 
1911 //--------------------------------------------------------------------
1912 
onSliderRelease()1913 void FlipConsole::onSliderRelease() { emit sliderReleased(); }
1914 
1915 //--------------------------------------------------------------------
OnFrameSliderRelease()1916 void FlipConsole::OnFrameSliderRelease() {
1917   m_settings.m_recomputeIfNeeded = true;
1918   m_currentFrame                 = -1;
1919   OnSetCurrentFrame();
1920 }
1921 
OnFrameSliderPress()1922 void FlipConsole::OnFrameSliderPress() {
1923   m_settings.m_recomputeIfNeeded = false;
1924 }
1925 
1926 //---------------------
1927 //----------------------------------
1928 
OnSetCurrentFrame(int index)1929 void FlipConsole::OnSetCurrentFrame(int index) {
1930   if (m_framesCount == 0) return;
1931 
1932   if (index == m_currentFrame) return;
1933 
1934   int deltaFrame = index - m_currentFrame;
1935 
1936   m_currentFrame = index;
1937 
1938   assert(m_currentFrame <= m_to);
1939   m_editCurrFrame->setText(QString::number(m_currentFrame));
1940 
1941   m_consoleOwner->onDrawFrame(m_currentFrame, m_settings);
1942 
1943   if (m_areLinked)
1944     for (int i = 0; i < m_visibleConsoles.size(); i++) {
1945       FlipConsole *console = m_visibleConsoles.at(i);
1946       if (console->m_isLinkable && console != this)
1947         console->incrementCurrentFrame(deltaFrame);
1948     }
1949 }
1950 
1951 //--------------------------------------------------------------------
1952 
setCurrentFrame(int frame,bool forceResetting)1953 void FlipConsole::setCurrentFrame(int frame, bool forceResetting) {
1954   m_currentFrame = (frame == -1) ? m_from : frame;
1955   if ((m_playbackExecutor.isRunning() || m_isLinkedPlaying) &&
1956       !forceResetting)  // if in playing mode, the slider and the frame field
1957                         // are already set in the timer!
1958     return;
1959 
1960   m_editCurrFrame->setValue(m_currentFrame);
1961   m_currFrameSlider->setValue(m_currentFrame);
1962 }
1963 
1964 //--------------------------------------------------------------------
1965 
enableProgressBar(bool enable)1966 void FlipConsole::enableProgressBar(bool enable) {
1967   m_currFrameSlider->setProgressBarEnabled(enable);
1968 }
1969 
1970 //--------------------------------------------------------------------
1971 
setProgressBarStatus(const std::vector<UCHAR> * pbStatus)1972 void FlipConsole::setProgressBarStatus(const std::vector<UCHAR> *pbStatus) {
1973   m_currFrameSlider->setProgressBarStatus(pbStatus);
1974 }
1975 
1976 //--------------------------------------------------------------------
1977 
getProgressBarStatus() const1978 const std::vector<UCHAR> *FlipConsole::getProgressBarStatus() const {
1979   return m_currFrameSlider->getProgressBarStatus();
1980 }
1981 
1982 //--------------------------------------------------------------------
1983 
onPreferenceChanged(const QString & prefName)1984 void FlipConsole::onPreferenceChanged(const QString &prefName) {
1985   // react only when related properties are changed
1986   if (prefName != "BlankCount" && prefName != "BlankColor" &&
1987       !prefName.isEmpty())
1988     return;
1989 
1990   if (m_drawBlanksEnabled) {
1991     Preferences::instance()->getBlankValues(m_blanksCount, m_blankColor);
1992     if (m_blanksCount == 0) {
1993       if (m_enableBlankFrameButton->isVisible())
1994         m_enableBlankFrameButton->hide();
1995     } else {
1996       if (m_enableBlankFrameButton->isHidden())
1997         m_enableBlankFrameButton->show();
1998       QString buttonText = QString("+%1 Blank").arg(m_blanksCount);
1999       if (m_blanksCount > 1) buttonText += "s";
2000       m_enableBlankFrameButton->setText(buttonText);
2001 
2002       // Set text color based on luminescence of blankColor color
2003       QString textColor;
2004       double luminescence =
2005           ((0.299 * (int)m_blankColor.r) + (0.587 * (int)m_blankColor.g) +
2006            (0.114 * (int)m_blankColor.b)) /
2007           255;
2008       if (luminescence > 0.5)
2009         textColor = QString("black");
2010       else
2011         textColor = QString("white");
2012 
2013       m_enableBlankFrameButton->setStyleSheet(
2014           QString("#enableBlankFrameButton:checked { \
2015               background-color: rgb(%1,%2,%3); \
2016               color: %4;}")
2017               .arg(m_blankColor.r)
2018               .arg(m_blankColor.g)
2019               .arg(m_blankColor.b)
2020               .arg(textColor));
2021       m_enableBlankFrameButton->update();
2022     }
2023   }
2024 }
2025 
2026 //====================================================================
2027 
2028 class FlipConsoleActionsCreator : AuxActionsCreator {
createToggleAction(QObject * parent,const char * cmdId,const char * name,int buttonId)2029   void createToggleAction(QObject *parent, const char *cmdId, const char *name,
2030                           int buttonId) {
2031     QAction *action = new QAction(name, parent);
2032     action->setData(QVariant(buttonId));
2033     CommandManager::instance()->define(cmdId, MiscCommandType, "", action);
2034   }
2035 
2036 public:
createActions(QObject * parent)2037   void createActions(QObject *parent) override {
2038     /*createToggleAction(parent, "A_Flip_Play",  "Play",  FlipConsole::ePlay);
2039 createToggleAction(parent, "A_Flip_Pause", "Pause", FlipConsole::ePause);
2040 createToggleAction(parent, "A_Flip_Loop",  "Loop",  FlipConsole::eLoop);*/
2041   }
2042 } flipConsoleActionsCreator;
2043 
2044 //--------------------------------------------------------------------
2045