All Classes Namespaces Functions Variables Enumerations Properties Pages
timelinecells.cpp
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 
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);
52 
53  connect(mPrefs, &PreferenceManager::optionChanged, this, &TimeLineCells::loadSetting);
54 }
55 
56 TimeLineCells::~TimeLineCells()
57 {
58  delete mCache;
59 }
60 
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 
88 int TimeLineCells::getFrameNumber(int x) const
89 {
90  int frameNumber = mFrameOffset + 1 + (x - mOffsetX) / mFrameSize;
91  return frameNumber;
92 }
93 
94 int TimeLineCells::getFrameX(int frameNumber) const
95 {
96  int x = mOffsetX + (frameNumber - mFrameOffset) * mFrameSize;
97  return x;
98 }
99 
100 void TimeLineCells::setFrameSize(int size)
101 {
102  mFrameSize = size;
103  mPrefs->set(SETTING::FRAME_SIZE, mFrameSize);
104  updateContent();
105 }
106 
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 
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 
153 int TimeLineCells::getLayerY(int layerNumber)
154 {
155  return mOffsetY + (mEditor->object()->getLayerCount() - 1 - layerNumber - mLayerOffset)*mLayerHeight;
156 }
157 
158 void TimeLineCells::updateFrame(int frameNumber)
159 {
160  int x = getFrameX(frameNumber);
161  update(x - mFrameSize, 0, mFrameSize + 1, height());
162 }
163 
164 void TimeLineCells::updateContent()
165 {
166  drawContent();
167  update();
168 }
169 
170 bool TimeLineCells::didDetachLayer() const {
171  return abs(getMouseMoveY()) > mLayerDetachThreshold;
172 }
173 
174 void TimeLineCells::drawContent()
175 {
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 
340 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);
357  painter.drawRect(x, y - 1, width, height);
358 
359  if (!layer->visible()) return;
360 
361  // Changes the appearance if selected
362  if (selected)
363  {
364  paintSelection(painter, x, y, width, height);
365  }
366  else
367  {
368  painter.save();
369  QLinearGradient linearGrad(QPointF(0, y), QPointF(0, y + height));
370  linearGrad.setColorAt(0, QColor(255,255,255,150));
371  linearGrad.setColorAt(1, QColor(0,0,0,0));
373  painter.setBrush(linearGrad);
374  painter.drawRect(x, y - 1, width, height);
375  painter.restore();
376  }
377 
378  paintFrames(painter, layer, col, y, height, selected, frameSize);
379 
380  painter.restore();
381 }
382 
383 void TimeLineCells::paintFrames(QPainter& painter, const Layer* layer, QColor trackCol, int y, int height, bool selected, int frameSize) const
384 {
385  painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
386 
387  layer->foreachKeyFrame([&](KeyFrame* key)
388  {
389  int recLeft = getFrameX(key->pos()) - frameSize + 2;
390  int recTop = y + 1;
391  int recWidth = frameSize - 2;
392  int recHeight = height - 4;
393 
394  if (key->length() > 1)
395  {
396  // This is especially for sound clips.
397  // Sound clips are the only type of KeyFrame with variable frame length.
398  recWidth = frameSize * key->length() - 2;
399  }
400 
401  // Paint the frame border
402  if (selected && key->pos() == getCurrentFrame()) {
403  painter.setPen(Qt::white);
404  } else {
405  painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
406  }
407 
408  // Paint the frame contents
409  if (key->isSelected())
410  {
411  painter.setBrush(QColor(60, 60, 60));
412  }
413  else if (selected)
414  {
415  painter.setBrush(QColor(trackCol.red(), trackCol.green(), trackCol.blue(), 150));
416  }
417  painter.drawRect(recLeft, recTop, recWidth, recHeight);
418  });
419 }
420 
421 void TimeLineCells::paintLabel(QPainter& painter, const Layer* layer,
422  int x, int y, int width, int height,
423  bool selected, LayerVisibility layerVisibility) const
424 {
425  const QPalette palette = QApplication::palette();
426 
427  if (selected)
428  {
429  painter.setBrush(palette.color(QPalette::Highlight));
430  }
431  else
432  {
433  painter.setBrush(palette.color(QPalette::Base));
434  }
435  painter.setPen(Qt::NoPen);
436  painter.drawRect(x, y - 1, width, height); // empty rectangle by default
437 
438  if (!layer->visible())
439  {
440  painter.setBrush(palette.color(QPalette::Base));
441  }
442  else
443  {
444  if ((layerVisibility == LayerVisibility::ALL) || selected)
445  {
446  painter.setBrush(palette.color(QPalette::Text));
447  }
448  else if (layerVisibility == LayerVisibility::CURRENTONLY)
449  {
450  painter.setBrush(palette.color(QPalette::Base));
451  }
452  else if (layerVisibility == LayerVisibility::RELATED)
453  {
454  QColor color = palette.color(QPalette::Text);
455  color.setAlpha(128);
456  painter.setBrush(color);
457  }
458  }
459  if (selected)
460  {
461  painter.setPen(palette.color(QPalette::HighlightedText));
462  }
463  else
464  {
465  painter.setPen(palette.color(QPalette::Text));
466  }
467  painter.setRenderHint(QPainter::Antialiasing, true);
468  painter.drawEllipse(x + 6, y + 4, 9, 9);
469  painter.setRenderHint(QPainter::Antialiasing, false);
470 
471  if (layer->type() == Layer::BITMAP) painter.drawPixmap(QPoint(20, y + 2), QPixmap(":/icons/layer-bitmap.png"));
472  if (layer->type() == Layer::VECTOR) painter.drawPixmap(QPoint(20, y + 2), QPixmap(":/icons/layer-vector.png"));
473  if (layer->type() == Layer::SOUND) painter.drawPixmap(QPoint(21, y + 2), QPixmap(":/icons/layer-sound.png"));
474  if (layer->type() == Layer::CAMERA) painter.drawPixmap(QPoint(21, y + 2), QPixmap(":/icons/layer-camera.png"));
475 
476  if (selected)
477  {
478  painter.setPen(palette.color(QPalette::HighlightedText));
479  }
480  else
481  {
482  painter.setPen(palette.color(QPalette::Text));
483  }
484  painter.drawText(QPoint(45, y + (2 * height) / 3), layer->name());
485 }
486 
487 void TimeLineCells::paintSelection(QPainter& painter, int x, int y, int width, int height) const
488 {
489  QLinearGradient linearGrad(QPointF(0, y), QPointF(0, y + height));
490  linearGrad.setColorAt(0, QColor(0, 0, 0, 255));
491  linearGrad.setColorAt(1, QColor(255, 255, 255, 0));
492  painter.save();
494  painter.setBrush(linearGrad);
495  painter.setPen(Qt::NoPen);
496  painter.drawRect(x, y, width, height - 1);
497  painter.restore();
498 }
499 
500 void TimeLineCells::paintLayerGutter(QPainter& painter)
501 {
502  painter.setPen(QApplication::palette().color(QPalette::Mid));
503  if (getMouseMoveY() > mLayerDetachThreshold)
504  {
505  painter.drawRect(0, getLayerY(getInbetweenLayerNumber(mEndY))+mLayerHeight, width(), 2);
506  }
507  else
508  {
509  painter.drawRect(0, getLayerY(getInbetweenLayerNumber(mEndY)), width(), 2);
510  }
511 }
512 
513 void TimeLineCells::paintOnionSkin(QPainter& painter)
514 {
515  Layer* layer = mEditor->layers()->currentLayer();
516  if (layer == nullptr) { return; }
517 
518  int frameNumber = mEditor->currentFrame();
519 
520  int prevOnionSkinCount = mEditor->preference()->getInt(SETTING::ONION_PREV_FRAMES_NUM);
521  int nextOnionSkinCount = mEditor->preference()->getInt(SETTING::ONION_NEXT_FRAMES_NUM);
522 
523  bool isAbsolute = (mEditor->preference()->getString(SETTING::ONION_TYPE) == "absolute");
524 
525  if (mEditor->preference()->isOn(SETTING::PREV_ONION) && prevOnionSkinCount > 0)
526  {
527  int onionFrameNumber = frameNumber;
528  if (isAbsolute)
529  {
530  onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber+1, true);
531  }
532  onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber, isAbsolute);
533  int onionPosition = 0;
534 
535  while (onionPosition < prevOnionSkinCount && onionFrameNumber > 0)
536  {
537  painter.setBrush(QColor(128, 128, 128, 128));
538  painter.setPen(Qt::NoPen);
539  QRect onionRect;
540  onionRect.setTopLeft(QPoint(getFrameX(onionFrameNumber - 1), 0));
541  onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), height()));
542  onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), 19));
543  painter.drawRect(onionRect);
544 
545  onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber, isAbsolute);
546  onionPosition++;
547  }
548  }
549 
550  if (mEditor->preference()->isOn(SETTING::NEXT_ONION) && nextOnionSkinCount > 0) {
551 
552  int onionFrameNumber = layer->getNextFrameNumber(frameNumber, isAbsolute);
553  int onionPosition = 0;
554 
555  while (onionPosition < nextOnionSkinCount && onionFrameNumber > 0)
556  {
557  painter.setBrush(QColor(128, 128, 128, 128));
558  painter.setPen(Qt::NoPen);
559  QRect onionRect;
560  onionRect.setTopLeft(QPoint(getFrameX(onionFrameNumber - 1), 0));
561  onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), height()));
562  onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), 19));
563  painter.drawRect(onionRect);
564 
565  onionFrameNumber = layer->getNextFrameNumber(onionFrameNumber, isAbsolute);
566  onionPosition++;
567  }
568  }
569 }
570 
571 void TimeLineCells::paintEvent(QPaintEvent*)
572 {
573  Object* object = mEditor->object();
574  Layer* layer = mEditor->layers()->currentLayer();
575 
576  Q_ASSUME(object != nullptr && layer != nullptr);
577 
578  const QPalette palette = QApplication::palette();
579  QPainter painter(this);
580 
581  bool isPlaying = mEditor->playback()->isPlaying();
582  if ((!isPlaying && !mTimeLine->scrubbing) || mCache == nullptr)
583  {
584  drawContent();
585  }
586  if (mCache)
587  {
588  painter.drawPixmap(QPoint(0, 0), *mCache);
589  }
590 
591  if (mType == TIMELINE_CELL_TYPE::Tracks)
592  {
593  if (!isPlaying)
594  {
595  paintOnionSkin(painter);
596  }
597 
598  if (mPrevFrame != mEditor->currentFrame() || mEditor->playback()->isPlaying())
599  {
600  mPrevFrame = mEditor->currentFrame();
601  trackScrubber();
602  }
603 
604  // --- draw the position of the current frame
605  if (mEditor->currentFrame() > mFrameOffset)
606  {
607  QColor scrubColor = palette.color(QPalette::Highlight);
608  scrubColor.setAlpha(160);
609  painter.setBrush(scrubColor);
610  painter.setPen(Qt::NoPen);
611  //painter.setCompositionMode(QPainter::CompositionMode_Source); // this causes the message: QPainter::setCompositionMode: PorterDuff modes not supported on device
612  QRect scrubRect;
613  scrubRect.setTopLeft(QPoint(getFrameX(mEditor->currentFrame() - 1), 0));
614  scrubRect.setBottomRight(QPoint(getFrameX(mEditor->currentFrame()), height()));
615  if (mbShortScrub)
616  {
617  scrubRect.setBottomRight(QPoint(getFrameX(mEditor->currentFrame()), 19));
618  }
619  painter.drawRect(scrubRect);
620  painter.setPen(palette.color(QPalette::HighlightedText));
621  int incr = (mEditor->currentFrame() < 10) ? 4 : 0;
622  painter.drawText(QPoint(getFrameX(mEditor->currentFrame() - 1) + incr, 15),
623  QString::number(mEditor->currentFrame()));
624  }
625  }
626 }
627 
628 void TimeLineCells::resizeEvent(QResizeEvent* event)
629 {
630  clearCache();
631  updateContent();
632  event->accept();
633  emit lengthChanged(getFrameLength());
634 }
635 
636 void TimeLineCells::mousePressEvent(QMouseEvent* event)
637 {
638  int frameNumber = getFrameNumber(event->pos().x());
639  int layerNumber = getLayerNumber(event->pos().y());
640  mFromLayer = mToLayer = layerNumber;
641 
642  mStartY = event->pos().y();
643  mStartLayerNumber = layerNumber;
644  mEndY = event->pos().y();
645 
646  mStartFrameNumber = frameNumber;
647  mLastFrameNumber = mStartFrameNumber;
648 
649  mCanMoveFrame = false;
650  mMovingFrames = false;
651 
652  mCanBoxSelect = false;
653  mBoxSelecting = false;
654 
655  mClickSelecting = false;
656 
657  primaryButton = event->button();
658 
659  bool switchLayer = mEditor->tools()->currentTool()->switchingLayer();
660  if (!switchLayer) { return; }
661 
662  switch (mType)
663  {
664  case TIMELINE_CELL_TYPE::Layers:
665  if (layerNumber != -1 && layerNumber < mEditor->object()->getLayerCount())
666  {
667  if (event->pos().x() < 15)
668  {
669  mEditor->switchVisibilityOfLayer(layerNumber);
670  }
671  else
672  {
673  mEditor->layers()->setCurrentLayer(layerNumber);
674  }
675  }
676  if (layerNumber == -1)
677  {
678  if (event->pos().x() < 15)
679  {
680  if (event->button() == Qt::LeftButton) {
681  mEditor->increaseLayerVisibilityIndex();
682  } else if (event->button() == Qt::RightButton) {
683  mEditor->decreaseLayerVisibilityIndex();
684  }
685  }
686  }
687  break;
688  case TIMELINE_CELL_TYPE::Tracks:
689  if (event->button() == Qt::MidButton)
690  {
691  mLastFrameNumber = getFrameNumber(event->pos().x());
692  }
693  else
694  {
695  if (frameNumber == mEditor->currentFrame() && (!mbShortScrub || (mbShortScrub && mStartY < 20)))
696  {
697  if (mEditor->playback()->isPlaying())
698  {
699  mEditor->playback()->stop();
700  }
701  mTimeLine->scrubbing = true;
702  }
703  else
704  {
705  if ((layerNumber != -1) && layerNumber < mEditor->object()->getLayerCount())
706  {
707  int previousLayerNumber = mEditor->layers()->currentLayerIndex();
708 
709  if (previousLayerNumber != layerNumber) {
710  Layer *previousLayer = mEditor->object()->getLayer(previousLayerNumber);
711  previousLayer->deselectAll();
712 
713  mEditor->layers()->setCurrentLayer(layerNumber);
714  }
715 
716  Layer *currentLayer = mEditor->object()->getLayer(layerNumber);
717 
718  // Check if we are using the alt key
719  if (event->modifiers() == Qt::AltModifier)
720  {
721  // If it is the case, we select everything that is after the selected frame
722  mClickSelecting = true;
723  mCanMoveFrame = true;
724 
725  currentLayer->selectAllFramesAfter(frameNumber);
726  }
727  // Check if we are clicking on a non selected frame
728  else if (!currentLayer->isFrameSelected(frameNumber))
729  {
730  // If it is the case, we select it
731  mCanBoxSelect = true;
732  mClickSelecting = true;
733 
734  if (event->modifiers() == Qt::ControlModifier)
735  {
736  // Add/remove from already selected
737  currentLayer->toggleFrameSelected(frameNumber, true);
738  }
739  else if (event->modifiers() == Qt::ShiftModifier)
740  {
741  // Select a range from the last selected
742  currentLayer->extendSelectionTo(frameNumber);
743  }
744  else
745  {
746  currentLayer->toggleFrameSelected(frameNumber, false);
747  }
748  }
749  else
750  {
751  // We clicked on a selected frame, we can move it
752  mCanMoveFrame = true;
753  }
754 
755  mTimeLine->updateContent();
756  }
757  else
758  {
759  if (frameNumber > 0)
760  {
761  if (mEditor->playback()->isPlaying())
762  {
763  mEditor->playback()->stop();
764  }
765  if (mEditor->playback()->getSoundScrubActive() && mLastScrubFrame != frameNumber)
766  {
767  mEditor->playback()->playScrub(frameNumber);
768  mLastScrubFrame = frameNumber;
769  }
770 
771  mEditor->scrubTo(frameNumber);
772 
773  mTimeLine->scrubbing = true;
774  qDebug("Scrub to %d frame", frameNumber);
775  }
776  }
777  }
778  }
779  break;
780  }
781 }
782 
783 void TimeLineCells::mouseMoveEvent(QMouseEvent* event)
784 {
785  if (mType == TIMELINE_CELL_TYPE::Layers)
786  {
787  mEndY = event->pos().y();
788  emit mouseMovedY(mEndY - mStartY);
789  }
790  else if (mType == TIMELINE_CELL_TYPE::Tracks)
791  {
792  int frameNumber = getFrameNumber(event->pos().x());
793  if (primaryButton == Qt::MidButton)
794  {
795  // qMin( max_frame_offset, qMax ( min_frame_offset, draw_frame_offset ) )
796  mFrameOffset = qMin(qMax(0, mFrameLength - width() / getFrameSize()), qMax(0, mFrameOffset + mLastFrameNumber - frameNumber));
797  update();
798  emit offsetChanged(mFrameOffset);
799  }
800  else
801  {
802  if (mTimeLine->scrubbing)
803  {
804  if (mEditor->playback()->getSoundScrubActive() && mLastScrubFrame != frameNumber)
805  {
806  mEditor->playback()->playScrub(frameNumber);
807  mLastScrubFrame = frameNumber;
808  }
809  mEditor->scrubTo(frameNumber);
810  }
811  else
812  {
813  if (mStartLayerNumber != -1 && mStartLayerNumber < mEditor->object()->getLayerCount())
814  {
815  Layer *currentLayer = mEditor->object()->getLayer(mStartLayerNumber);
816 
817  // Did we move to another frame ?
818  if (frameNumber != mLastFrameNumber)
819  {
820  // Check if the frame we clicked was selected
821  if (mCanMoveFrame) {
822 
823  // If it is the case, we move the selected frames in the layer
824  mMovingFrames = true;
825 
826  int offset = frameNumber - mLastFrameNumber;
827  currentLayer->moveSelectedFrames(offset);
828  mEditor->layers()->notifyAnimationLengthChanged();
829  mEditor->updateCurrentFrame();
830  }
831  else if (mCanBoxSelect)
832  {
833  // Otherwise, we do a box select
834  mBoxSelecting = true;
835 
836  currentLayer->deselectAll();
837  currentLayer->setFrameSelected(mStartFrameNumber, true);
838  currentLayer->extendSelectionTo(frameNumber);
839  }
840  mLastFrameNumber = frameNumber;
841  }
842  }
843  }
844  }
845  }
846  mTimeLine->update();
847 }
848 
849 void TimeLineCells::mouseReleaseEvent(QMouseEvent* event)
850 {
851  if (event->button() != primaryButton) return;
852 
853  primaryButton = Qt::NoButton;
854  mEndY = mStartY;
855  mTimeLine->scrubbing = false;
856  int frameNumber = getFrameNumber(event->pos().x());
857  if (frameNumber < 1) frameNumber = -1;
858  int layerNumber = getLayerNumber(event->pos().y());
859  if (mType == TIMELINE_CELL_TYPE::Tracks && primaryButton != Qt::MidButton && layerNumber != -1 && layerNumber < mEditor->object()->getLayerCount())
860  {
861  Layer *currentLayer = mEditor->object()->getLayer(layerNumber);
862 
863  if (!mTimeLine->scrubbing && !mMovingFrames && !mClickSelecting && !mBoxSelecting)
864  {
865  // De-selecting if we didn't move, scrub nor select anything
866  bool multipleSelection = (event->modifiers() == Qt::ControlModifier);
867 
868  // Add/remove from already selected
869  currentLayer->toggleFrameSelected(frameNumber, multipleSelection);
870  }
871  }
872  if (mType == TIMELINE_CELL_TYPE::Layers && layerNumber != mStartLayerNumber && mStartLayerNumber != -1 && layerNumber != -1)
873  {
874  mToLayer = getInbetweenLayerNumber(event->pos().y());
875  if (mToLayer != mFromLayer && mToLayer > -1 && mToLayer < mEditor->layers()->count())
876  {
877  // Bubble the from layer up or down to the to layer
878  if (mToLayer < mFromLayer) // bubble up
879  {
880  for (int i = mFromLayer - 1; i >= mToLayer; i--)
881  mEditor->swapLayers(i, i + 1);
882  }
883  else // bubble down
884  {
885  for (int i = mFromLayer + 1; i <= mToLayer; i++)
886  mEditor->swapLayers(i, i - 1);
887  }
888  }
889  }
890  emit mouseMovedY(0);
891  mTimeLine->updateContent();
892 }
893 
894 void TimeLineCells::mouseDoubleClickEvent(QMouseEvent* event)
895 {
896  int layerNumber = getLayerNumber(event->pos().y());
897 
898  // -- short scrub --
899  if (event->pos().y() < 20 && (mType != TIMELINE_CELL_TYPE::Layers || event->pos().x() >= 15))
900  {
901  mPrefs->set(SETTING::SHORT_SCRUB, !mbShortScrub);
902  }
903 
904  // -- layer --
905  Layer* layer = mEditor->object()->getLayer(layerNumber);
906  if (layer && mType == TIMELINE_CELL_TYPE::Layers && event->pos().x() >= 15)
907  {
908  editLayerProperties(layer);
909  }
911 }
912 
913 void TimeLineCells::editLayerProperties(Layer *layer) const
914 {
915  if (layer->type() != Layer::CAMERA)
916  {
917  editLayerName(layer);
918  return;
919  }
920 
921  auto cameraLayer = qobject_cast<LayerCamera*>(layer);
922  Q_ASSERT(cameraLayer);
923  editLayerProperties(cameraLayer);
924 }
925 
926 void TimeLineCells::editLayerProperties(LayerCamera *layer) const
927 {
928  QRegExp regex("([\\xFFEF-\\xFFFF])+");
929 
930  CameraPropertiesDialog dialog(layer->name(), layer->getViewRect().width(), layer->getViewRect().height());
931  if (dialog.exec() != QDialog::Accepted)
932  {
933  return;
934  }
935  QString name = dialog.getName().replace(regex, "");
936 
937  if (!name.isEmpty())
938  {
939  mEditor->layers()->renameLayer(layer, name);
940  }
941  QSettings settings(PENCIL2D, PENCIL2D);
942  settings.setValue(SETTING_FIELD_W, dialog.getWidth());
943  settings.setValue(SETTING_FIELD_H, dialog.getHeight());
944  layer->setViewRect(QRect(-dialog.getWidth() / 2, -dialog.getHeight() / 2, dialog.getWidth(), dialog.getHeight()));
945 }
946 
947 void TimeLineCells::editLayerName(Layer* layer) const
948 {
949  QRegExp regex("([\\xFFEF-\\xFFFF])+");
950 
951  bool ok;
952  QString name = QInputDialog::getText(nullptr, tr("Layer Properties"),
953  tr("Layer name:"), QLineEdit::Normal,
954  layer->name(), &ok);
955  name.replace(regex, "");
956  if (!ok || name.isEmpty())
957  {
958  return;
959  }
960 
961  mEditor->layers()->renameLayer(layer, name);
962 }
963 
964 void TimeLineCells::hScrollChange(int x)
965 {
966  mFrameOffset = x;
967  update();
968 }
969 
970 void TimeLineCells::vScrollChange(int x)
971 {
972  mLayerOffset = x;
973  update();
974 }
975 
976 void TimeLineCells::setMouseMoveY(int x)
977 {
978  mMouseMoveY = x;
979  if (x == 0)
980  {
981  update();
982  }
983 }
984 
985 void TimeLineCells::trackScrubber()
986 {
987  if (mEditor->currentFrame() <= mFrameOffset)
988  {
989  // Move the timeline back if the scrubber is offscreen to the left
990  mFrameOffset = mEditor->currentFrame() - 1;
991  emit offsetChanged(mFrameOffset);
992  mTimeLine->updateContent();
993  }
994  else if (width() < (mEditor->currentFrame() - mFrameOffset + 1) * mFrameSize)
995  {
996  // Move timeline forward if the scrubber is offscreen to the right
997  if (mEditor->playback()->isPlaying())
998  mFrameOffset = mFrameOffset + ((mEditor->currentFrame() - mFrameOffset) / 2);
999  else
1000  mFrameOffset = mEditor->currentFrame() - width() / mFrameSize;
1001  emit offsetChanged(mFrameOffset);
1002  mTimeLine->updateContent();
1003  }
1004 }
AltModifier
const QPalette & palette() const const
void setBottomRight(const QPoint &position)
void setCompositionMode(QPainter::CompositionMode mode)
void setRenderHint(QPainter::RenderHint hint, bool on)
const QColor & color(QPalette::ColorGroup group, QPalette::ColorRole role) const const
void save()
LeftButton
void setAttribute(Qt::WidgetAttribute attribute, bool on)
void setAlpha(int alpha)
int height() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
RoundCap
void update()
int x() const const
int y() const const
int width() const const
QSize size() const const
void setMinimumSize(const QSize &)
void drawRect(const QRectF &rectangle)
QString number(int n, int base)
const QBrush & brush(QPalette::ColorGroup group, QPalette::ColorRole role) const const
int x() const const
int red() const const
void setPen(const QColor &color)
void drawEllipse(const QRectF &rectangle)
QPalette palette()
Qt::MouseButton button() const const
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
bool isEmpty() const const
Definition: layer.h:39
void setBrush(const QBrush &brush)
void drawText(const QPointF &position, const QString &text)
void setSizePolicy(QSizePolicy)
QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode, const QString &text, bool *ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints)
Qt::KeyboardModifiers modifiers() const const
int green() const const
CompositionMode_Overlay
bool isNull() const const
RoundJoin
void restore()
virtual void mouseDoubleClickEvent(QMouseEvent *event)
WA_OpaquePaintEvent
void setTopLeft(const QPoint &position)
QString & replace(int position, int n, QChar after)
int blue() const const
int width() const const
Definition: object.h:54
QPoint pos() const const
Definition: editor.h:51
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
QObject * parent() const const
int height() const const