All Classes Namespaces Functions Variables Enumerations Properties Pages
timecontrols.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 "timecontrols.h"
19 
20 #include <QLabel>
21 #include <QSettings>
22 #include <QMenu>
23 #include <QDebug>
24 #include "editor.h"
25 #include "playbackmanager.h"
26 #include "layermanager.h"
27 #include "pencildef.h"
28 #include "util.h"
29 #include "preferencemanager.h"
30 #include "timeline.h"
31 #include "pencildef.h"
32 
33 TimeControls::TimeControls(TimeLine* parent) : QToolBar(parent)
34 {
35  mTimeline = parent;
36 }
37 
38 void TimeControls::initUI()
39 {
40  QSettings settings(PENCIL2D, PENCIL2D);
41 
42  mFpsBox = new QSpinBox(this);
43  mFpsBox->setFixedHeight(24);
44  mFpsBox->setValue(settings.value("Fps").toInt());
45  mFpsBox->setMinimum(1);
46  mFpsBox->setMaximum(90);
47  mFpsBox->setSuffix(tr(" fps"));
48  mFpsBox->setToolTip(tr("Frames per second"));
50 
51  mFps = mFpsBox->value();
52  mTimecodeSelect = new QToolButton(this);
53  mTimecodeSelect->setIcon(QIcon(":app/icons/new/svg/more_options.svg"));
54  mTimecodeSelect->setPopupMode(QToolButton::InstantPopup);
55  mTimecodeSelect->addAction(mNoTimecodeAction = new QAction(tr("No text"), this));
56  mTimecodeSelect->addAction(mOnlyFramesAction = new QAction(tr("Frames"), this));
57  mTimecodeSelect->addAction(mSmpteAction = new QAction(tr("SMPTE Timecode"), this));
58  mTimecodeSelect->addAction(mSffAction = new QAction(tr("SFF Timecode"), this));
59  mTimecodeLabelEnum = mEditor->preference()->getInt(SETTING::TIMECODE_TEXT);
60  mTimecodeLabel = new QLabel(this);
61  mTimecodeLabel->setContentsMargins(2, 0, 0, 0);
62  mTimecodeLabel->setText("");
63 
64  switch (mTimecodeLabelEnum)
65  {
66  case NOTEXT:
67  mTimecodeLabel->setToolTip("");
68  break;
69  case FRAMES:
70  mTimecodeLabel->setToolTip(tr("Actual frame number"));
71  break;
72  case SMPTE:
73  mTimecodeLabel->setToolTip(tr("Timecode format MM:SS:FF"));
74  break;
75  case SFF:
76  mTimecodeLabel->setToolTip(tr("Timecode format S:FF"));
77  break;
78  default:
79  mTimecodeLabel->setToolTip("");
80  }
81 
82  mLoopStartSpinBox = new QSpinBox(this);
83  mLoopStartSpinBox->setFixedHeight(24);
84  mLoopStartSpinBox->setValue(settings.value("loopStart").toInt());
85  mLoopStartSpinBox->setMinimum(1);
86  mLoopStartSpinBox->setEnabled(false);
87  mLoopStartSpinBox->setToolTip(tr("Start of playback loop"));
88  mLoopStartSpinBox->setFocusPolicy(Qt::WheelFocus);
89 
90  mLoopEndSpinBox = new QSpinBox(this);
91  mLoopEndSpinBox->setFixedHeight(24);
92  mLoopEndSpinBox->setValue(settings.value("loopEnd").toInt());
93  mLoopEndSpinBox->setMinimum(2);
94  mLoopEndSpinBox->setEnabled(false);
95  mLoopEndSpinBox->setToolTip(tr("End of playback loop"));
96  mLoopEndSpinBox->setFocusPolicy(Qt::WheelFocus);
97 
98  mPlaybackRangeCheckBox = new QCheckBox(tr("Range"));
99  mPlaybackRangeCheckBox->setFixedHeight(24);
100  mPlaybackRangeCheckBox->setToolTip(tr("Playback range"));
101 
102  mPlayButton = new QPushButton(this);
103  mLoopButton = new QPushButton(this);
104  mSoundButton = new QPushButton(this);
105  mSoundScrubButton = new QPushButton(this);
106  mJumpToEndButton = new QPushButton(this);
107  mJumpToStartButton = new QPushButton(this);
108 
109  mLoopIcon = QIcon(":icons/controls/loop.png");
110  mSoundIcon = QIcon();
111  mSoundIcon.addFile(":icons/controls/sound.png", QSize(), QIcon::Normal, QIcon::On);
112  mSoundIcon.addFile(":icons/controls/sound-disabled.png", QSize(), QIcon::Normal, QIcon::Off);
113  mSoundScrubIcon = QIcon();
114  mSoundScrubIcon.addFile(":icons/controls/soundscrub.png", QSize(), QIcon::Normal, QIcon::On);
115  mSoundScrubIcon.addFile(":icons/controls/soundscrub-disabled.png", QSize(), QIcon::Normal, QIcon::Off);
116  mJumpToEndIcon = QIcon(":icons/controls/endplay.png");
117  mJumpToStartIcon = QIcon(":icons/controls/startplay.png");
118  mStartIcon = QIcon(":icons/controls/play.png");
119  mStopIcon = QIcon(":icons/controls/stop.png");
120  mPlayButton->setIcon(mStartIcon);
121  mLoopButton->setIcon(mLoopIcon);
122  mSoundButton->setIcon(mSoundIcon);
123  mSoundScrubButton->setIcon(mSoundScrubIcon);
124  mJumpToEndButton->setIcon(mJumpToEndIcon);
125  mJumpToStartButton->setIcon(mJumpToStartIcon);
126 
127  mPlayButton->setToolTip(tr("Play"));
128  mLoopButton->setToolTip(tr("Loop"));
129  mSoundButton->setToolTip(tr("Sound on/off"));
130  mSoundScrubButton->setToolTip(tr("Sound scrub on/off"));
131  mJumpToEndButton->setToolTip(tr("Jump to the End", "Tooltip of the jump to end button"));
132  mJumpToStartButton->setToolTip(tr("Jump to the Start", "Tooltip of the jump to start button"));
133 
134  mLoopButton->setCheckable(true);
135  mSoundButton->setCheckable(true);
136  mSoundButton->setChecked(true);
137  mSoundScrubButton->setCheckable(true);
138  mSoundScrubButton->setChecked(mEditor->preference()->isOn(SETTING::SOUND_SCRUB_ACTIVE));
139 
140 
141  addWidget(mJumpToStartButton);
142  addWidget(mPlayButton);
143  addWidget(mJumpToEndButton);
144  addWidget(mLoopButton);
145  addWidget(mFpsBox);
146  addWidget(mPlaybackRangeCheckBox);
147  addWidget(mLoopStartSpinBox);
148  addWidget(mLoopEndSpinBox);
149  addWidget(mSoundButton);
150  addWidget(mSoundScrubButton);
151  addWidget(mTimecodeSelect);
152  mTimecodeLabelAction = addWidget(mTimecodeLabel);
153 
154  makeConnections();
155 
156  updateUI();
157 }
158 
159 void TimeControls::updateUI()
160 {
161  PlaybackManager* playback = mEditor->playback();
162 
163  mPlaybackRangeCheckBox->setChecked(playback->isRangedPlaybackOn()); // don't block this signal since it enables start/end range spinboxes.
164 
165  QSignalBlocker b1(mLoopStartSpinBox);
166  mLoopStartSpinBox->setValue(playback->markInFrame());
167 
168  QSignalBlocker b2(mLoopEndSpinBox);
169  mLoopEndSpinBox->setValue(playback->markOutFrame());
170 
171  QSignalBlocker b3(mFpsBox);
172  mFpsBox->setValue(playback->fps());
173 
174  QSignalBlocker b4(mLoopButton);
175  mLoopButton->setChecked(playback->isLooping());
176 }
177 
178 void TimeControls::setEditor(Editor* editor)
179 {
180  Q_ASSERT(editor != nullptr);
181  mEditor = editor;
182 }
183 
184 void TimeControls::setFps(int value)
185 {
186  QSignalBlocker blocker(mFpsBox);
187  mFpsBox->setValue(value);
188  mFps = value;
189  updateTimecodeLabel(mEditor->currentFrame());
190 }
191 
192 void TimeControls::setLoop(bool checked)
193 {
194  mLoopButton->setChecked(checked);
195 }
196 
197 void TimeControls::setRangeState(bool checked)
198 {
199  mPlaybackRangeCheckBox->setChecked(checked);
200  mTimeline->updateLength();
201 }
202 
203 void TimeControls::makeConnections()
204 {
205  connect(mPlayButton, &QPushButton::clicked, this, &TimeControls::playButtonClicked);
206  connect(mJumpToEndButton, &QPushButton::clicked, this, &TimeControls::jumpToEndButtonClicked);
207  connect(mJumpToStartButton, &QPushButton::clicked, this, &TimeControls::jumpToStartButtonClicked);
208  connect(mLoopButton, &QPushButton::clicked, this, &TimeControls::loopButtonClicked);
209  connect(mPlaybackRangeCheckBox, &QCheckBox::clicked, this, &TimeControls::playbackRangeClicked);
210 
211  auto spinBoxValueChanged = static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged);
212  connect(mLoopStartSpinBox, spinBoxValueChanged, this, &TimeControls::loopStartValueChanged);
213  clearFocusOnFinished(mLoopStartSpinBox);
214  connect(mLoopEndSpinBox, spinBoxValueChanged, this, &TimeControls::loopEndValueChanged);
215  clearFocusOnFinished(mLoopEndSpinBox);
216 
217  connect(mPlaybackRangeCheckBox, &QCheckBox::toggled, mLoopStartSpinBox, &QSpinBox::setEnabled);
218  connect(mPlaybackRangeCheckBox, &QCheckBox::toggled, mLoopEndSpinBox, &QSpinBox::setEnabled);
219 
220  connect(mSoundButton, &QPushButton::clicked, this, &TimeControls::soundToggled);
221 
222  connect(mSoundScrubButton, &QPushButton::clicked, this, &TimeControls::soundScrubToggled);
223  connect(mSoundScrubButton, &QPushButton::clicked, this, &TimeControls::updateSoundScrubIcon);
224 
225  connect(mFpsBox, spinBoxValueChanged, this, &TimeControls::fpsChanged);
227 
228  connect(mFpsBox, spinBoxValueChanged, this, &TimeControls::setFps);
229  connect(mEditor, &Editor::fpsChanged, this, &TimeControls::setFps);
230  connect(mNoTimecodeAction, &QAction::triggered, this, &TimeControls::noTimecodeText);
231  connect(mOnlyFramesAction, &QAction::triggered, this, &TimeControls::onlyFramesText);
232  connect(mSmpteAction, &QAction::triggered, this, &TimeControls::smpteText);
233  connect(mSffAction, &QAction::triggered, this, &TimeControls::sffText);
234 }
235 
236 void TimeControls::playButtonClicked()
237 {
238  emit playButtonTriggered();
239 }
240 
241 void TimeControls::updatePlayState()
242 {
243  if (mEditor->playback()->isPlaying())
244  {
245  mPlayButton->setIcon(mStopIcon);
246  mPlayButton->setToolTip(tr("Stop"));
247  }
248  else
249  {
250  mPlayButton->setIcon(mStartIcon);
251  mPlayButton->setToolTip(tr("Play"));
252  }
253 }
254 
255 void TimeControls::jumpToStartButtonClicked()
256 {
257  if (mPlaybackRangeCheckBox->isChecked())
258  {
259  mEditor->scrubTo(mLoopStartSpinBox->value());
260  mEditor->playback()->setCheckForSoundsHalfway(true);
261  }
262  else
263  {
264  mEditor->scrubTo(mEditor->layers()->firstKeyFrameIndex());
265  }
266  mEditor->playback()->stopSounds();
267 }
268 
269 void TimeControls::jumpToEndButtonClicked()
270 {
271  if (mPlaybackRangeCheckBox->isChecked())
272  {
273  mEditor->scrubTo(mLoopEndSpinBox->value());
274  }
275  else
276  {
277  mEditor->scrubTo(mEditor->layers()->lastKeyFrameIndex());
278  }
279 }
280 
281 void TimeControls::loopButtonClicked(bool bChecked)
282 {
283  mEditor->playback()->setLooping(bChecked);
284 }
285 
286 void TimeControls::playbackRangeClicked(bool bChecked)
287 {
288  mEditor->playback()->enableRangedPlayback(bChecked);
289 }
290 
291 void TimeControls::loopStartValueChanged(int i)
292 {
293  if (i >= mLoopEndSpinBox->value())
294  {
295  mLoopEndSpinBox->setValue(i + 1);
296  }
297  mLoopEndSpinBox->setMinimum(i + 1);
298 
299  mEditor->playback()->setRangedStartFrame(i);
300  mTimeline->updateLength();
301 }
302 
303 void TimeControls::loopEndValueChanged(int i)
304 {
305  mEditor->playback()->setRangedEndFrame(i);
306  mTimeline->updateLength();
307 }
308 
309 void TimeControls::updateSoundScrubIcon(bool soundScrubEnabled)
310 {
311  if (soundScrubEnabled)
312  {
313  mEditor->playback()->setSoundScrubActive(true);
314  mEditor->preference()->set(SETTING::SOUND_SCRUB_ACTIVE, true);
315  }
316  else
317  {
318  mEditor->playback()->setSoundScrubActive(false);
319  mEditor->preference()->set(SETTING::SOUND_SCRUB_ACTIVE, false);
320  }
321 }
322 
323 void TimeControls::noTimecodeText()
324 {
325  QSettings settings(PENCIL2D, PENCIL2D);
326  settings.setValue(SETTING_TIMECODE_TEXT, NOTEXT);
327  mTimecodeLabelEnum = NOTEXT;
328  mTimecodeLabel->setToolTip(tr(""));
329  updateTimecodeLabel(mEditor->currentFrame());
330 }
331 
332 void TimeControls::onlyFramesText()
333 {
334  QSettings settings(PENCIL2D, PENCIL2D);
335  settings.setValue(SETTING_TIMECODE_TEXT, FRAMES);
336  mTimecodeLabelEnum = FRAMES;
337  mTimecodeLabel->setToolTip(tr("Actual frame number"));
338  updateTimecodeLabel(mEditor->currentFrame());
339 }
340 
341 void TimeControls::sffText()
342 {
343  QSettings settings(PENCIL2D, PENCIL2D);
344  settings.setValue(SETTING_TIMECODE_TEXT, SFF);
345  mTimecodeLabelEnum = SFF;
346  mTimecodeLabel->setToolTip(tr("Timecode format S:FF"));
347  updateTimecodeLabel(mEditor->currentFrame());
348 }
349 
350 void TimeControls::smpteText()
351 {
352  QSettings settings(PENCIL2D, PENCIL2D);
353  settings.setValue(SETTING_TIMECODE_TEXT, SMPTE);
354  mTimecodeLabelEnum = SMPTE;
355  mTimecodeLabel->setToolTip(tr("Timecode format MM:SS:FF"));
356  updateTimecodeLabel(mEditor->currentFrame());
357 }
358 
360 {
361  mFpsBox->clearFocus();
362  emit fpsChanged(mFpsBox->value());
363  mFps = mFpsBox->value();
364 }
365 
366 void TimeControls::updateTimecodeLabel(int frame)
367 {
368  mTimecodeLabelAction->setVisible(true);
369 
370  switch (mTimecodeLabelEnum)
371  {
372  case TimecodeTextLevel::SMPTE:
373  mTimecodeLabel->setText(QString("%1:%2:%3")
374  .arg(frame / (60 * mFps) % 60, 2, 10, QLatin1Char('0'))
375  .arg(frame / mFps % 60, 2, 10, QLatin1Char('0'))
376  .arg(frame % mFps, 2, 10, QLatin1Char('0')));
377  break;
378  case TimecodeTextLevel::SFF:
379  mTimecodeLabel->setText(QString("%1:%2")
380  .arg(frame / mFps)
381  .arg(frame % mFps, 2, 10, QLatin1Char('0')));
382  break;
383  case TimecodeTextLevel::FRAMES:
384  mTimecodeLabel->setText(QString::number(frame).rightJustified(4, '0'));
385  break;
386  case TimecodeTextLevel::NOTEXT:
387  default:
388  mTimecodeLabelAction->setVisible(false);
389  break;
390  }
391 
392 }
393 
394 void TimeControls::updateLength(int frameLength)
395 {
396  mLoopStartSpinBox->setMaximum(frameLength - 1);
397  mLoopEndSpinBox->setMaximum(frameLength);
398 }
void setMinimum(int min)
void triggered(bool checked)
WheelFocus
void setSuffix(const QString &suffix)
void addAction(QAction *action)
void setFocusPolicy(Qt::FocusPolicy policy)
void setVisible(bool)
QAction * addWidget(QWidget *widget)
void onFpsEditingFinished()
Work-around in case the FPS spin-box "valueChanged" signal doesn't work.
void setIcon(const QIcon &icon)
QString tr(const char *sourceText, const char *disambiguation, int n)
void valueChanged(int i)
void setEnabled(bool)
QString number(int n, int base)
void clicked(bool checked)
void setText(const QString &)
void setCheckable(bool)
void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state)
void setMaximum(int max)
void setChecked(bool)
void clearFocus()
void editingFinished()
void setFixedHeight(int h)
void setValue(int val)
void toggled(bool checked)
void setPopupMode(QToolButton::ToolButtonPopupMode mode)
void setToolTip(const QString &)
Definition: editor.h:55
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
void setContentsMargins(int left, int top, int right, int bottom)