1 /*
2 
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2020 Matthew Chiawen Chang
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2 of the License.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 */
17 
18 #include "timelinecells.h"
19 
20 #include <QApplication>
21 #include <QResizeEvent>
22 #include <QInputDialog>
23 #include <QPainter>
24 #include <QSettings>
25 
26 #include "camerapropertiesdialog.h"
27 #include "editor.h"
28 #include "keyframe.h"
29 #include "layermanager.h"
30 #include "object.h"
31 #include "playbackmanager.h"
32 #include "preferencemanager.h"
33 #include "timeline.h"
34 #include "toolmanager.h"
35 
TimeLineCells(TimeLine * parent,Editor * editor,TIMELINE_CELL_TYPE type)36 TimeLineCells::TimeLineCells(TimeLine* parent, Editor* editor, TIMELINE_CELL_TYPE type) : QWidget(parent)
37 {
38     mTimeLine = parent;
39     mEditor = editor;
40     mPrefs = editor->preference();
41     mType = type;
42 
43     mFrameLength = mPrefs->getInt(SETTING::TIMELINE_SIZE);
44     mFontSize = mPrefs->getInt(SETTING::LABEL_FONT_SIZE);
45     mFrameSize = mPrefs->getInt(SETTING::FRAME_SIZE);
46     mbShortScrub = mPrefs->isOn(SETTING::SHORT_SCRUB);
47     mDrawFrameNumber = mPrefs->isOn(SETTING::DRAW_LABEL);
48 
49     setMinimumSize(500, 4 * mLayerHeight);
50     setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
51     setAttribute(Qt::WA_OpaquePaintEvent, false);
52 
53     connect(mPrefs, &PreferenceManager::optionChanged, this, &TimeLineCells::loadSetting);
54 }
55 
~TimeLineCells()56 TimeLineCells::~TimeLineCells()
57 {
58     delete mCache;
59 }
60 
loadSetting(SETTING setting)61 void TimeLineCells::loadSetting(SETTING setting)
62 {
63     switch (setting)
64     {
65     case SETTING::TIMELINE_SIZE:
66         mFrameLength = mPrefs->getInt(SETTING::TIMELINE_SIZE);
67         mTimeLine->updateLength();
68         break;
69     case SETTING::LABEL_FONT_SIZE:
70         mFontSize = mPrefs->getInt(SETTING::LABEL_FONT_SIZE);
71         break;
72     case SETTING::FRAME_SIZE:
73         mFrameSize = mPrefs->getInt(SETTING::FRAME_SIZE);
74         mTimeLine->updateLength();
75         break;
76     case SETTING::SHORT_SCRUB:
77         mbShortScrub = mPrefs->isOn(SETTING::SHORT_SCRUB);
78         break;
79     case SETTING::DRAW_LABEL:
80         mDrawFrameNumber = mPrefs->isOn(SETTING::DRAW_LABEL);
81         break;
82     default:
83         break;
84     }
85     updateContent();
86 }
87 
getFrameNumber(int x) const88 int TimeLineCells::getFrameNumber(int x) const
89 {
90     int frameNumber = mFrameOffset + 1 + (x - mOffsetX) / mFrameSize;
91     return frameNumber;
92 }
93 
getFrameX(int frameNumber) const94 int TimeLineCells::getFrameX(int frameNumber) const
95 {
96     int x = mOffsetX + (frameNumber - mFrameOffset) * mFrameSize;
97     return x;
98 }
99 
setFrameSize(int size)100 void TimeLineCells::setFrameSize(int size)
101 {
102     mFrameSize = size;
103     mPrefs->set(SETTING::FRAME_SIZE, mFrameSize);
104     updateContent();
105 }
106 
getLayerNumber(int y)107 int TimeLineCells::getLayerNumber(int y)
108 {
109     int layerNumber = mLayerOffset + (y - mOffsetY) / mLayerHeight;
110 
111     int totalLayerCount = mEditor->object()->getLayerCount();
112 
113     // Layers numbers are displayed in descending order
114     // The last row is layer 0
115     if (layerNumber <= totalLayerCount)
116         layerNumber = (totalLayerCount - 1) - layerNumber;
117     else
118         layerNumber = 0;
119 
120     if (y < mOffsetY)
121     {
122         layerNumber = -1;
123     }
124 
125     if (layerNumber >= totalLayerCount)
126     {
127         layerNumber = totalLayerCount;
128     }
129 
130     //If the mouse release event if fired with mouse off the frame of the application
131     // mEditor->object()->getLayerCount() doesn't return the correct value.
132     if (layerNumber < -1)
133     {
134         layerNumber = -1;
135     }
136     return layerNumber;
137 }
138 
getInbetweenLayerNumber(int y)139 int TimeLineCells::getInbetweenLayerNumber(int y) {
140     int layerNumber = getLayerNumber(y);
141     // Round the layer number towards the drag start
142     if(layerNumber != mFromLayer) {
143         if(getMouseMoveY() > 0 && y < getLayerY(layerNumber) + getLayerHeight() / 2) {
144             layerNumber++;
145         }
146         else if(getMouseMoveY() < 0 && y > getLayerY(layerNumber) + getLayerHeight() / 2) {
147             layerNumber--;
148         }
149     }
150     return layerNumber;
151 }
152 
getLayerY(int layerNumber)153 int TimeLineCells::getLayerY(int layerNumber)
154 {
155     return mOffsetY + (mEditor->object()->getLayerCount() - 1 - layerNumber - mLayerOffset)*mLayerHeight;
156 }
157 
updateFrame(int frameNumber)158 void TimeLineCells::updateFrame(int frameNumber)
159 {
160     int x = getFrameX(frameNumber);
161     update(x - mFrameSize, 0, mFrameSize + 1, height());
162 }
163 
updateContent()164 void TimeLineCells::updateContent()
165 {
166     drawContent();
167     update();
168 }
169 
didDetachLayer() const170 bool TimeLineCells::didDetachLayer() const {
171     return abs(getMouseMoveY()) > mLayerDetachThreshold;
172 }
173 
drawContent()174 void TimeLineCells::drawContent()
175 {
176     const QPalette palette = QApplication::palette();
177 
178     if (mCache == nullptr)
179     {
180         mCache = new QPixmap(size());
181         if (mCache->isNull())
182         {
183             // fail to create cache
184             return;
185         }
186     }
187 
188     mCurrentFrame = mEditor->currentFrame();
189 
190     QPainter painter(mCache);
191 
192     const Object* object = mEditor->object();
193 
194     Q_ASSERT(object != nullptr);
195 
196     const Layer* layer = mEditor->layers()->currentLayer();
197     if (layer == nullptr) return;
198 
199     // grey background of the view
200     painter.setPen(Qt::NoPen);
201     painter.setBrush(palette.color(QPalette::Base));
202     painter.drawRect(QRect(0, 0, width(), height()));
203 
204     // --- draw layers of the current object
205     for (int i = 0; i < object->getLayerCount(); i++)
206     {
207         if (i == mEditor->layers()->currentLayerIndex())
208         {
209             continue;
210         }
211         Layer* layeri = object->getLayer(i);
212 
213         if (layeri != nullptr)
214         {
215             switch (mType)
216             {
217             case TIMELINE_CELL_TYPE::Tracks:
218                 paintTrack(painter, layeri, mOffsetX,
219                            getLayerY(i), width() - mOffsetX,
220                            getLayerHeight(), false, mFrameSize);
221                 break;
222 
223             case TIMELINE_CELL_TYPE::Layers:
224                 paintLabel(painter, layeri, 0,
225                            getLayerY(i), width() - 1,
226                            getLayerHeight(), false, mEditor->layerVisibility());
227                 break;
228             }
229         }
230     }
231     if (didDetachLayer())
232     {
233         if (mType == TIMELINE_CELL_TYPE::Tracks)
234         {
235             paintTrack(painter, layer,
236                        mOffsetX, getLayerY(mEditor->layers()->currentLayerIndex()) + getMouseMoveY(),
237                        width() - mOffsetX, getLayerHeight(),
238                        true, mFrameSize);
239         }
240         else if (mType == TIMELINE_CELL_TYPE::Layers)
241         {
242             paintLabel(painter, layer,
243                        0, getLayerY(mEditor->layers()->currentLayerIndex()) + getMouseMoveY(),
244                        width() - 1, getLayerHeight(), true, mEditor->layerVisibility());
245 
246             paintLayerGutter(painter);
247         }
248     }
249     else
250     {
251         if (mType == TIMELINE_CELL_TYPE::Tracks)
252         {
253             paintTrack(painter,
254                        layer,
255                        mOffsetX,
256                        getLayerY(mEditor->layers()->currentLayerIndex()),
257                        width() - mOffsetX,
258                        getLayerHeight(),
259                        true,
260                        mFrameSize);
261         }
262         else if (mType == TIMELINE_CELL_TYPE::Layers)
263         {
264             paintLabel(painter,
265                        layer,
266                        0,
267                        getLayerY(mEditor->layers()->currentLayerIndex()),
268                        width() - 1,
269                        getLayerHeight(),
270                        true,
271                        mEditor->layerVisibility());
272         }
273     }
274 
275     // --- draw top
276     painter.setPen(Qt::NoPen);
277     painter.setBrush(palette.color(QPalette::Base));
278     painter.drawRect(QRect(0, 0, width() - 1, mOffsetY - 1));
279     painter.setPen(palette.color(QPalette::Mid));
280     painter.drawLine(0, mOffsetY - 2, width() - 1, mOffsetY - 2);
281 
282     if (mType == TIMELINE_CELL_TYPE::Layers)
283     {
284         // --- draw circle
285         painter.setPen(palette.color(QPalette::Text));
286         if (mEditor->layerVisibility() == LayerVisibility::CURRENTONLY)
287         {
288             painter.setBrush(palette.color(QPalette::Base));
289         }
290         else if (mEditor->layerVisibility() == LayerVisibility::RELATED)
291         {
292             QColor color = palette.color(QPalette::Text);
293             color.setAlpha(128);
294             painter.setBrush(color);
295         }
296         else if (mEditor->layerVisibility() == LayerVisibility::ALL)
297         {
298             painter.setBrush(palette.brush(QPalette::Text));
299         }
300         painter.setRenderHint(QPainter::Antialiasing, true);
301         painter.drawEllipse(6, 4, 9, 9);
302         painter.setRenderHint(QPainter::Antialiasing, false);
303     }
304     else if (mType == TIMELINE_CELL_TYPE::Tracks)
305     {
306         // --- draw ticks
307         painter.setPen(palette.color(QPalette::Text));
308         painter.setBrush(palette.brush(QPalette::Text));
309         int fps = mEditor->playback()->fps();
310         for (int i = mFrameOffset; i < mFrameOffset + (width() - mOffsetX) / mFrameSize; i++)
311         {
312             if (i + 1 >= mTimeLine->getRangeLower() && i < mTimeLine->getRangeUpper())
313             {
314                 painter.setPen(Qt::NoPen);
315                 painter.setBrush(palette.color(QPalette::Highlight));
316 
317                 painter.drawRect(getFrameX(i), 1, mFrameSize + 1, 2);
318 
319                 painter.setPen(palette.color(QPalette::Text));
320                 painter.setBrush(palette.brush(QPalette::Text));
321             }
322 
323             if (i%fps == 0 || i%fps == fps / 2)
324             {
325                 painter.drawLine(getFrameX(i), 1, getFrameX(i), 5);
326             }
327             else
328             {
329                 painter.drawLine(getFrameX(i), 1, getFrameX(i), 3);
330             }
331             if (i == 0 || i % fps == fps - 1)
332             {
333                 int incr = (i < 9) ? 4 : 0; // poor man’s text centering
334                 painter.drawText(QPoint(getFrameX(i) + incr, 15), QString::number(i + 1));
335             }
336         }
337     }
338 }
339 
paintTrack(QPainter & painter,const Layer * layer,int x,int y,int width,int height,bool selected,int frameSize) const340 void TimeLineCells::paintTrack(QPainter& painter, const Layer* layer,
341                        int x, int y, int width, int height,
342                        bool selected, int frameSize) const
343 {
344     const QPalette palette = QApplication::palette();
345     QColor col;
346     // Color each track according to the layer type
347     if (layer->type() == Layer::BITMAP) col = QColor(51, 155, 252);
348     if (layer->type() == Layer::VECTOR) col = QColor(70, 205, 123);
349     if (layer->type() == Layer::SOUND) col = QColor(255, 141, 112);
350     if (layer->type() == Layer::CAMERA) col = QColor(253, 202, 92);
351     // Dim invisible layers
352     if (!layer->visible()) col.setAlpha(64);
353 
354     painter.save();
355     painter.setBrush(col);
356     painter.setPen(QPen(QBrush(palette.color(QPalette::Mid)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
357     painter.drawRect(x, y - 1, width, height);
358 
359     if (!layer->visible())
360     {
361         painter.restore();
362         return;
363     }
364 
365     // Changes the appearance if selected
366     if (selected)
367     {
368         paintSelection(painter, x, y, width, height);
369     }
370     else
371     {
372         painter.save();
373         QLinearGradient linearGrad(QPointF(0, y), QPointF(0, y + height));
374         linearGrad.setColorAt(0, QColor(255,255,255,150));
375         linearGrad.setColorAt(1, QColor(0,0,0,0));
376         painter.setCompositionMode(QPainter::CompositionMode_Overlay);
377         painter.setBrush(linearGrad);
378         painter.drawRect(x, y - 1, width, height);
379         painter.restore();
380     }
381 
382     paintFrames(painter, layer, col, y, height, selected, frameSize);
383 
384     painter.restore();
385 }
386 
paintFrames(QPainter & painter,const Layer * layer,QColor trackCol,int y,int height,bool selected,int frameSize) const387 void TimeLineCells::paintFrames(QPainter& painter, const Layer* layer, QColor trackCol, int y, int height, bool selected, int frameSize) const
388 {
389     painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
390 
391     layer->foreachKeyFrame([&](KeyFrame* key)
392     {
393         int recLeft = getFrameX(key->pos()) - frameSize + 2;
394         int recTop = y + 1;
395         int recWidth = frameSize - 2;
396         int recHeight = height - 4;
397 
398         if (key->length() > 1)
399         {
400             // This is especially for sound clips.
401             // Sound clips are the only type of KeyFrame with variable frame length.
402             recWidth = frameSize * key->length() - 2;
403         }
404 
405         // Paint the frame border
406         if (selected && key->pos() == getCurrentFrame()) {
407             painter.setPen(Qt::white);
408         } else {
409             painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
410         }
411 
412         // Paint the frame contents
413         if (key->isSelected())
414         {
415             painter.setBrush(QColor(60, 60, 60));
416         }
417         else if (selected)
418         {
419             painter.setBrush(QColor(trackCol.red(), trackCol.green(), trackCol.blue(), 150));
420         }
421         painter.drawRect(recLeft, recTop, recWidth, recHeight);
422     });
423 }
424 
paintLabel(QPainter & painter,const Layer * layer,int x,int y,int width,int height,bool selected,LayerVisibility layerVisibility) const425 void TimeLineCells::paintLabel(QPainter& painter, const Layer* layer,
426                        int x, int y, int width, int height,
427                        bool selected, LayerVisibility layerVisibility) const
428 {
429     const QPalette palette = QApplication::palette();
430 
431     if (selected)
432     {
433         painter.setBrush(palette.color(QPalette::Highlight));
434     }
435     else
436     {
437         painter.setBrush(palette.color(QPalette::Base));
438     }
439     painter.setPen(Qt::NoPen);
440     painter.drawRect(x, y - 1, width, height); // empty rectangle by default
441 
442     if (!layer->visible())
443     {
444         painter.setBrush(palette.color(QPalette::Base));
445     }
446     else
447     {
448         if ((layerVisibility == LayerVisibility::ALL) || selected)
449         {
450             painter.setBrush(palette.color(QPalette::Text));
451         }
452         else if (layerVisibility == LayerVisibility::CURRENTONLY)
453         {
454             painter.setBrush(palette.color(QPalette::Base));
455         }
456         else if (layerVisibility == LayerVisibility::RELATED)
457         {
458             QColor color = palette.color(QPalette::Text);
459             color.setAlpha(128);
460             painter.setBrush(color);
461         }
462     }
463     if (selected)
464     {
465         painter.setPen(palette.color(QPalette::HighlightedText));
466     }
467     else
468     {
469         painter.setPen(palette.color(QPalette::Text));
470     }
471     painter.setRenderHint(QPainter::Antialiasing, true);
472     painter.drawEllipse(x + 6, y + 4, 9, 9);
473     painter.setRenderHint(QPainter::Antialiasing, false);
474 
475     if (layer->type() == Layer::BITMAP) painter.drawPixmap(QPoint(20, y + 2), QPixmap(":/icons/layer-bitmap.png"));
476     if (layer->type() == Layer::VECTOR) painter.drawPixmap(QPoint(20, y + 2), QPixmap(":/icons/layer-vector.png"));
477     if (layer->type() == Layer::SOUND) painter.drawPixmap(QPoint(21, y + 2), QPixmap(":/icons/layer-sound.png"));
478     if (layer->type() == Layer::CAMERA) painter.drawPixmap(QPoint(21, y + 2), QPixmap(":/icons/layer-camera.png"));
479 
480     if (selected)
481     {
482         painter.setPen(palette.color(QPalette::HighlightedText));
483     }
484     else
485     {
486         painter.setPen(palette.color(QPalette::Text));
487     }
488     painter.drawText(QPoint(45, y + (2 * height) / 3), layer->name());
489 }
490 
paintSelection(QPainter & painter,int x,int y,int width,int height) const491 void TimeLineCells::paintSelection(QPainter& painter, int x, int y, int width, int height) const
492 {
493     QLinearGradient linearGrad(QPointF(0, y), QPointF(0, y + height));
494     linearGrad.setColorAt(0, QColor(0, 0, 0, 255));
495     linearGrad.setColorAt(1, QColor(255, 255, 255, 0));
496     painter.save();
497     painter.setCompositionMode(QPainter::CompositionMode_Overlay);
498     painter.setBrush(linearGrad);
499     painter.setPen(Qt::NoPen);
500     painter.drawRect(x, y, width, height - 1);
501     painter.restore();
502 }
503 
paintLayerGutter(QPainter & painter)504 void TimeLineCells::paintLayerGutter(QPainter& painter)
505 {
506     painter.setPen(QApplication::palette().color(QPalette::Mid));
507     if (getMouseMoveY() > mLayerDetachThreshold)
508     {
509         painter.drawRect(0, getLayerY(getInbetweenLayerNumber(mEndY))+mLayerHeight, width(), 2);
510     }
511     else
512     {
513         painter.drawRect(0, getLayerY(getInbetweenLayerNumber(mEndY)), width(), 2);
514     }
515 }
516 
paintOnionSkin(QPainter & painter)517 void TimeLineCells::paintOnionSkin(QPainter& painter)
518 {
519     Layer* layer = mEditor->layers()->currentLayer();
520     if (layer == nullptr) { return; }
521 
522     int frameNumber = mEditor->currentFrame();
523 
524     int prevOnionSkinCount = mEditor->preference()->getInt(SETTING::ONION_PREV_FRAMES_NUM);
525     int nextOnionSkinCount = mEditor->preference()->getInt(SETTING::ONION_NEXT_FRAMES_NUM);
526 
527     bool isAbsolute = (mEditor->preference()->getString(SETTING::ONION_TYPE) == "absolute");
528 
529     if (mEditor->preference()->isOn(SETTING::PREV_ONION) && prevOnionSkinCount > 0)
530     {
531         int onionFrameNumber = frameNumber;
532         if (isAbsolute)
533         {
534             onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber+1, true);
535         }
536         onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber, isAbsolute);
537         int onionPosition = 0;
538 
539         while (onionPosition < prevOnionSkinCount && onionFrameNumber > 0)
540         {
541             painter.setBrush(QColor(128, 128, 128, 128));
542             painter.setPen(Qt::NoPen);
543             QRect onionRect;
544             onionRect.setTopLeft(QPoint(getFrameX(onionFrameNumber - 1), 0));
545             onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), height()));
546             onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), 19));
547             painter.drawRect(onionRect);
548 
549             onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber, isAbsolute);
550             onionPosition++;
551         }
552     }
553 
554     if (mEditor->preference()->isOn(SETTING::NEXT_ONION) && nextOnionSkinCount > 0) {
555 
556         int onionFrameNumber = layer->getNextFrameNumber(frameNumber, isAbsolute);
557         int onionPosition = 0;
558 
559         while (onionPosition < nextOnionSkinCount && onionFrameNumber > 0)
560         {
561             painter.setBrush(QColor(128, 128, 128, 128));
562             painter.setPen(Qt::NoPen);
563             QRect onionRect;
564             onionRect.setTopLeft(QPoint(getFrameX(onionFrameNumber - 1), 0));
565             onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), height()));
566             onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), 19));
567             painter.drawRect(onionRect);
568 
569             onionFrameNumber = layer->getNextFrameNumber(onionFrameNumber, isAbsolute);
570             onionPosition++;
571         }
572     }
573 }
574 
paintEvent(QPaintEvent *)575 void TimeLineCells::paintEvent(QPaintEvent*)
576 {
577     Object* object = mEditor->object();
578     Layer* layer = mEditor->layers()->currentLayer();
579 
580     Q_ASSUME(object != nullptr && layer != nullptr);
581 
582     const QPalette palette = QApplication::palette();
583     QPainter painter(this);
584 
585     bool isPlaying = mEditor->playback()->isPlaying();
586     if ((!isPlaying && !mTimeLine->scrubbing) || mCache == nullptr)
587     {
588         drawContent();
589     }
590     if (mCache)
591     {
592         painter.drawPixmap(QPoint(0, 0), *mCache);
593     }
594 
595     if (mType == TIMELINE_CELL_TYPE::Tracks)
596     {
597         if (!isPlaying)
598         {
599             paintOnionSkin(painter);
600         }
601 
602         if (mPrevFrame != mEditor->currentFrame()  || mEditor->playback()->isPlaying())
603         {
604             mPrevFrame = mEditor->currentFrame();
605             trackScrubber();
606         }
607 
608         // --- draw the position of the current frame
609         if (mEditor->currentFrame() > mFrameOffset)
610         {
611             QColor scrubColor = palette.color(QPalette::Highlight);
612             scrubColor.setAlpha(160);
613             painter.setBrush(scrubColor);
614             painter.setPen(Qt::NoPen);
615             //painter.setCompositionMode(QPainter::CompositionMode_Source); // this causes the message: QPainter::setCompositionMode: PorterDuff modes not supported on device
616             QRect scrubRect;
617             scrubRect.setTopLeft(QPoint(getFrameX(mEditor->currentFrame() - 1), 0));
618             scrubRect.setBottomRight(QPoint(getFrameX(mEditor->currentFrame()), height()));
619             if (mbShortScrub)
620             {
621                 scrubRect.setBottomRight(QPoint(getFrameX(mEditor->currentFrame()), 19));
622             }
623             painter.drawRect(scrubRect);
624             painter.setPen(palette.color(QPalette::HighlightedText));
625             int incr = (mEditor->currentFrame() < 10) ? 4 : 0;
626             painter.drawText(QPoint(getFrameX(mEditor->currentFrame() - 1) + incr, 15),
627                              QString::number(mEditor->currentFrame()));
628         }
629     }
630 }
631 
resizeEvent(QResizeEvent * event)632 void TimeLineCells::resizeEvent(QResizeEvent* event)
633 {
634     clearCache();
635     updateContent();
636     event->accept();
637     emit lengthChanged(getFrameLength());
638 }
639 
mousePressEvent(QMouseEvent * event)640 void TimeLineCells::mousePressEvent(QMouseEvent* event)
641 {
642     int frameNumber = getFrameNumber(event->pos().x());
643     int layerNumber = getLayerNumber(event->pos().y());
644     mFromLayer = mToLayer = layerNumber;
645 
646     mStartY = event->pos().y();
647     mStartLayerNumber = layerNumber;
648     mEndY = event->pos().y();
649 
650     mStartFrameNumber = frameNumber;
651     mLastFrameNumber = mStartFrameNumber;
652 
653     mCanMoveFrame = false;
654     mMovingFrames = false;
655 
656     mCanBoxSelect = false;
657     mBoxSelecting = false;
658 
659     mClickSelecting = false;
660 
661     primaryButton = event->button();
662 
663     bool switchLayer = mEditor->tools()->currentTool()->switchingLayer();
664     if (!switchLayer) { return; }
665 
666     switch (mType)
667     {
668     case TIMELINE_CELL_TYPE::Layers:
669         if (layerNumber != -1 && layerNumber < mEditor->object()->getLayerCount())
670         {
671             if (event->pos().x() < 15)
672             {
673                 mEditor->switchVisibilityOfLayer(layerNumber);
674             }
675             else
676             {
677                 mEditor->layers()->setCurrentLayer(layerNumber);
678             }
679         }
680         if (layerNumber == -1)
681         {
682             if (event->pos().x() < 15)
683             {
684                 if (event->button() == Qt::LeftButton) {
685                     mEditor->increaseLayerVisibilityIndex();
686                 } else if (event->button() == Qt::RightButton) {
687                     mEditor->decreaseLayerVisibilityIndex();
688                 }
689             }
690         }
691         break;
692     case TIMELINE_CELL_TYPE::Tracks:
693         if (event->button() == Qt::MidButton)
694         {
695             mLastFrameNumber = getFrameNumber(event->pos().x());
696         }
697         else
698         {
699             if (frameNumber == mEditor->currentFrame() && (!mbShortScrub || (mbShortScrub && mStartY < 20)))
700             {
701                 if (mEditor->playback()->isPlaying())
702                 {
703                     mEditor->playback()->stop();
704                 }
705                 mTimeLine->scrubbing = true;
706             }
707             else
708             {
709                 if ((layerNumber != -1) && layerNumber < mEditor->object()->getLayerCount())
710                 {
711                     int previousLayerNumber = mEditor->layers()->currentLayerIndex();
712 
713                     if (previousLayerNumber != layerNumber) {
714                         Layer *previousLayer = mEditor->object()->getLayer(previousLayerNumber);
715                         previousLayer->deselectAll();
716 
717                         mEditor->layers()->setCurrentLayer(layerNumber);
718                     }
719 
720                     Layer *currentLayer = mEditor->object()->getLayer(layerNumber);
721 
722                     // Check if we are using the alt key
723                     if (event->modifiers() == Qt::AltModifier)
724                     {
725                         // If it is the case, we select everything that is after the selected frame
726                         mClickSelecting = true;
727                         mCanMoveFrame = true;
728 
729                         currentLayer->selectAllFramesAfter(frameNumber);
730                     }
731                     // Check if we are clicking on a non selected frame
732                     else if (!currentLayer->isFrameSelected(frameNumber))
733                     {
734                         // If it is the case, we select it
735                         mCanBoxSelect = true;
736                         mClickSelecting = true;
737 
738                         if (event->modifiers() == Qt::ControlModifier)
739                         {
740                             // Add/remove from already selected
741                             currentLayer->toggleFrameSelected(frameNumber, true);
742                         }
743                         else if (event->modifiers() == Qt::ShiftModifier)
744                         {
745                             // Select a range from the last selected
746                             currentLayer->extendSelectionTo(frameNumber);
747                         }
748                         else
749                         {
750                             currentLayer->toggleFrameSelected(frameNumber, false);
751                         }
752                     }
753                     else
754                     {
755                         // We clicked on a selected frame, we can move it
756                         mCanMoveFrame = true;
757                     }
758 
759                     mTimeLine->updateContent();
760                 }
761                 else
762                 {
763                     if (frameNumber > 0)
764                     {
765                         if (mEditor->playback()->isPlaying())
766                         {
767                             mEditor->playback()->stop();
768                         }
769                         if (mEditor->playback()->getSoundScrubActive() && mLastScrubFrame != frameNumber)
770                         {
771                             mEditor->playback()->playScrub(frameNumber);
772                             mLastScrubFrame = frameNumber;
773                         }
774 
775                         mEditor->scrubTo(frameNumber);
776 
777                         mTimeLine->scrubbing = true;
778                         qDebug("Scrub to %d frame", frameNumber);
779                     }
780                 }
781             }
782         }
783         break;
784     }
785 }
786 
mouseMoveEvent(QMouseEvent * event)787 void TimeLineCells::mouseMoveEvent(QMouseEvent* event)
788 {
789     if (mType == TIMELINE_CELL_TYPE::Layers)
790     {
791         mEndY = event->pos().y();
792         emit mouseMovedY(mEndY - mStartY);
793     }
794     else if (mType == TIMELINE_CELL_TYPE::Tracks)
795     {
796         int frameNumber = getFrameNumber(event->pos().x());
797         if (primaryButton == Qt::MidButton)
798         {
799             // qMin( max_frame_offset, qMax ( min_frame_offset, draw_frame_offset ) )
800             mFrameOffset = qMin(qMax(0, mFrameLength - width() / getFrameSize()), qMax(0, mFrameOffset + mLastFrameNumber - frameNumber));
801             update();
802             emit offsetChanged(mFrameOffset);
803         }
804         else
805         {
806             if (mTimeLine->scrubbing)
807             {
808                 if (mEditor->playback()->getSoundScrubActive() && mLastScrubFrame != frameNumber)
809                 {
810                     mEditor->playback()->playScrub(frameNumber);
811                     mLastScrubFrame = frameNumber;
812                 }
813                 mEditor->scrubTo(frameNumber);
814             }
815             else
816             {
817                 if (mStartLayerNumber != -1 && mStartLayerNumber < mEditor->object()->getLayerCount())
818                 {
819                     Layer *currentLayer = mEditor->object()->getLayer(mStartLayerNumber);
820 
821                     // Did we move to another frame ?
822                     if (frameNumber != mLastFrameNumber)
823                     {
824                         // Check if the frame we clicked was selected
825                         if (mCanMoveFrame) {
826 
827                             // If it is the case, we move the selected frames in the layer
828                             mMovingFrames = true;
829 
830                             int offset = frameNumber - mLastFrameNumber;
831                             currentLayer->moveSelectedFrames(offset);
832                             mEditor->layers()->notifyAnimationLengthChanged();
833                             mEditor->framesModified();
834                         }
835                         else if (mCanBoxSelect)
836                         {
837                             // Otherwise, we do a box select
838                             mBoxSelecting = true;
839 
840                             currentLayer->deselectAll();
841                             currentLayer->setFrameSelected(mStartFrameNumber, true);
842                             currentLayer->extendSelectionTo(frameNumber);
843                         }
844                         mLastFrameNumber = frameNumber;
845                     }
846                 }
847             }
848         }
849     }
850     mTimeLine->update();
851 }
852 
mouseReleaseEvent(QMouseEvent * event)853 void TimeLineCells::mouseReleaseEvent(QMouseEvent* event)
854 {
855     if (event->button() != primaryButton) return;
856 
857     primaryButton = Qt::NoButton;
858     mEndY = mStartY;
859     mTimeLine->scrubbing = false;
860     int frameNumber = getFrameNumber(event->pos().x());
861     if (frameNumber < 1) frameNumber = -1;
862     int layerNumber = getLayerNumber(event->pos().y());
863     if (mType == TIMELINE_CELL_TYPE::Tracks && primaryButton != Qt::MidButton && layerNumber != -1 && layerNumber < mEditor->object()->getLayerCount())
864     {
865         Layer *currentLayer = mEditor->object()->getLayer(layerNumber);
866 
867         if (!mTimeLine->scrubbing && !mMovingFrames && !mClickSelecting && !mBoxSelecting)
868         {
869             // De-selecting if we didn't move, scrub nor select anything
870             bool multipleSelection = (event->modifiers() == Qt::ControlModifier);
871 
872             // Add/remove from already selected
873             currentLayer->toggleFrameSelected(frameNumber, multipleSelection);
874         }
875     }
876     if (mType == TIMELINE_CELL_TYPE::Layers && layerNumber != mStartLayerNumber && mStartLayerNumber != -1 && layerNumber != -1)
877     {
878         mToLayer = getInbetweenLayerNumber(event->pos().y());
879         if (mToLayer != mFromLayer && mToLayer > -1 && mToLayer < mEditor->layers()->count())
880         {
881             // Bubble the from layer up or down to the to layer
882             if (mToLayer < mFromLayer) // bubble up
883             {
884                 for (int i = mFromLayer - 1; i >= mToLayer; i--)
885                     mEditor->swapLayers(i, i + 1);
886             }
887             else // bubble down
888             {
889                 for (int i = mFromLayer + 1; i <= mToLayer; i++)
890                     mEditor->swapLayers(i, i - 1);
891             }
892         }
893     }
894 
895     emit mouseMovedY(0);
896     mTimeLine->updateContent();
897 }
898 
mouseDoubleClickEvent(QMouseEvent * event)899 void TimeLineCells::mouseDoubleClickEvent(QMouseEvent* event)
900 {
901     int layerNumber = getLayerNumber(event->pos().y());
902 
903     // -- short scrub --
904     if (event->pos().y() < 20 && (mType != TIMELINE_CELL_TYPE::Layers || event->pos().x() >= 15))
905     {
906         mPrefs->set(SETTING::SHORT_SCRUB, !mbShortScrub);
907     }
908 
909     // -- layer --
910     Layer* layer = mEditor->object()->getLayer(layerNumber);
911     if (layer && mType == TIMELINE_CELL_TYPE::Layers && event->pos().x() >= 15)
912     {
913         editLayerProperties(layer);
914     }
915     QWidget::mouseDoubleClickEvent(event);
916 }
917 
editLayerProperties(Layer * layer) const918 void TimeLineCells::editLayerProperties(Layer *layer) const
919 {
920     if (layer->type() != Layer::CAMERA)
921     {
922         editLayerName(layer);
923         return;
924     }
925 
926     auto cameraLayer = qobject_cast<LayerCamera*>(layer);
927     Q_ASSERT(cameraLayer);
928     editLayerProperties(cameraLayer);
929 }
930 
editLayerProperties(LayerCamera * layer) const931 void TimeLineCells::editLayerProperties(LayerCamera *layer) const
932 {
933     QRegExp regex("([\\xFFEF-\\xFFFF])+");
934 
935     CameraPropertiesDialog dialog(layer->name(), layer->getViewRect().width(), layer->getViewRect().height());
936     if (dialog.exec() != QDialog::Accepted)
937     {
938         return;
939     }
940     QString name = dialog.getName().replace(regex, "");
941 
942     if (!name.isEmpty())
943     {
944         mEditor->layers()->renameLayer(layer, name);
945     }
946     QSettings settings(PENCIL2D, PENCIL2D);
947     settings.setValue(SETTING_FIELD_W, dialog.getWidth());
948     settings.setValue(SETTING_FIELD_H, dialog.getHeight());
949     layer->setViewRect(QRect(-dialog.getWidth() / 2, -dialog.getHeight() / 2, dialog.getWidth(), dialog.getHeight()));
950 }
951 
editLayerName(Layer * layer) const952 void TimeLineCells::editLayerName(Layer* layer) const
953 {
954     QRegExp regex("([\\xFFEF-\\xFFFF])+");
955 
956     bool ok;
957     QString name = QInputDialog::getText(nullptr, tr("Layer Properties"),
958                                          tr("Layer name:"), QLineEdit::Normal,
959                                          layer->name(), &ok);
960     name.replace(regex, "");
961     if (!ok || name.isEmpty())
962     {
963         return;
964     }
965 
966     mEditor->layers()->renameLayer(layer, name);
967 }
968 
hScrollChange(int x)969 void TimeLineCells::hScrollChange(int x)
970 {
971     mFrameOffset = x;
972     update();
973 }
974 
vScrollChange(int x)975 void TimeLineCells::vScrollChange(int x)
976 {
977     mLayerOffset = x;
978     update();
979 }
980 
setMouseMoveY(int x)981 void TimeLineCells::setMouseMoveY(int x)
982 {
983     mMouseMoveY = x;
984     if (x == 0)
985     {
986         update();
987     }
988 }
989 
trackScrubber()990 void TimeLineCells::trackScrubber()
991 {
992     if (mEditor->currentFrame() <= mFrameOffset)
993     {
994         // Move the timeline back if the scrubber is offscreen to the left
995         mFrameOffset = mEditor->currentFrame() - 1;
996         emit offsetChanged(mFrameOffset);
997         mTimeLine->updateContent();
998     }
999     else if (width() < (mEditor->currentFrame() - mFrameOffset + 1) * mFrameSize)
1000     {
1001         // Move timeline forward if the scrubber is offscreen to the right
1002         if (mEditor->playback()->isPlaying())
1003             mFrameOffset = mFrameOffset + ((mEditor->currentFrame() - mFrameOffset) / 2);
1004         else
1005             mFrameOffset = mEditor->currentFrame() - width() / mFrameSize;
1006         emit offsetChanged(mFrameOffset);
1007         mTimeLine->updateContent();
1008     }
1009 }
1010