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