1 
2 
3 #include "filmstrip.h"
4 
5 // Tnz6 includes
6 #include "tapp.h"
7 #include "filmstripcommand.h"
8 #include "frameheadgadget.h"
9 #include "floatingpanelcommand.h"
10 #include "menubarcommandids.h"
11 #include "filmstripselection.h"
12 #include "onionskinmaskgui.h"
13 #include "comboviewerpane.h"
14 
15 // TnzQt includes
16 #include "toonzqt/icongenerator.h"
17 #include "toonzqt/trepetitionguard.h"
18 #include "toonzqt/gutil.h"
19 #include "toonzqt/tselectionhandle.h"
20 
21 // TnzLib includes
22 #include "toonz/txshlevelhandle.h"
23 #include "toonz/tcolumnhandle.h"
24 #include "toonz/txsheethandle.h"
25 #include "toonz/tframehandle.h"
26 #include "toonz/tonionskinmaskhandle.h"
27 #include "toonz/tobjecthandle.h"
28 #include "toonz/txshleveltypes.h"
29 #include "toonz/txshsimplelevel.h"
30 #include "toonz/stage2.h"
31 #include "toonz/levelproperties.h"
32 #include "toonz/palettecontroller.h"
33 #include "toonz/tpalettehandle.h"
34 #include "toonz/tscenehandle.h"
35 #include "toonz/toonzscene.h"
36 #include "toonz/levelset.h"
37 #include "toonz/preferences.h"
38 
39 // TnzCore includes
40 #include "tpalette.h"
41 
42 // Qt includes
43 #include <QPainter>
44 #include <QScrollBar>
45 #include <QComboBox>
46 #include <QPushButton>
47 #include <QSettings>
48 #include <QApplication>
49 #include <QMainWindow>
50 #include <QMimeData>
51 #include <QDrag>
52 
53 namespace {
fidToFrameNumberWithLetter(int f)54 QString fidToFrameNumberWithLetter(int f) {
55   QString str = QString::number((int)(f / 10));
56   while (str.length() < 3) str.push_front("0");
57   switch (f % 10) {
58   case 1:
59     str.append('A');
60     break;
61   case 2:
62     str.append('B');
63     break;
64   case 3:
65     str.append('C');
66     break;
67   case 4:
68     str.append('D');
69     break;
70   case 5:
71     str.append('E');
72     break;
73   case 6:
74     str.append('F');
75     break;
76   case 7:
77     str.append('G');
78     break;
79   case 8:
80     str.append('H');
81     break;
82   case 9:
83     str.append('I');
84     break;
85   default:
86     str.append(' ');
87     break;
88   }
89   return str;
90 }
91 }  // namespace
92 
93 //=============================================================================
94 // Filmstrip
95 //-----------------------------------------------------------------------------
96 
97 #if QT_VERSION >= 0x050500
FilmstripFrames(QScrollArea * parent,Qt::WindowFlags flags)98 FilmstripFrames::FilmstripFrames(QScrollArea *parent, Qt::WindowFlags flags)
99 #else
100 FilmstripFrames::FilmstripFrames(QScrollArea *parent, Qt::WFlags flags)
101 #endif
102     : QFrame(parent, flags)
103     , m_scrollArea(parent)
104     , m_selection(new TFilmstripSelection())
105     , m_frameHeadGadget(0)
106     , m_inbetweenDialog(0)
107     , m_pos(0, 0)
108     , m_iconSize(dimension2QSize(IconGenerator::instance()->getIconSize()))
109     , m_frameLabelWidth(11)
110     , m_selectingRange(0, 0)
111     , m_scrollSpeed(0)
112     , m_dragSelectionStartIndex(-1)
113     , m_dragSelectionEndIndex(-1)
114     , m_timerId(0)
115     , m_selecting(false)
116     , m_dragDropArmed(false)
117     , m_readOnly(false) {
118   setObjectName("filmStripFrames");
119   setFrameStyle(QFrame::StyledPanel);
120 
121   setFocusPolicy(Qt::StrongFocus);
122   if (m_isVertical) {
123     setFixedWidth(m_iconSize.width() + fs_leftMargin + fs_rightMargin +
124                   fs_iconMarginLR * 2);
125     setFixedHeight(parentWidget()->height());
126   } else {
127     setFixedHeight(parentWidget()->height());
128     setFixedWidth(parentWidget()->width());
129   }
130 
131   // la testa mobile che indica il frame corrente (e gestisce la GUI dell'onion
132   // skin)
133   m_frameHeadGadget = new FilmstripFrameHeadGadget(this);
134   installEventFilter(m_frameHeadGadget);
135 
136   m_selection->setView(this);
137   setMouseTracking(true);
138 
139   m_viewer = NULL;
140 }
141 
142 //-----------------------------------------------------------------------------
143 
~FilmstripFrames()144 FilmstripFrames::~FilmstripFrames() {
145   delete m_selection;
146   delete m_frameHeadGadget;
147 }
148 
149 //-----------------------------------------------------------------------------
150 
getLevel() const151 TXshSimpleLevel *FilmstripFrames::getLevel() const {
152   TXshLevel *xl = TApp::instance()->getCurrentLevel()->getLevel();
153   return xl ? xl->getSimpleLevel() : 0;
154 }
155 
156 //-----------------------------------------------------------------------------
157 
y2index(int y) const158 int FilmstripFrames::y2index(int y) const {
159   const int dy = getIconSize().height() + fs_frameSpacing + fs_iconMarginTop +
160                  fs_iconMarginBottom;
161   return y / dy;
162 }
163 
164 //-----------------------------------------------------------------------------
165 
x2index(int x) const166 int FilmstripFrames::x2index(int x) const {
167   const int dx = getIconSize().width() + fs_frameSpacing + fs_iconMarginLR +
168                  fs_leftMargin + fs_rightMargin;
169   return x / dx;
170 }
171 
172 //-----------------------------------------------------------------------------
173 
index2y(int index) const174 int FilmstripFrames::index2y(int index) const {
175   const int dy = getIconSize().height() + fs_frameSpacing + fs_iconMarginTop +
176                  fs_iconMarginBottom;
177   return index * dy;
178 }
179 
180 //-----------------------------------------------------------------------------
181 
index2x(int index) const182 int FilmstripFrames::index2x(int index) const {
183   const int dx = getIconSize().width() + fs_frameSpacing + fs_iconMarginLR +
184                  fs_leftMargin + fs_rightMargin;
185   return index * dx;
186 }
187 
188 //-----------------------------------------------------------------------------
189 
index2fid(int index) const190 TFrameId FilmstripFrames::index2fid(int index) const {
191   TXshSimpleLevel *sl = getLevel();
192   if (!sl || index < 0) return TFrameId();
193   return sl->index2fid(index);
194 }
195 
196 //-----------------------------------------------------------------------------
197 
fid2index(const TFrameId & fid) const198 int FilmstripFrames::fid2index(const TFrameId &fid) const {
199   TXshSimpleLevel *sl = getLevel();
200   if (!sl) return -1;
201   return sl->guessIndex(fid);  // ATTENZIONE: dovrebbe usare fid2index()
202 }
203 
204 //-----------------------------------------------------------------------------
205 
getFramesHeight() const206 int FilmstripFrames::getFramesHeight() const {
207   TXshSimpleLevel *level = getLevel();
208   int frameCount         = level ? level->getFrameCount() : 1;
209   int frameHeight = m_iconSize.height() + fs_frameSpacing + fs_iconMarginTop +
210                     fs_iconMarginBottom;
211   return frameHeight * (frameCount + 1);
212 }
213 
214 //-----------------------------------------------------------------------------
215 
getFramesWidth() const216 int FilmstripFrames::getFramesWidth() const {
217   TXshSimpleLevel *level = getLevel();
218   int frameCount         = level ? level->getFrameCount() : 1;
219   int frameWidth = m_iconSize.width() + fs_frameSpacing + fs_leftMargin +
220                    fs_rightMargin + fs_iconMarginLR;
221   return frameWidth * (frameCount + 1);
222 }
223 
224 //-----------------------------------------------------------------------------
225 
getOneFrameHeight()226 int FilmstripFrames::getOneFrameHeight() {
227   return m_iconSize.height() + fs_frameSpacing + fs_iconMarginTop +
228          fs_iconMarginBottom;
229 }
230 
231 //-----------------------------------------------------------------------------
232 
getOneFrameWidth()233 int FilmstripFrames::getOneFrameWidth() {
234   return m_iconSize.width() + fs_frameSpacing + fs_leftMargin +
235          fs_iconMarginLR + fs_rightMargin;
236 }
237 
238 //-----------------------------------------------------------------------------
updateContentHeight(int minimumHeight)239 void FilmstripFrames::updateContentHeight(int minimumHeight) {
240   if (minimumHeight < 0)
241     minimumHeight = visibleRegion().boundingRect().bottom();
242   int contentHeight = getFramesHeight();
243   if (contentHeight < minimumHeight) contentHeight = minimumHeight;
244   int parentHeight = parentWidget()->height();
245   if (contentHeight < parentHeight) contentHeight = parentHeight;
246   if (contentHeight != height()) setFixedHeight(contentHeight);
247 }
248 
249 //-----------------------------------------------------------------------------
updateContentWidth(int minimumWidth)250 void FilmstripFrames::updateContentWidth(int minimumWidth) {
251   setFixedHeight(getOneFrameHeight());
252   if (minimumWidth < 0) minimumWidth = visibleRegion().boundingRect().right();
253   int contentWidth = getFramesWidth();
254   if (contentWidth < minimumWidth) contentWidth = minimumWidth;
255   int parentWidth = parentWidget()->width();
256   if (contentWidth < parentWidth) contentWidth = parentWidth;
257   if (contentWidth != width()) setFixedWidth(contentWidth);
258 }
259 
260 //-----------------------------------------------------------------------------
261 
showFrame(int index)262 void FilmstripFrames::showFrame(int index) {
263   TXshSimpleLevel *level = getLevel();
264   if (m_isVertical) {
265     if (!level->isFid(index2fid(index))) {
266       if (!level->isFid(index2fid(index))) return;
267     }
268     int y0 = index2y(index);
269     int y1 = y0 + m_iconSize.height() + fs_frameSpacing + fs_iconMarginTop +
270              fs_iconMarginBottom;
271     if (y1 > height()) setFixedHeight(y1);
272     m_scrollArea->ensureVisible(0, (y0 + y1) / 2, 50, (y1 - y0) / 2);
273   } else {
274     if (!level->isFid(index2fid(index))) {
275       if (!level->isFid(index2fid(index - 1))) return;
276     }
277     int x0 = index2x(index);
278     int x1 = x0 + m_iconSize.width() + fs_frameSpacing + fs_leftMargin +
279              fs_rightMargin + fs_iconMarginLR;
280     if (x1 > width()) setFixedWidth(x1);
281     m_scrollArea->ensureVisible((x0 + x1) / 2, 0, (x1 - x0) / 2, 50);
282   }
283 }
284 
285 //-----------------------------------------------------------------------------
286 
scroll(int dy)287 void FilmstripFrames::scroll(int dy) {
288   if (m_isVertical) {
289     QScrollBar *sb = m_scrollArea->verticalScrollBar();
290     int sbValue    = sb->value();
291 
292     updateContentHeight(getFramesHeight());
293     if (sbValue + dy > getFramesHeight()) {
294       sb->setValue(getFramesHeight());
295       return;
296     }
297     sb->setValue(sbValue + dy);
298   } else {
299     QScrollBar *sb = m_scrollArea->horizontalScrollBar();
300     int sbValue    = sb->value();
301 
302     updateContentWidth(getFramesWidth());
303     if (sbValue + dy > getFramesWidth()) {
304       sb->setValue(getFramesWidth());
305       return;
306     }
307     sb->setValue(sbValue + dy);
308   }
309 }
310 
311 //---------------------------------------------------------------------------
312 
mouseDoubleClickEvent(QMouseEvent * event)313 void FilmstripFrames::mouseDoubleClickEvent(QMouseEvent *event) {
314   int index;
315   if (m_isVertical) {
316     index = y2index(event->pos().y());
317   } else {
318     index = x2index(event->pos().x());
319   }
320   select(index, ONLY_SELECT);  // ONLY_SELECT
321 }
322 
323 //-----------------------------------------------------------------------------
324 
select(int index,SelectionMode mode)325 void FilmstripFrames::select(int index, SelectionMode mode) {
326   TXshSimpleLevel *sl = getLevel();
327   bool outOfRange     = !sl || index < 0 || index >= sl->getFrameCount();
328 
329   TFrameId fid;
330   if (!outOfRange) fid = index2fid(index);
331 
332   switch (mode) {
333   // select one frame only
334   case ONLY_SELECT:
335     m_selection->selectNone();
336     if (!outOfRange) m_selection->select(fid);
337     break;
338   case SIMPLE_SELECT:
339     // Bail out if fid is already selected
340     if (!outOfRange && m_selection->isSelected(fid)) return;
341 
342     m_selection->selectNone();
343     if (!outOfRange) m_selection->select(fid);
344     break;
345 
346   case SHIFT_SELECT:
347     if (outOfRange) return;
348 
349     // Bail out if fid is already selected
350     if (m_selection->isSelected(fid)) return;
351 
352     if (m_selection->isEmpty())
353       m_selection->select(fid);
354     else {
355       TXshSimpleLevel *sl = getLevel();
356       if (!sl) return;
357 
358       // seleziono il range da fid al piu' vicino frame selezionato (in entrambe
359       // le direzioni)
360       int frameCount = sl->getFrameCount();
361 
362       // calcolo il limite inferiore della selezione
363       int ia = index;
364       while (ia > 0 && !m_selection->isSelected(sl->index2fid(ia - 1))) ia--;
365       if (ia == 0) ia = index;
366 
367       // calcolo il limite superiore della selezione
368       int ib = index;
369       while (ib < frameCount - 1 &&
370              !m_selection->isSelected(sl->index2fid(ib + 1)))
371         ib++;
372       if (ib == frameCount - 1) ib = index;
373 
374       // seleziono
375       for (int i = ia; i <= ib; i++) m_selection->select(sl->index2fid(i));
376     }
377     break;
378 
379   case CTRL_SELECT:
380     if (outOfRange) return;
381 
382     m_selection->select(fid, !m_selection->isSelected(fid));
383     break;
384 
385   case START_DRAG_SELECT:
386     m_selection->selectNone();
387 
388     if (outOfRange) {
389       m_dragSelectionStartIndex = m_dragSelectionEndIndex = -1;
390 
391       break;
392     }
393 
394     m_selection->select(fid);
395     m_dragSelectionStartIndex = index;
396     break;
397 
398   case DRAG_SELECT:
399     if (outOfRange || m_dragSelectionStartIndex < 0 ||
400         m_dragSelectionEndIndex == index)
401       return;
402 
403     m_dragSelectionEndIndex = index;
404 
405     m_selection->selectNone();
406 
407     int ia = m_dragSelectionStartIndex;
408     int ib = index;
409 
410     if (ia > ib) std::swap(ia, ib);
411 
412     for (int i = ia; i <= ib; ++i) m_selection->select(index2fid(i));
413 
414     break;
415   }
416 
417   TObjectHandle *objectHandle = TApp::instance()->getCurrentObject();
418   if (objectHandle->isSpline()) objectHandle->setIsSpline(false);
419 
420   // Update current selection
421   m_selection->makeCurrent();
422 
423   TSelectionHandle *selHandle = TApp::instance()->getCurrentSelection();
424   selHandle->notifySelectionChanged();
425 }
426 
427 //-----------------------------------------------------------------------------
428 
showEvent(QShowEvent *)429 void FilmstripFrames::showEvent(QShowEvent *) {
430   TApp *app = TApp::instance();
431 
432   // cambiamenti al livello
433   TXshLevelHandle *levelHandle = app->getCurrentLevel();
434   bool ret                     = true;
435   ret = ret && connect(levelHandle, SIGNAL(xshLevelSwitched(TXshLevel *)), this,
436                        SLOT(onLevelSwitched(TXshLevel *)));
437   ret = ret && connect(levelHandle, SIGNAL(xshLevelChanged()), this,
438                        SLOT(onLevelChanged()));
439   ret = ret && connect(levelHandle, SIGNAL(xshLevelViewChanged()), this,
440                        SLOT(onLevelChanged()));
441 
442   // al frame corrente
443   ret = ret && connect(app->getCurrentFrame(), SIGNAL(frameSwitched()), this,
444                        SLOT(onFrameSwitched()));
445   ret = ret && connect(app->getCurrentFrame(), SIGNAL(frameTypeChanged()), this,
446                        SLOT(update()));
447 
448   // iconcine
449   ret = ret && connect(IconGenerator::instance(), SIGNAL(iconGenerated()), this,
450                        SLOT(update()));
451 
452   // onion skin
453   ret = ret && connect(app->getCurrentOnionSkin(),
454                        SIGNAL(onionSkinMaskChanged()), this, SLOT(update()));
455 
456   // active viewer change
457   ret = ret &&
458         connect(app, SIGNAL(activeViewerChanged()), this, SLOT(getViewer()));
459 
460   assert(ret);
461   getViewer();
462 }
463 
464 //-----------------------------------------------------------------------------
465 
hideEvent(QHideEvent *)466 void FilmstripFrames::hideEvent(QHideEvent *) {
467   TApp *app = TApp::instance();
468 
469   // cambiamenti al livello
470   disconnect(app->getCurrentLevel(), 0, this, 0);
471 
472   // al frame corrente
473   disconnect(app->getCurrentFrame(), SIGNAL(frameSwitched()), this,
474              SLOT(onFrameSwitched()));
475   disconnect(app->getCurrentFrame(), SIGNAL(frameTypeChanged()), this,
476              SLOT(update()));
477 
478   // iconcine
479   disconnect(IconGenerator::instance(), SIGNAL(iconGenerated()), this,
480              SLOT(update()));
481 
482   // onion skin
483   disconnect(app->getCurrentOnionSkin(), SIGNAL(onionSkinMaskChanged()), this,
484              SLOT(update()));
485 
486   // active viewer change
487   disconnect(app, SIGNAL(activeViewerChanged()), this, SLOT(getViewer()));
488 
489   if (m_viewer) {
490     disconnect(m_viewer, SIGNAL(onZoomChanged()), this, SLOT(update()));
491     disconnect(m_viewer, SIGNAL(refreshNavi()), this, SLOT(update()));
492     m_viewer = nullptr;
493   }
494 }
495 
496 //-----------------------------------------------------------------------------
497 
getViewer()498 void FilmstripFrames::getViewer() {
499   bool viewerChanged = false;
500   if (m_viewer != TApp::instance()->getActiveViewer()) {
501     if (m_viewer) {
502       disconnect(m_viewer, SIGNAL(onZoomChanged()), this, SLOT(update()));
503       disconnect(m_viewer, SIGNAL(refreshNavi()), this, SLOT(update()));
504       disconnect(m_viewer, SIGNAL(aboutToBeDestroyed()), this,
505                  SLOT(onViewerAboutToBeDestroyed()));
506     }
507     viewerChanged = true;
508   }
509 
510   m_viewer = TApp::instance()->getActiveViewer();
511 
512   if (m_viewer && viewerChanged) {
513     connect(m_viewer, SIGNAL(onZoomChanged()), this, SLOT(update()));
514     connect(m_viewer, SIGNAL(refreshNavi()), this, SLOT(update()));
515     connect(m_viewer, SIGNAL(aboutToBeDestroyed()), this,
516             SLOT(onViewerAboutToBeDestroyed()));
517     update();
518   }
519 }
520 
521 //-----------------------------------------------------------------------------
522 
paintEvent(QPaintEvent * evt)523 void FilmstripFrames::paintEvent(QPaintEvent *evt) {
524   QPainter p(this);
525 
526   // p.setRenderHint(QPainter::Antialiasing, true);
527 
528   QRect clipRect = evt->rect();
529 
530   p.fillRect(clipRect, Qt::black);
531   // thumbnail rect, including offsets
532   QRect iconImgRect = QRect(QPoint(fs_leftMargin + fs_iconMarginLR,
533                                    fs_frameSpacing / 2 + fs_iconMarginTop),
534                             m_iconSize);
535   // frame size with margins
536   QSize frameSize = m_iconSize + QSize(fs_iconMarginLR * 2,
537                                        fs_iconMarginTop + fs_iconMarginBottom);
538   //  .. and with offset
539   QRect frameRect =
540       QRect(QPoint(fs_leftMargin, fs_frameSpacing / 2), frameSize);
541 
542   int oneFrameHeight = frameSize.height() + fs_frameSpacing;
543   int oneFrameWidth  = frameSize.width() + fs_frameSpacing;
544 
545   // visible frame index range
546   int i0, i1;
547   if (m_isVertical) {
548     i0 = y2index(clipRect.top());
549     i1 = y2index(clipRect.bottom());
550   } else {
551     i0 = x2index(clipRect.left());
552     i1 = x2index(clipRect.right());
553   }
554 
555   // fids, frameCount <- frames del livello
556   std::vector<TFrameId> fids;
557   TXshSimpleLevel *sl = getLevel();
558   if (sl)
559     sl->getFids(fids);
560   else {
561     for (int i = i0; i <= i1; i++) {
562       // draw white rectangles if obtaining the level is failed
563       QRect iconRect;
564       if (m_isVertical) {
565         iconRect = frameRect.translated(QPoint(0, oneFrameHeight * i));
566       } else {
567         iconRect = frameRect.translated(QPoint(oneFrameWidth * i, 0));
568       }
569       p.setBrush(QColor(192, 192, 192));
570       p.setPen(Qt::NoPen);
571       p.drawRect(iconRect);
572     }
573     return;
574   }
575 
576   //--- compute navigator rect ---
577 
578   QRect naviRect;
579 
580   if ((sl->getType() == TZP_XSHLEVEL || sl->getType() == OVL_XSHLEVEL) &&
581       m_viewer && m_viewer->isVisible()) {
582     // imgSize: image's pixel size
583     QSize imgSize(sl->getProperties()->getImageRes().lx,
584                   sl->getProperties()->getImageRes().ly);
585     // Viewer affine
586     TAffine viewerAff = m_viewer->getViewMatrix();
587     // pixel size which will be displayed with 100% scale in Viewer Stage
588     TFrameId currentId = TApp::instance()->getCurrentFrame()->getFid();
589     double imgPixelWidth =
590         (double)(imgSize.width()) / sl->getDpi(currentId).x * Stage::inch;
591     double imgPixelHeight =
592         (double)(imgSize.height()) / sl->getDpi(currentId).y * Stage::inch;
593 
594     // get the image's corner positions in viewer matrix (with current zoom
595     // scale)
596     TPointD imgTopRight =
597         viewerAff * TPointD(imgPixelWidth / 2.0f, imgPixelHeight / 2.0f);
598     TPointD imgBottomLeft =
599         viewerAff * TPointD(-imgPixelWidth / 2.0f, -imgPixelHeight / 2.0f);
600 
601     // pixel size in viewer matrix ( with current zoom scale )
602     QSizeF imgSizeInViewer(imgTopRight.x - imgBottomLeft.x,
603                            imgTopRight.y - imgBottomLeft.y);
604 
605     // ratio of the Viewer frame's position and size
606     QRectF naviRatio(
607         (-(float)m_viewer->width() * 0.5f - (float)imgBottomLeft.x) /
608             imgSizeInViewer.width(),
609         1.0f - ((float)m_viewer->height() * 0.5f - (float)imgBottomLeft.y) /
610                    imgSizeInViewer.height(),
611         (float)m_viewer->width() / imgSizeInViewer.width(),
612         (float)m_viewer->height() / imgSizeInViewer.height());
613 
614     naviRect = QRect(iconImgRect.left() +
615                          (int)(naviRatio.left() * (float)iconImgRect.width()),
616                      iconImgRect.top() +
617                          (int)(naviRatio.top() * (float)iconImgRect.height()),
618                      (int)((float)iconImgRect.width() * naviRatio.width()),
619                      (int)((float)iconImgRect.height() * naviRatio.height()));
620     // for drag move
621     m_naviRectPos = naviRect.center();
622 
623     naviRect = naviRect.intersected(frameRect);
624 
625     m_icon2ViewerRatio.setX(imgSizeInViewer.width() /
626                             (float)iconImgRect.width());
627     m_icon2ViewerRatio.setY(imgSizeInViewer.height() /
628                             (float)iconImgRect.height());
629   }
630 
631   //--- compute navigator rect end ---
632 
633   int frameCount = (int)fids.size();
634 
635   bool isReadOnly = false;
636   if (sl) isReadOnly = sl->isReadOnly();
637 
638   int i;
639   int iconWidth   = m_iconSize.width();
640   int x0          = m_frameLabelWidth;
641   int x1          = x0 + iconWidth;
642   int frameHeight = m_iconSize.height();
643 
644   // linee orizzontali che separano i frames
645   p.setPen(getLightLineColor());
646   for (i = i0; i <= i1; i++) {
647     if (m_isVertical) {
648       int y = index2y(i) + frameHeight;
649       p.drawLine(0, y, x1, y);
650     } else {
651       int x = index2x(i) + iconWidth;
652       p.drawLine(x, 0, x, frameHeight);
653     }
654   }
655 
656   TFilmstripSelection::InbetweenRange range = m_selection->getInbetweenRange();
657 
658   // draw for each frames
659   for (i = i0; i <= i1; i++) {
660     QRect tmp_iconImgRect, tmp_frameRect;
661     if (m_isVertical) {
662       tmp_iconImgRect = iconImgRect.translated(QPoint(0, oneFrameHeight * i));
663       tmp_frameRect   = frameRect.translated(QPoint(0, oneFrameHeight * i));
664     } else {
665       tmp_iconImgRect = iconImgRect.translated(QPoint(oneFrameWidth * i, 0));
666       tmp_frameRect   = frameRect.translated(QPoint(oneFrameWidth * i, 0));
667     }
668     bool isCurrentFrame =
669         (i == sl->fid2index(TApp::instance()->getCurrentFrame()->getFid()));
670     bool isSelected =
671         (0 <= i && i < frameCount && m_selection->isSelected(fids[i]));
672     TFrameId fid;
673     if (0 <= i && i < frameCount) {
674       fid = fids[i];
675       // normal or inbetween (for vector levels)
676       int flags = (sl->getType() == PLI_XSHLEVEL && range.first < fid &&
677                    fid < range.second)
678                       ? F_INBETWEEN_RANGE
679                       : F_NORMAL;
680 
681       // draw icons
682       drawFrameIcon(p, tmp_iconImgRect, i, fid, flags);
683 
684       p.setPen(Qt::NoPen);
685       p.setBrush(Qt::NoBrush);
686       p.drawRect(tmp_iconImgRect);
687 
688       // Frame number
689       if (m_selection->isSelected(fids[i])) {
690         if (TApp::instance()->getCurrentFrame()->isEditingLevel() &&
691             isCurrentFrame)
692           p.setPen(Qt::red);
693         else
694           p.setPen(Qt::white);
695       } else
696         p.setPen(QColor(192, 192, 192));
697 
698       p.setBrush(Qt::NoBrush);
699       // for single frame
700       QString text;
701       if (fid.getNumber() == TFrameId::EMPTY_FRAME ||
702           fid.getNumber() == TFrameId::NO_FRAME) {
703         text = QString("Single Frame");
704       }
705       // for sequencial frame (with letter)
706       else if (Preferences::instance()->isShowFrameNumberWithLettersEnabled()) {
707         text = fidToFrameNumberWithLetter(fid.getNumber());
708       }
709       // for sequencial frame
710       else {
711         char letter = fid.getLetter();
712         text        = QString::number(fid.getNumber()).rightJustified(4, '0') +
713                (letter != '\0' ? QString(letter) : "");
714       }
715       p.drawText(tmp_frameRect.adjusted(0, 0, -3, 2), text,
716                  QTextOption(Qt::AlignRight | Qt::AlignBottom));
717       p.setPen(Qt::NoPen);
718 
719       // Read-only frames (lock)
720       if (0 <= i && i < frameCount) {
721         if (sl->isFrameReadOnly(fids[i])) {
722           static QPixmap lockPixmap(":Resources/forbidden.png");
723           p.drawPixmap(tmp_frameRect.bottomLeft() + QPoint(3, -13), lockPixmap);
724         }
725       }
726     }
727 
728     // navigator rect
729     if (m_showNavigator && naviRect.isValid() && fid >= 0 &&
730         fid == getCurrentFrameId()) {
731       p.setPen(QPen(Qt::red, 1));
732       if (m_isVertical) {
733         p.drawRect(naviRect.translated(0, oneFrameHeight * i));
734       } else {
735         p.drawRect(naviRect.translated(oneFrameWidth * i, 0));
736       }
737       p.setPen(Qt::NoPen);
738     }
739 
740     // red frame for the current frame
741     if (TApp::instance()->getCurrentFrame()->isEditingLevel() &&
742         (isCurrentFrame || isSelected)) {
743       QPen pen;
744       pen.setColor(Qt::red);
745       pen.setWidth(2);
746       pen.setJoinStyle(Qt::RoundJoin);
747       p.setPen(pen);
748 
749       p.drawRect(tmp_frameRect.adjusted(-1, -1, 2, 2));
750       p.setPen(Qt::NoPen);
751     }
752   }
753 
754   // se sono in modalita' level edit faccio vedere la freccia che indica il
755   // frame corrente
756   if (TApp::instance()->getCurrentFrame()->isEditingLevel())
757     m_frameHeadGadget->draw(p, QColor(Qt::white), QColor(Qt::black));
758 }
759 
760 //-----------------------------------------------------------------------------
761 
drawFrameIcon(QPainter & p,const QRect & r,int index,const TFrameId & fid,int flags)762 void FilmstripFrames::drawFrameIcon(QPainter &p, const QRect &r, int index,
763                                     const TFrameId &fid, int flags) {
764   QPixmap pm;
765   TXshSimpleLevel *sl = getLevel();
766   if (sl) {
767     pm = IconGenerator::instance()->getIcon(sl, fid);
768   }
769   if (!pm.isNull()) {
770     p.drawPixmap(r.left(), r.top(), pm);
771 
772     if (sl && sl->getType() == PLI_XSHLEVEL && flags & F_INBETWEEN_RANGE) {
773       if (m_isVertical) {
774         int x1 = r.right();
775         int x0 = x1 - 12;
776         int y0 = r.top();
777         int y1 = r.bottom();
778         p.fillRect(x0, y0, x1 - x0 + 1, y1 - y0 + 1,
779                    QColor(180, 180, 180, 255));
780         p.setPen(Qt::black);
781         p.drawLine(x0 - 1, y0, x0 - 1, y1);
782 
783         QRectF txtRect(y0 + 1, -x1, y1 - y0 - 1, x1 - x0 + 1);
784         QFontMetricsF tmpFm(p.font());
785         QRectF bbox = tmpFm.boundingRect(
786             txtRect, Qt::AlignBottom | Qt::AlignHCenter, tr("INBETWEEN"));
787         double ratio = std::min(1.0, txtRect.width() / bbox.width());
788 
789         p.save();
790         p.setRenderHint(QPainter::TextAntialiasing);
791         p.rotate(90.0);
792         p.scale(ratio, 1.0);
793         p.drawText(QRectF(txtRect.left() / ratio, txtRect.top(),
794                           txtRect.width() / ratio, txtRect.height()),
795                    tr("INBETWEEN"),
796                    QTextOption(Qt::AlignBottom | Qt::AlignHCenter));
797         p.restore();
798       } else {
799         int x1 = r.right();
800         int x0 = r.left();
801         int y0 = r.bottom() - 15;
802         int y1 = r.bottom();
803         p.fillRect(x0, y0, x1 - x0 + 1, y1 - y0 + 1,
804                    QColor(180, 180, 180, 255));
805         p.setPen(Qt::black);
806         p.drawLine(x0, y0, x1, y0);
807         p.drawText(r, tr("INBETWEEN"),
808                    QTextOption(Qt::AlignBottom | Qt::AlignHCenter));
809       }
810     }
811   } else {
812     // non riesco (per qualche ragione) a visualizzare l'icona
813     p.fillRect(r, QColor(255, 200, 200));
814     p.setPen(Qt::black);
815     p.drawText(r, tr("no icon"), QTextOption(Qt::AlignCenter));
816   }
817 }
818 
enterEvent(QEvent * event)819 void FilmstripFrames::enterEvent(QEvent *event) { getViewer(); }
820 
821 //-----------------------------------------------------------------------------
822 
getCurrentFrameId()823 TFrameId FilmstripFrames::getCurrentFrameId() {
824   TApp *app        = TApp::instance();
825   TFrameHandle *fh = app->getCurrentFrame();
826   TFrameId currFid;
827   if (fh->isEditingLevel())
828     currFid = fh->getFid();
829   else {
830     TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
831     int col      = app->getCurrentColumn()->getColumnIndex();
832     int row      = fh->getFrame();
833     if (row < 0 || col < 0) return TFrameId();
834     TXshCell cell = xsh->getCell(row, col);
835     // if (cell.isEmpty()) return;
836     currFid = cell.getFrameId();
837   }
838   return currFid;
839 }
840 
841 //-----------------------------------------------------------------------------
842 
mousePressEvent(QMouseEvent * event)843 void FilmstripFrames::mousePressEvent(QMouseEvent *event) {
844   m_selecting = false;
845   int index   = 0;
846   if (m_isVertical) {
847     index = y2index(event->pos().y());
848   } else {
849     index = x2index(event->pos().x());
850   }
851   TFrameId fid = index2fid(index);
852 
853   TXshSimpleLevel *sl = getLevel();
854   int frameHeight = m_iconSize.height() + fs_frameSpacing + fs_iconMarginTop +
855                     fs_iconMarginBottom;
856   int frameWidth = m_iconSize.width() + fs_frameSpacing + fs_iconMarginLR +
857                    fs_leftMargin + fs_rightMargin;
858 
859   int i0;
860   QPoint clickedPos;
861   bool actualIconClicked;
862 
863   if (m_isVertical) {
864     i0         = y2index(0);
865     clickedPos = event->pos() - QPoint(0, (index - i0) * frameHeight);
866   } else {
867     i0         = x2index(0);
868     clickedPos = event->pos() - QPoint((index - i0) * frameWidth, 0);
869   }
870   actualIconClicked =
871       QRect(QPoint(fs_leftMargin + fs_iconMarginLR,
872                    fs_frameSpacing / 2 +
873                        fs_iconMarginTop)  //<- top-left position of the icon
874             ,
875             m_iconSize)
876           .contains(clickedPos);
877 
878   if (event->button() == Qt::LeftButton ||
879       event->button() == Qt::MiddleButton) {
880     // navigator pan
881     // make sure the viewer is visible and that a toonz raster or raster level
882     // is current
883     if (fid.getNumber() >= 0 && fid == getCurrentFrameId() &&
884         (sl->getType() == TZP_XSHLEVEL || sl->getType() == OVL_XSHLEVEL) &&
885         m_viewer && m_viewer->isVisible() && actualIconClicked &&
886         event->button() == Qt::MiddleButton) {
887       if (m_showNavigator) {
888         m_isNavigatorPanning = true;
889         execNavigatorPan(event->pos());
890         QApplication::setOverrideCursor(Qt::ClosedHandCursor);
891       }
892     } else
893       m_isNavigatorPanning = false;
894     // end of navigator section
895 
896     // return if frame empty or middle button pressed
897     if (fid == TFrameId() || event->button() == Qt::MiddleButton) {
898       m_justStartedSelection = false;
899       return;
900     }
901 
902     // was the inbetween button clicked?
903     bool inbetweenSelected = false;
904     if (m_isVertical)
905       inbetweenSelected = event->pos().x() > width() - 20 - fs_rightMargin;
906     else
907       inbetweenSelected =
908           event->pos().y() > height() - fs_iconMarginBottom - 20 &&
909           event->pos().y() < height() - fs_iconMarginBottom - fs_frameSpacing;
910 
911     // with shift or control
912     if (event->modifiers() & Qt::ShiftModifier) {
913       select(index, SHIFT_SELECT);
914       if (m_selection->isSelected(fid)) {
915         // If the frame is already selected enable
916         // drag'n'drop
917         m_dragDropArmed = true;
918         m_pos           = event->pos();
919       }
920     } else if (event->modifiers() & Qt::ControlModifier)
921       select(index, CTRL_SELECT);
922 
923     else if (sl->getType() == PLI_XSHLEVEL &&
924              m_selection->isInInbetweenRange(fid) && inbetweenSelected) {
925       inbetween();
926     } else {
927       // move current frame when clicked without modifier
928       TApp *tapp = TApp::instance();
929       std::vector<TFrameId> fids;
930       TXshLevel *level = tapp->getCurrentLevel()->getLevel();
931       level->getFids(fids);
932 
933       tapp->getCurrentFrame()->setFrameIds(fids);
934       tapp->getCurrentFrame()->setFid(fid);
935 
936       if (actualIconClicked &&
937           (!m_selection->isSelected(fid) || m_justStartedSelection)) {
938         // click on a non-selected frame
939         m_selecting = true;  // allow drag-select
940         select(index, START_DRAG_SELECT);
941       } else if (m_selection->isSelected(fid)) {
942         // if it's already selected - it can be drag and dropped
943         m_dragDropArmed = true;
944         m_pos           = event->pos();
945         // allow a the frame to be reselected if the mouse isn't moved far
946         // this is to enable a group selection to become a single selection
947         m_allowResetSelection    = true;
948         m_indexForResetSelection = index;
949       } else if (!actualIconClicked) {
950         // this allows clicking the frame number to trigger an instant drag
951         select(index, ONLY_SELECT);
952         m_dragDropArmed = true;
953         m_pos           = event->pos();
954       }
955     }
956     update();
957   } else if (event->button() == Qt::RightButton) {
958     select(index);
959   }
960   m_justStartedSelection = false;
961 }
962 
963 //-----------------------------------------------------------------------------
964 
execNavigatorPan(const QPoint & point)965 void FilmstripFrames::execNavigatorPan(const QPoint &point) {
966   int index = y2index(point.y());
967   if (!m_isVertical) index = x2index(point.x());
968   TFrameId fid = index2fid(index);
969   int i0       = y2index(0);
970   if (!m_isVertical) i0 = x2index(0);
971 
972   int frameHeight = m_iconSize.height() + fs_frameSpacing + fs_iconMarginTop +
973                     fs_iconMarginBottom;
974   int frameWidth = m_iconSize.width() + fs_frameSpacing + fs_iconMarginLR +
975                    fs_leftMargin + fs_rightMargin;
976   QPoint clickedPos = point - QPoint(0, (index - i0) * frameHeight);
977   if (!m_isVertical) clickedPos = point - QPoint((index - i0) * frameWidth, 0);
978 
979   if (fid != getCurrentFrameId()) return;
980 
981   QRect iconRect =
982       QRect(QPoint(fs_leftMargin + fs_iconMarginLR,
983                    fs_frameSpacing / 2 +
984                        fs_iconMarginTop)  //<- top-left position of the icon
985             ,
986             m_iconSize);
987 
988   QPointF delta = m_naviRectPos - clickedPos;
989 
990   if (iconRect.left() > clickedPos.x() || iconRect.right() < clickedPos.x())
991     delta.setX(0.0);
992   if (iconRect.top() > clickedPos.y() || iconRect.bottom() < clickedPos.y())
993     delta.setY(0.0);
994   if (delta.x() == 0.0 && delta.y() == 0.0) return;
995 
996   delta.setX(delta.x() * m_icon2ViewerRatio.x());
997   delta.setY(delta.y() * m_icon2ViewerRatio.y());
998 
999   if (m_viewer) m_viewer->navigatorPan(delta.toPoint());
1000 }
1001 
1002 //-----------------------------------------------------------------------------
1003 
mouseReleaseEvent(QMouseEvent * e)1004 void FilmstripFrames::mouseReleaseEvent(QMouseEvent *e) {
1005   stopAutoPanning();
1006   m_selecting          = false;
1007   m_dragDropArmed      = false;
1008   m_isNavigatorPanning = false;
1009   if (m_allowResetSelection) {
1010     select(m_indexForResetSelection, ONLY_SELECT);
1011     update();
1012   }
1013   m_allowResetSelection    = false;
1014   m_indexForResetSelection = -1;
1015   QApplication::restoreOverrideCursor();
1016 }
1017 
1018 //-----------------------------------------------------------------------------
1019 
mouseMoveEvent(QMouseEvent * e)1020 void FilmstripFrames::mouseMoveEvent(QMouseEvent *e) {
1021   QPoint pos = e->pos();
1022   int index  = y2index(e->pos().y());
1023   if (!m_isVertical) index = x2index(e->pos().x());
1024   if (e->buttons() & Qt::LeftButton || e->buttons() & Qt::MiddleButton) {
1025     // navigator pan
1026     if (m_showNavigator && m_isNavigatorPanning) {
1027       execNavigatorPan(e->pos());
1028       e->accept();
1029       return;
1030     }
1031     if (e->buttons() & Qt::MiddleButton) return;
1032     if (m_dragDropArmed) {
1033       if ((m_pos - e->pos()).manhattanLength() > 10) {
1034         startDragDrop();
1035         m_dragDropArmed       = false;
1036         m_allowResetSelection = false;
1037       }
1038     } else if (m_selecting) {
1039       m_pos = e->globalPos();
1040       select(index, DRAG_SELECT);
1041     }
1042 
1043     // autopan
1044     int speed = getOneFrameHeight() / 64;
1045     if (!m_isVertical) speed = getOneFrameWidth() / 64;
1046 
1047     QRect visibleRect = visibleRegion().boundingRect();
1048     int visibleTop    = visibleRect.top();
1049     int visibleBottom = visibleRect.bottom();
1050     if (m_isVertical) {
1051       if (pos.y() < visibleRect.top()) {
1052         m_scrollSpeed = -speed;
1053         if (visibleRect.top() - pos.y() > 30) {
1054           m_timerInterval = 50;
1055         } else if (visibleRect.top() - pos.y() > 15) {
1056           m_timerInterval = 150;
1057         } else {
1058           m_timerInterval = 300;
1059         }
1060       } else if (pos.y() > visibleRect.bottom()) {
1061         m_scrollSpeed = speed;
1062         if (pos.y() - visibleRect.bottom() > 30) {
1063           m_timerInterval = 50;
1064         } else if (pos.y() - visibleRect.bottom() > 15) {
1065           m_timerInterval = 150;
1066         } else {
1067           m_timerInterval = 300;
1068         }
1069       } else
1070         m_scrollSpeed = 0;
1071     } else {
1072       if (pos.x() < visibleRect.left()) {
1073         m_scrollSpeed = -speed;
1074         if (visibleRect.left() - pos.x() > 30) {
1075           m_timerInterval = 50;
1076         } else if (visibleRect.left() - pos.x() > 15) {
1077           m_timerInterval = 150;
1078         } else {
1079           m_timerInterval = 300;
1080         }
1081       } else if (pos.x() > visibleRect.right()) {
1082         m_scrollSpeed = speed;
1083         if (pos.x() - visibleRect.right() > 30) {
1084           m_timerInterval = 50;
1085         } else if (pos.x() - visibleRect.right() > 15) {
1086           m_timerInterval = 150;
1087         } else {
1088           m_timerInterval = 300;
1089         }
1090       } else
1091         m_scrollSpeed = 0;
1092     }
1093     if (m_scrollSpeed != 0) {
1094       startAutoPanning();
1095     } else
1096       stopAutoPanning();
1097     update();
1098   } else if (e->buttons() & Qt::MidButton) {
1099     // scroll con il tasto centrale
1100     pos = e->globalPos();
1101     if (m_isVertical) {
1102       scroll((m_pos.y() - pos.y()) * 10);
1103     } else {
1104       scroll((m_pos.x() - pos.x()) * 10);
1105     }
1106     m_pos = pos;
1107   } else {
1108     TFrameId fid        = index2fid(index);
1109     TXshSimpleLevel *sl = getLevel();
1110 
1111     if (m_isVertical && sl && m_selection && sl->getType() == PLI_XSHLEVEL &&
1112         m_selection->isInInbetweenRange(fid) &&
1113         e->pos().x() > width() - 20 - fs_rightMargin) {
1114       setToolTip(tr("Auto Inbetween"));
1115     }
1116     if (!m_isVertical && sl && m_selection && sl->getType() == PLI_XSHLEVEL &&
1117         m_selection->isInInbetweenRange(fid) &&
1118         e->pos().y() > height() - 15 - fs_iconMarginBottom &&
1119         e->pos().y() < height() - fs_iconMarginBottom - fs_frameSpacing) {
1120       setToolTip(tr("Auto Inbetween"));
1121     }
1122   }
1123 }
1124 
1125 //-----------------------------------------------------------------------------
1126 
keyPressEvent(QKeyEvent * event)1127 void FilmstripFrames::keyPressEvent(QKeyEvent *event) {
1128   TFrameHandle *fh       = TApp::instance()->getCurrentFrame();
1129   TXshSimpleLevel *level = getLevel();
1130   if (!level) return;
1131   std::vector<TFrameId> fids;
1132   level->getFids(fids);
1133   if (fids.empty()) return;
1134 
1135   // If on a level frame pass the frame id after the last frame to allow
1136   // creating a new frame with the down arrow key
1137   TFrameId newId = 0;
1138   if (Preferences::instance()->getDownArrowLevelStripNewFrame() &&
1139       fh->getFrameType() == TFrameHandle::LevelFrame) {
1140     int frameCount = (int)fids.size();
1141     newId          = index2fid(frameCount);
1142   }
1143 
1144   fh->setFrameIds(fids);
1145   if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Left)
1146     fh->prevFrame();
1147   else if (event->key() == Qt::Key_Down || event->key() == Qt::Key_Right)
1148     fh->nextFrame(newId);
1149   else if (event->key() == Qt::Key_Home)
1150     fh->firstFrame();
1151   else if (event->key() == Qt::Key_End)
1152     fh->lastFrame();
1153   else if (event->key() == Qt::Key_PageDown) {
1154     if (m_isVertical) {
1155       int frameHeight   = m_iconSize.height();
1156       int visibleHeight = visibleRegion().rects()[0].height();
1157       int visibleFrames = double(visibleHeight) / double(frameHeight);
1158       scroll(visibleFrames * frameHeight);
1159     } else {
1160       int frameWidth    = m_iconSize.width();
1161       int visibleWidth  = visibleRegion().rects()[0].width();
1162       int visibleFrames = double(visibleWidth) / double(frameWidth);
1163       scroll(visibleFrames * frameWidth);
1164     }
1165     return;
1166   } else if (event->key() == Qt::Key_PageUp) {
1167     if (m_isVertical) {
1168       int frameHeight   = m_iconSize.height();
1169       int visibleHeight = visibleRegion().rects()[0].height();
1170       int visibleFrames = double(visibleHeight) / double(frameHeight);
1171       scroll(-visibleFrames * frameHeight);
1172     } else {
1173       int frameWidth    = m_iconSize.width();
1174       int visibleWidth  = visibleRegion().rects()[0].width();
1175       int visibleFrames = double(visibleWidth) / double(frameWidth);
1176       scroll(-visibleFrames * frameWidth);
1177     }
1178     return;
1179   } else
1180     return;
1181 
1182   m_selection->selectNone();
1183   if (getLevel()) m_selection->select(fh->getFid());
1184   int index = fid2index(fh->getFid());
1185   if (index >= 0) showFrame(index);
1186 }
1187 
1188 //-----------------------------------------------------------------------------
1189 
wheelEvent(QWheelEvent * event)1190 void FilmstripFrames::wheelEvent(QWheelEvent *event) {
1191   scroll(-event->delta());
1192 }
1193 
1194 //-----------------------------------------------------------------------------
1195 
startAutoPanning()1196 void FilmstripFrames::startAutoPanning() {
1197   if (m_timerId == 0) m_timerId = startTimer(m_timerInterval);
1198 }
1199 
1200 //-----------------------------------------------------------------------------
1201 
stopAutoPanning()1202 void FilmstripFrames::stopAutoPanning() {
1203   if (m_timerId != 0) {
1204     killTimer(m_timerId);
1205     m_timerId     = 0;
1206     m_scrollSpeed = 0;
1207   }
1208 }
1209 
1210 //-----------------------------------------------------------------------------
1211 
timerEvent(QTimerEvent *)1212 void FilmstripFrames::timerEvent(QTimerEvent *) {
1213   scroll(m_scrollSpeed);
1214   // reset the timer in case m_scroll speed has changed
1215   killTimer(m_timerId);
1216   m_timerId = 0;
1217   m_timerId = startTimer(m_timerInterval);
1218   if (m_selecting) {
1219     QPoint pos = mapFromGlobal(m_pos);
1220     int index  = y2index(pos.y());
1221     if (!m_isVertical) index = x2index(pos.x());
1222     select(index, DRAG_SELECT);
1223     showFrame(index);
1224     update();
1225   }
1226 }
1227 
1228 //-----------------------------------------------------------------------------
1229 
contextMenuEvent(QContextMenuEvent * event)1230 void FilmstripFrames::contextMenuEvent(QContextMenuEvent *event) {
1231   QMenu *menu             = new QMenu();
1232   TXshSimpleLevel *sl     = getLevel();
1233   bool isSubsequenceLevel = (sl && sl->isSubsequence());
1234   bool isReadOnly         = (sl && sl->isReadOnly());
1235   CommandManager *cm      = CommandManager::instance();
1236 
1237   menu->addAction(cm->getAction(MI_SelectAll));
1238   menu->addAction(cm->getAction(MI_InvertSelection));
1239   menu->addSeparator();
1240   if (!isSubsequenceLevel && !isReadOnly) {
1241     menu->addAction(cm->getAction(MI_Cut));
1242   }
1243   menu->addAction(cm->getAction(MI_Copy));
1244 
1245   if (!isSubsequenceLevel && !isReadOnly) {
1246     menu->addAction(cm->getAction(MI_Paste));
1247     menu->addAction(cm->getAction(MI_PasteInto));
1248     menu->addAction(cm->getAction(MI_Insert));
1249     menu->addAction(cm->getAction(MI_Clear));
1250     menu->addSeparator();
1251     menu->addAction(cm->getAction(MI_Reverse));
1252     menu->addAction(cm->getAction(MI_Swing));
1253     menu->addAction(cm->getAction(MI_Step2));
1254     menu->addAction(cm->getAction(MI_Step3));
1255     menu->addAction(cm->getAction(MI_Step4));
1256     menu->addAction(cm->getAction(MI_Each2));
1257     menu->addAction(cm->getAction(MI_Each3));
1258     menu->addAction(cm->getAction(MI_Each4));
1259     menu->addSeparator();
1260     menu->addAction(cm->getAction(MI_Duplicate));
1261     menu->addAction(cm->getAction(MI_MergeFrames));
1262   }
1263   menu->addAction(cm->getAction(MI_ExposeResource));
1264   if (!isSubsequenceLevel && !isReadOnly) {
1265     menu->addAction(cm->getAction(MI_AddFrames));
1266     menu->addAction(cm->getAction(MI_Renumber));
1267     if (sl && sl->getType() == TZP_XSHLEVEL)
1268       menu->addAction(cm->getAction(MI_RevertToCleanedUp));
1269   }
1270   if (sl &&
1271       (sl->getType() == TZP_XSHLEVEL || sl->getType() == PLI_XSHLEVEL ||
1272        (sl->getType() == OVL_XSHLEVEL && sl->getPath().getType() != "gif" &&
1273         sl->getPath().getType() != "mp4" && sl->getPath().getType() != "webm")))
1274     menu->addAction(cm->getAction(MI_RevertToLastSaved));
1275   menu->addSeparator();
1276   createSelectLevelMenu(menu);
1277   QMenu *panelMenu           = menu->addMenu(tr("Panel Settings"));
1278   QAction *toggleOrientation = panelMenu->addAction(tr("Toggle Orientation"));
1279   QAction *hideComboBox = panelMenu->addAction(tr("Show/Hide Drop Down Menu"));
1280   QAction *hideNavigator =
1281       panelMenu->addAction(tr("Show/Hide Level Navigator"));
1282   hideComboBox->setCheckable(true);
1283   hideComboBox->setChecked(m_showComboBox);
1284   hideNavigator->setCheckable(true);
1285   hideNavigator->setChecked(m_showNavigator);
1286   connect(toggleOrientation, SIGNAL(triggered(bool)), this,
1287           SLOT(orientationToggled(bool)));
1288   connect(hideComboBox, SIGNAL(triggered(bool)), this,
1289           SLOT(comboBoxToggled(bool)));
1290   connect(hideNavigator, SIGNAL(triggered(bool)), this,
1291           SLOT(navigatorToggled(bool)));
1292 
1293   menu->exec(event->globalPos());
1294 }
1295 
1296 //-----------------------------------------------------------------------------
1297 
createSelectLevelMenu(QMenu * menu)1298 void FilmstripFrames::createSelectLevelMenu(QMenu *menu) {
1299   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1300   if (scene) {
1301     std::vector<TXshLevel *> levels;
1302     scene->getLevelSet()->listLevels(levels);
1303     std::vector<TXshLevel *>::iterator it;
1304     int i       = 0;
1305     bool active = false;
1306     QMenu *levelSelectMenu;
1307     for (it = levels.begin(); it != levels.end(); ++it) {
1308       TXshSimpleLevel *sl = (*it)->getSimpleLevel();
1309       if (sl) {
1310         // register only used level in xsheet
1311         if (!scene->getTopXsheet()->isLevelUsed(sl)) continue;
1312         QString levelName = QString::fromStdWString(sl->getName());
1313         if (i == 0) {
1314           levelSelectMenu = menu->addMenu(tr("Select Level"));
1315           active          = true;
1316         }
1317         if (active) {
1318           QAction *action = levelSelectMenu->addAction(levelName);
1319           connect(action, &QAction::triggered, [=] { levelSelected(i); });
1320         }
1321         i++;
1322       }
1323     }
1324   }
1325 }
1326 
1327 //-----------------------------------------------------------------------------
1328 
levelSelected(int index)1329 void FilmstripFrames::levelSelected(int index) {
1330   emit(levelSelectedSignal(index));
1331 }
1332 
1333 //-----------------------------------------------------------------------------
1334 
comboBoxToggled(bool ignore)1335 void FilmstripFrames::comboBoxToggled(bool ignore) {
1336   emit(comboBoxToggledSignal());
1337 }
1338 
1339 //-----------------------------------------------------------------------------
1340 
navigatorToggled(bool ignore)1341 void FilmstripFrames::navigatorToggled(bool ignore) {
1342   emit(navigatorToggledSignal());
1343 }
1344 
1345 //-----------------------------------------------------------------------------
1346 
orientationToggled(bool ignore)1347 void FilmstripFrames::orientationToggled(bool ignore) {
1348   m_isVertical = !m_isVertical;
1349   emit(orientationToggledSignal(m_isVertical));
1350 }
1351 
1352 //-----------------------------------------------------------------------------
1353 
setOrientation(bool isVertical)1354 void FilmstripFrames::setOrientation(bool isVertical) {
1355   m_isVertical = isVertical;
1356   if (m_isVertical) {
1357     setFixedWidth(m_iconSize.width() + fs_leftMargin + fs_rightMargin +
1358                   fs_iconMarginLR * 2);
1359     setFixedHeight(parentWidget()->height());
1360   } else {
1361     setFixedHeight(parentWidget()->height());
1362     setFixedWidth(parentWidget()->width());
1363   }
1364   if (m_isVertical)
1365     updateContentHeight();
1366   else
1367     updateContentWidth();
1368   TApp *app        = TApp::instance();
1369   TFrameHandle *fh = app->getCurrentFrame();
1370   TFrameId fid     = getCurrentFrameId();
1371 
1372   int index = fid2index(fid);
1373   if (index >= 0) {
1374     showFrame(index);
1375   }
1376   update();
1377 }
1378 
1379 //-----------------------------------------------------------------------------
1380 
setNavigator(bool showNavigator)1381 void FilmstripFrames::setNavigator(bool showNavigator) {
1382   m_showNavigator = showNavigator;
1383 }
1384 
1385 //-----------------------------------------------------------------------------
1386 
setComboBox(bool showComboBox)1387 void FilmstripFrames::setComboBox(bool showComboBox) {
1388   m_showComboBox = showComboBox;
1389 }
1390 
1391 //-----------------------------------------------------------------------------
1392 
onLevelChanged()1393 void FilmstripFrames::onLevelChanged() {
1394   if (m_isVertical)
1395     updateContentHeight();
1396   else
1397     updateContentWidth();
1398   update();
1399 }
1400 
1401 //-----------------------------------------------------------------------------
1402 
onLevelSwitched(TXshLevel *)1403 void FilmstripFrames::onLevelSwitched(TXshLevel *) {
1404   if (m_isVertical)
1405     updateContentHeight(0);
1406   else
1407     updateContentWidth(0);
1408   onFrameSwitched();  // deve visualizzare il frame corrente nella levelstrip
1409 }
1410 
1411 //-----------------------------------------------------------------------------
1412 
onFrameSwitched()1413 void FilmstripFrames::onFrameSwitched() {
1414   // no. interferische con lo shift-click per la selezione.
1415   // m_selection->selectNone();
1416   TApp *app        = TApp::instance();
1417   TFrameHandle *fh = app->getCurrentFrame();
1418   TFrameId fid     = getCurrentFrameId();
1419 
1420   int index = fid2index(fid);
1421   if (index >= 0) {
1422     showFrame(index);
1423 
1424     TFilmstripSelection *fsSelection =
1425         dynamic_cast<TFilmstripSelection *>(TSelection::getCurrent());
1426 
1427     // don't select if already selected - may be part of a group selection
1428     if (!m_selection->isSelected(index2fid(index)) && fsSelection) {
1429       select(index, ONLY_SELECT);
1430       m_justStartedSelection = true;
1431     }
1432   }
1433   update();
1434 }
1435 
1436 //-----------------------------------------------------------------------------
1437 
startDragDrop()1438 void FilmstripFrames::startDragDrop() {
1439   TRepetitionGuard guard;
1440   if (!guard.hasLock()) return;
1441 
1442   TXshSimpleLevel *sl = getLevel();
1443   if (!sl) return;
1444   const std::set<TFrameId> &fids = m_selection->getSelectedFids();
1445   if (fids.empty()) return;
1446   QByteArray byteArray;
1447 
1448   QMimeData *mimeData = new QMimeData;
1449   mimeData->setData("application/vnd.toonz.drawings", byteArray);
1450   QDrag *drag           = new QDrag(this);
1451   QPixmap dropThumbnail = IconGenerator::instance()->getIcon(sl, *fids.begin());
1452   if (!dropThumbnail.isNull()) drag->setPixmap(dropThumbnail);
1453   drag->setMimeData(mimeData);
1454   Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
1455 }
1456 
1457 //-----------------------------------------------------------------------------
1458 
inbetween()1459 void FilmstripFrames::inbetween() {
1460   TFilmstripSelection::InbetweenRange range = m_selection->getInbetweenRange();
1461   if (range.first >= range.second || !getLevel()) return;
1462 
1463   QSettings settings;
1464   QString keyName("InbetweenInterpolation");
1465   QString currentItem = settings.value(keyName, tr("Linear")).toString();
1466   int index;
1467 
1468   {
1469     if (!m_inbetweenDialog) m_inbetweenDialog = new InbetweenDialog(this);
1470 
1471     // Default -> l'ultimo valore usato
1472 
1473     m_inbetweenDialog->setValue(currentItem);
1474 
1475     int ret = m_inbetweenDialog->exec();
1476     if (!ret) return;
1477 
1478     currentItem = m_inbetweenDialog->getValue();
1479     if (currentItem.isEmpty()) return;
1480     index = m_inbetweenDialog->getIndex(currentItem);
1481     if (index < 0) return;
1482 
1483     // registro il nuovo valore
1484   }
1485   settings.setValue(keyName, currentItem);
1486 
1487   // lo converto nella notazione di FilmstripCmd
1488   const FilmstripCmd::InbetweenInterpolation codes[] = {
1489       FilmstripCmd::II_Linear, FilmstripCmd::II_EaseIn,
1490       FilmstripCmd::II_EaseOut, FilmstripCmd::II_EaseInOut};
1491 
1492   FilmstripCmd::InbetweenInterpolation interpolation = codes[index];
1493 
1494   // inbetween
1495   FilmstripCmd::inbetween(getLevel(), range.first, range.second, interpolation);
1496 }
1497 
1498 //-----------------------------------------------------------------------------
1499 
onViewerAboutToBeDestroyed()1500 void FilmstripFrames::onViewerAboutToBeDestroyed() {
1501   if (m_viewer) {
1502     disconnect(m_viewer, SIGNAL(onZoomChanged()), this, SLOT(update()));
1503     disconnect(m_viewer, SIGNAL(refreshNavi()), this, SLOT(update()));
1504     m_viewer = nullptr;
1505   }
1506 }
1507 
1508 //=============================================================================
1509 // Filmstrip
1510 //-----------------------------------------------------------------------------
1511 
1512 #if QT_VERSION >= 0x050500
Filmstrip(QWidget * parent,Qt::WindowFlags flags)1513 Filmstrip::Filmstrip(QWidget *parent, Qt::WindowFlags flags)
1514 #else
1515 Filmstrip::Filmstrip(QWidget *parent, Qt::WFlags flags)
1516 #endif
1517     : QWidget(parent) {
1518   setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
1519 
1520   m_frameArea        = new QScrollArea(this);
1521   m_chooseLevelCombo = new QComboBox(this);
1522   m_frames           = new FilmstripFrames(m_frameArea);
1523 
1524   //----
1525   m_frames->m_isVertical = m_isVertical;
1526   m_frameArea->setObjectName("filmScrollArea");
1527   m_frameArea->setFrameStyle(QFrame::StyledPanel);
1528   setOrientation(m_isVertical);
1529 
1530   m_frameArea->setWidget(m_frames);
1531 
1532   m_chooseLevelCombo->setMaxVisibleItems(50);
1533   m_chooseLevelCombo->setObjectName("filmLevelCombo");
1534 
1535   // layout
1536   QVBoxLayout *mainLayout = new QVBoxLayout();
1537   mainLayout->setMargin(0);
1538   mainLayout->setSpacing(0);
1539   {
1540     mainLayout->addWidget(m_chooseLevelCombo, 0);
1541     mainLayout->addWidget(m_frameArea, 1);
1542   }
1543   setLayout(mainLayout);
1544 
1545   setFocusProxy(m_frames);
1546 
1547   onLevelSwitched(0);
1548 
1549   // signal-slot connections
1550   // switch the current level when the current index of m_chooseLevelCombo is
1551   // changed
1552   connect(m_chooseLevelCombo, SIGNAL(activated(int)), this,
1553           SLOT(onChooseLevelComboChanged(int)));
1554   connect(m_frames, SIGNAL(orientationToggledSignal(bool)), this,
1555           SLOT(orientationToggled(bool)));
1556   connect(m_frames, SIGNAL(comboBoxToggledSignal()), this,
1557           SLOT(comboBoxToggled()));
1558   connect(m_frames, SIGNAL(navigatorToggledSignal()), this,
1559           SLOT(navigatorToggled()));
1560   connect(m_frames, SIGNAL(levelSelectedSignal(int)), this,
1561           SLOT(onChooseLevelComboChanged(int)));
1562 }
1563 
1564 //-----------------------------------------------------------------------------
1565 /*! switch the current level when the current index of m_chooseLevelCombo is
1566  * changed
1567  */
onChooseLevelComboChanged(int index)1568 void Filmstrip::onChooseLevelComboChanged(int index) {
1569   TApp *tapp = TApp::instance();
1570   // empty level
1571   if (index == m_chooseLevelCombo->findText(tr("- No Current Level -")))
1572     tapp->getCurrentLevel()->setLevel(0);
1573   else {
1574     std::vector<TFrameId> fids;
1575     m_levels[index]->getFids(fids);
1576     tapp->getCurrentFrame()->setFrameIds(fids);
1577 
1578     // retrieve to the current working frame of the level
1579     TFrameId WF;
1580     std::map<TXshSimpleLevel *, TFrameId>::iterator WFit;
1581     WFit = m_workingFrames.find(m_levels[index]);
1582     if (WFit != m_workingFrames.end())
1583       WF = WFit->second;
1584     else
1585       WF = fids[0];
1586 
1587     // this function emits xshLevelSwitched() signal and eventually calls
1588     // FlipConsole::UpdateRange
1589     // it may move the current frame so we need to keep the current frameId
1590     // before calling setLevel.
1591     tapp->getCurrentLevel()->setLevel(m_levels[index]);
1592 
1593     if (tapp->getCurrentSelection()->getSelection())
1594       tapp->getCurrentSelection()->getSelection()->selectNone();
1595 
1596     // move to the current working frame
1597     tapp->getCurrentFrame()->setFid(WF);
1598 
1599     QApplication::setOverrideCursor(Qt::WaitCursor);
1600 
1601     invalidateIcons(m_levels[index], fids);
1602 
1603     QApplication::restoreOverrideCursor();
1604   }
1605 }
1606 
1607 //-----------------------------------------------------------------------------
1608 /*! update combo items when the contents of scene cast are changed
1609  */
updateChooseLevelComboItems()1610 void Filmstrip::updateChooseLevelComboItems() {
1611   // clear items
1612   m_chooseLevelCombo->clear();
1613   for (auto oldLevel : m_levels) oldLevel->release();
1614   m_levels.clear();
1615 
1616   std::map<TXshSimpleLevel *, TFrameId> new_workingFrames;
1617 
1618   // correct and register items
1619   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1620   if (scene) {
1621     std::vector<TXshLevel *> levels;
1622     scene->getLevelSet()->listLevels(levels);
1623     std::vector<TXshLevel *>::iterator it;
1624 
1625     for (it = levels.begin(); it != levels.end(); ++it) {
1626       // register only TLV and PLI
1627       TXshSimpleLevel *sl = (*it)->getSimpleLevel();
1628       if (sl) {
1629         // register only used level in xsheet
1630         if (!scene->getTopXsheet()->isLevelUsed(sl)) continue;
1631 
1632         sl->addRef();
1633         m_levels.push_back(sl);
1634 
1635         // create new m_workingFrames map with the new levelset
1636         TFrameId fId;
1637         std::map<TXshSimpleLevel *, TFrameId>::iterator WFit =
1638             m_workingFrames.find(sl);
1639 
1640         if (WFit != m_workingFrames.end())
1641           fId = WFit->second;
1642         else
1643           fId = sl->getFirstFid();
1644 
1645         new_workingFrames.insert(std::make_pair(sl, fId));
1646 
1647         QString levelName = QString::fromStdWString(sl->getName());
1648         if (sl->getProperties()->getDirtyFlag()) levelName += " *";
1649 
1650         // append the current working frame number to the item name
1651         if (fId != sl->getFirstFid() && fId.getNumber() >= 0)
1652           levelName +=
1653               QString("  [#") + QString::number(fId.getNumber()) + QString("]");
1654 
1655         m_chooseLevelCombo->addItem(levelName);
1656       }
1657     }
1658   }
1659 
1660   if (m_chooseLevelCombo->count() == 0)
1661     m_chooseLevelCombo->addItem(tr("- No Current Level -"));
1662 
1663   // swap the list
1664   m_workingFrames.clear();
1665   m_workingFrames = new_workingFrames;
1666 
1667   // synchronize the current index of combo to the current level
1668   updateCurrentLevelComboItem();
1669 }
1670 
1671 //-----------------------------------------------------------------------------
1672 /*! synchronize the current index of combo to the current level
1673  */
updateCurrentLevelComboItem()1674 void Filmstrip::updateCurrentLevelComboItem() {
1675   if (m_chooseLevelCombo->count() == 1 &&
1676       m_chooseLevelCombo->itemText(0) == tr("- No Current Level -")) {
1677     m_chooseLevelCombo->setCurrentIndex(0);
1678     return;
1679   }
1680 
1681   TXshSimpleLevel *currentLevel =
1682       TApp::instance()->getCurrentLevel()->getSimpleLevel();
1683   if (!currentLevel) {
1684     int noLevelIndex = m_chooseLevelCombo->findText(tr("- No Current Level -"));
1685     if (noLevelIndex == -1) {
1686       noLevelIndex = m_chooseLevelCombo->count();
1687       m_chooseLevelCombo->addItem(tr("- No Current Level -"));
1688     }
1689     m_chooseLevelCombo->setCurrentIndex(noLevelIndex);
1690     return;
1691   }
1692 
1693   for (int i = 0; i < m_levels.size(); i++) {
1694     if (currentLevel->getName() == m_levels[i]->getName()) {
1695       m_chooseLevelCombo->setCurrentIndex(i);
1696       int noLevelIndex =
1697           m_chooseLevelCombo->findText(tr("- No Current Level -"));
1698       if (noLevelIndex != -1) m_chooseLevelCombo->removeItem(noLevelIndex);
1699       return;
1700     }
1701   }
1702 
1703   int noLevelIndex = m_chooseLevelCombo->findText(tr("- No Current Level -"));
1704   if (noLevelIndex == -1) {
1705     noLevelIndex = m_chooseLevelCombo->count();
1706     m_chooseLevelCombo->addItem(tr("- No Current Level -"));
1707   }
1708   m_chooseLevelCombo->setCurrentIndex(noLevelIndex);
1709 }
1710 
1711 //-----------------------------------------------------------------------------
1712 
~Filmstrip()1713 Filmstrip::~Filmstrip() {
1714   for (auto level : m_levels) level->release();
1715   m_levels.clear();
1716 }
1717 
1718 //-----------------------------------------------------------------------------
1719 
showEvent(QShowEvent *)1720 void Filmstrip::showEvent(QShowEvent *) {
1721   TApp *app                    = TApp::instance();
1722   TXshLevelHandle *levelHandle = app->getCurrentLevel();
1723   bool ret = connect(levelHandle, SIGNAL(xshLevelSwitched(TXshLevel *)),
1724                      SLOT(onLevelSwitched(TXshLevel *)));
1725   ret      = ret &&
1726         connect(levelHandle, SIGNAL(xshLevelChanged()), SLOT(onLevelChanged()));
1727 
1728   // updateWindowTitle is called in the onLevelChanged
1729   ret = ret && connect(app->getPaletteController()->getCurrentLevelPalette(),
1730                        SIGNAL(colorStyleChangedOnMouseRelease()),
1731                        SLOT(onLevelChanged()));
1732   ret = ret && connect(levelHandle, SIGNAL(xshLevelTitleChanged()),
1733                        SLOT(onLevelChanged()));
1734 
1735   ret =
1736       ret && connect(m_frameArea->verticalScrollBar(),
1737                      SIGNAL(valueChanged(int)), this, SLOT(onSliderMoved(int)));
1738 
1739   TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene();
1740   ret = ret && connect(sceneHandle, SIGNAL(sceneSwitched()), this,
1741                        SLOT(updateChooseLevelComboItems()));
1742   ret = ret && connect(sceneHandle, SIGNAL(castChanged()), this,
1743                        SLOT(updateChooseLevelComboItems()));
1744 
1745   ret = ret &&
1746         connect(TApp::instance()->getCurrentXsheet(), SIGNAL(xsheetChanged()),
1747                 this, SLOT(updateChooseLevelComboItems()));
1748 
1749   ret = ret && connect(app->getCurrentFrame(), SIGNAL(frameSwitched()), this,
1750                        SLOT(onFrameSwitched()));
1751 
1752   assert(ret);
1753 
1754   updateChooseLevelComboItems();
1755   onFrameSwitched();
1756   onLevelSwitched(0);
1757 }
1758 
1759 //-----------------------------------------------------------------------------
1760 
hideEvent(QHideEvent *)1761 void Filmstrip::hideEvent(QHideEvent *) {
1762   TApp *app                    = TApp::instance();
1763   TXshLevelHandle *levelHandle = app->getCurrentLevel();
1764   disconnect(levelHandle, SIGNAL(xshLevelSwitched(TXshLevel *)), this,
1765              SLOT(onLevelSwitched(TXshLevel *)));
1766   disconnect(levelHandle, SIGNAL(xshLevelChanged()), this,
1767              SLOT(onLevelChanged()));
1768   disconnect(TApp::instance()->getPaletteController()->getCurrentLevelPalette(),
1769              SIGNAL(colorStyleChangedOnMouseRelease()), this,
1770              SLOT(onLevelChanged()));
1771 
1772   disconnect(levelHandle, SIGNAL(xshLevelTitleChanged()), this,
1773              SLOT(onLevelChanged()));
1774 
1775   disconnect(m_frameArea->verticalScrollBar(), SIGNAL(valueChanged(int)), this,
1776              SLOT(onSliderMoved(int)));
1777 
1778   TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene();
1779   disconnect(sceneHandle, SIGNAL(sceneSwitched()), this,
1780              SLOT(updateChooseLevelComboItems()));
1781   disconnect(sceneHandle, SIGNAL(castChanged()), this,
1782              SLOT(updateChooseLevelComboItems()));
1783 
1784   disconnect(TApp::instance()->getCurrentXsheet(), SIGNAL(xsheetChanged()),
1785              this, SLOT(updateChooseLevelComboItems()));
1786 
1787   disconnect(TApp::instance()->getCurrentFrame(), SIGNAL(frameSwitched()), this,
1788              SLOT(onFrameSwitched()));
1789 }
1790 
1791 //-----------------------------------------------------------------------------
1792 
resizeEvent(QResizeEvent * e)1793 void Filmstrip::resizeEvent(QResizeEvent *e) {
1794   if (m_isVertical) {
1795     m_frames->updateContentHeight();
1796     m_frameArea->verticalScrollBar()->setSingleStep(
1797         m_frames->getOneFrameHeight());
1798   } else {
1799     m_frames->updateContentWidth();
1800     m_frameArea->horizontalScrollBar()->setSingleStep(
1801         m_frames->getOneFrameWidth());
1802   }
1803 }
1804 
1805 //-----------------------------------------------------------------------------
1806 
onLevelChanged()1807 void Filmstrip::onLevelChanged() { updateWindowTitle(); }
1808 
1809 //-----------------------------------------------------------------------------
1810 
updateWindowTitle()1811 void Filmstrip::updateWindowTitle() {
1812   updateCurrentLevelComboItem();
1813 
1814   TXshSimpleLevel *level = m_frames->getLevel();
1815 
1816   QString levelName;
1817 
1818   if (!level) {
1819     parentWidget()->setWindowTitle(tr("Level Strip"));
1820     return;
1821   } else {
1822     levelName = QString::fromStdWString(level->getName());
1823     if (level->getProperties()->getDirtyFlag()) levelName += " *";
1824   }
1825 
1826   // parentWidget() is TPanel
1827   parentWidget()->setWindowTitle(tr("Level:  ") + levelName);
1828 
1829   TFrameHandle *fh = TApp::instance()->getCurrentFrame();
1830   if (fh->isEditingLevel() && fh->getFid().getNumber() >= 0)
1831     levelName += QString("  [#") + QString::number(fh->getFid().getNumber()) +
1832                  QString("]");
1833 
1834   m_chooseLevelCombo->setItemText(m_chooseLevelCombo->currentIndex(),
1835                                   levelName);
1836 }
1837 
1838 //-----------------------------------------------------------------------------
1839 
onLevelSwitched(TXshLevel * oldLevel)1840 void Filmstrip::onLevelSwitched(TXshLevel *oldLevel) {
1841   updateWindowTitle();
1842 
1843   int tc = ToonzCheck::instance()->getChecks();
1844   if (tc & (ToonzCheck::eInk | ToonzCheck::ePaint)) {
1845     TXshLevel *sl = TApp::instance()->getCurrentLevel()->getLevel();
1846     if (!sl) return;
1847     std::vector<TFrameId> fids;
1848     sl->getFids(fids);
1849     removeIcons(sl, fids, true);
1850   }
1851   update();
1852 }
1853 
1854 //-----------------------------------------------------------------------------
1855 
onSliderMoved(int val)1856 void Filmstrip::onSliderMoved(int val) {
1857   int oneFrameHeight = m_frames->getIconSize().height() + fs_frameSpacing +
1858                        fs_iconMarginTop + fs_iconMarginBottom;
1859   int oneFrameWidth =
1860       m_frames->getIconSize().width() + fs_frameSpacing + fs_iconMarginLR;
1861   if (m_isVertical) {
1862     int tmpVal =
1863         (int)((float)val / (float)oneFrameHeight + 0.5f) * oneFrameHeight;
1864     m_frameArea->verticalScrollBar()->setValue(tmpVal);
1865   } else {
1866     int tmpVal =
1867         (int)((float)val / (float)oneFrameWidth + 0.5f) * oneFrameWidth;
1868     m_frameArea->horizontalScrollBar()->setValue(tmpVal);
1869   }
1870 }
1871 
1872 //-----------------------------------------------------------------------------
1873 
onFrameSwitched()1874 void Filmstrip::onFrameSwitched() {
1875   TFrameHandle *fh = TApp::instance()->getCurrentFrame();
1876   if (!fh->isEditingLevel()) return;
1877 
1878   TXshSimpleLevel *level = m_frames->getLevel();
1879 
1880   std::map<TXshSimpleLevel *, TFrameId>::iterator WFit;
1881   WFit = m_workingFrames.find(level);
1882   if (WFit == m_workingFrames.end()) return;
1883 
1884   WFit->second = fh->getFid();
1885 
1886   QString levelName = QString::fromStdWString(level->getName());
1887   if (level->getProperties()->getDirtyFlag()) levelName += " *";
1888   if (fh->getFid().getNumber() >= 0)
1889     levelName += QString("  [#") + QString::number(fh->getFid().getNumber()) +
1890                  QString("]");
1891 
1892   m_chooseLevelCombo->setItemText(m_chooseLevelCombo->currentIndex(),
1893                                   levelName);
1894 }
1895 
1896 //-----------------------------------------------------------------------------
1897 
orientationToggled(bool isVertical)1898 void Filmstrip::orientationToggled(bool isVertical) {
1899   setOrientation(isVertical);
1900 }
1901 
1902 //-----------------------------------------------------------------------------
1903 
comboBoxToggled()1904 void Filmstrip::comboBoxToggled() {
1905   if (m_chooseLevelCombo->isHidden())
1906     m_chooseLevelCombo->show();
1907   else
1908     m_chooseLevelCombo->hide();
1909   m_showComboBox = !m_chooseLevelCombo->isHidden();
1910   m_frames->setComboBox(m_showComboBox);
1911 }
1912 
1913 //-----------------------------------------------------------------------------
1914 
navigatorToggled()1915 void Filmstrip::navigatorToggled() {
1916   m_showNavigator = !m_showNavigator;
1917   m_frames->setNavigator(m_showNavigator);
1918 }
1919 
1920 //-----------------------------------------------------------------------------
1921 
setOrientation(bool isVertical)1922 void Filmstrip::setOrientation(bool isVertical) {
1923   m_isVertical = isVertical;
1924   if (isVertical) {
1925     m_frameArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1926     m_frameArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1927     m_frameArea->verticalScrollBar()->setObjectName("LevelStripScrollBar");
1928   } else {
1929     m_frameArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1930     m_frameArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1931     m_frameArea->horizontalScrollBar()->setObjectName("LevelStripScrollBar");
1932   }
1933   m_frames->setOrientation(m_isVertical);
1934   dynamic_cast<TPanel *>(parentWidget())->setCanFixWidth(m_isVertical);
1935 }
1936 
1937 // SaveLoadQSettings
save(QSettings & settings) const1938 void Filmstrip::save(QSettings &settings) const {
1939   UINT orientation = 0;
1940   orientation      = m_isVertical ? 1 : 0;
1941   settings.setValue("vertical", orientation);
1942 
1943   UINT showCombo = 0;
1944   showCombo      = m_chooseLevelCombo->isHidden() ? 0 : 1;
1945   settings.setValue("showCombo", showCombo);
1946 
1947   UINT navigator = 0;
1948   navigator      = m_showNavigator ? 1 : 0;
1949   settings.setValue("navigator", navigator);
1950 }
1951 
load(QSettings & settings)1952 void Filmstrip::load(QSettings &settings) {
1953   UINT orientation = settings.value("vertical", 1).toUInt();
1954   m_isVertical     = orientation == 1;
1955   setOrientation(m_isVertical);
1956 
1957   UINT navigator  = settings.value("navigator", 1).toUInt();
1958   m_showNavigator = navigator == 1;
1959   m_frames->setNavigator(m_showNavigator);
1960 
1961   UINT showCombo = settings.value("showCombo", 1).toUInt();
1962   m_showComboBox = showCombo == 1;
1963   if (showCombo == 1) {
1964     m_chooseLevelCombo->show();
1965   } else {
1966     m_chooseLevelCombo->hide();
1967   }
1968   m_frames->setComboBox(m_showComboBox);
1969 }
1970 
1971 //=============================================================================
1972 // inbetweenDialog
1973 //-----------------------------------------------------------------------------
1974 
InbetweenDialog(QWidget * parent)1975 InbetweenDialog::InbetweenDialog(QWidget *parent)
1976     : Dialog(TApp::instance()->getMainWindow(), true, "InBeetween") {
1977   setWindowTitle(tr("Inbetween"));
1978 
1979   QString linear(tr("Linear"));
1980   QString easeIn(tr("Ease In"));
1981   QString easeOut(tr("Ease Out"));
1982   QString easeInOut(tr("Ease In / Ease Out"));
1983   QStringList items;
1984   items << linear << easeIn << easeOut << easeInOut;
1985 
1986   beginHLayout();
1987   m_comboBox = new QComboBox(this);
1988   m_comboBox->addItems(items);
1989   addWidget(tr("Interpolation:"), m_comboBox);
1990   endHLayout();
1991 
1992   QPushButton *okBtn     = new QPushButton(tr("Inbetween"), this);
1993   QPushButton *cancelBtn = new QPushButton(tr("Cancel"), this);
1994   connect(okBtn, SIGNAL(clicked()), this, SLOT(accept()));
1995   connect(cancelBtn, SIGNAL(clicked()), this, SLOT(reject()));
1996 
1997   addButtonBarWidget(okBtn, cancelBtn);
1998 }
1999 
2000 //-----------------------------------------------------------------------------
2001 
getValue()2002 QString InbetweenDialog::getValue() { return m_comboBox->currentText(); }
2003 
2004 //-----------------------------------------------------------------------------
2005 
setValue(const QString & value)2006 void InbetweenDialog::setValue(const QString &value) {
2007   int currentIndex = m_comboBox->findText(value);
2008   if (currentIndex < 0) currentIndex = 0;
2009   m_comboBox->setCurrentIndex(currentIndex);
2010 }
2011 
2012 //-----------------------------------------------------------------------------
2013 
getIndex(const QString & text)2014 int InbetweenDialog::getIndex(const QString &text) {
2015   return m_comboBox->findText(text);
2016 }
2017 
2018 //=============================================================================
2019 
2020 OpenFloatingPanel openFilmstripCommand(MI_OpenFilmStrip, "FilmStrip",
2021                                        QObject::tr("Level: "));
2022