1 /* This file is part of the KDE project
2     SPDX-FileCopyrightText: 2010 Jean-Baptiste Mardelle <jb@kdenlive.org>
3 
4     SPDX-License-Identifier: LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 
7 #include "timecodedisplay.h"
8 #include "kdenlivesettings.h"
9 
10 #include <QFontDatabase>
11 #include <QLineEdit>
12 #include <QMouseEvent>
13 #include <QStyle>
14 
15 #include <KColorScheme>
16 
MyValidator(QObject * parent)17 MyValidator::MyValidator(QObject *parent)
18     : QValidator(parent)
19 {
20 }
21 
fixup(QString & str) const22 void MyValidator::fixup(QString &str) const
23 {
24     str.replace(QLatin1Char(' '), QLatin1Char('0'));
25 }
26 
validate(QString & str,int &) const27 QValidator::State MyValidator::validate(QString &str, int &) const
28 {
29     if (str.contains(QLatin1Char(' '))) {
30         fixup(str);
31     }
32     return QValidator::Acceptable;
33 }
34 
TimecodeDisplay(const Timecode & t,QWidget * parent)35 TimecodeDisplay::TimecodeDisplay(const Timecode &t, QWidget *parent)
36     : QAbstractSpinBox(parent)
37     , m_timecode(t)
38     , m_frametimecode(false)
39     , m_minimum(0)
40     , m_maximum(-1)
41     , m_value(0)
42     , m_offset(0)
43 {
44     const QFont ft = QFontDatabase::systemFont(QFontDatabase::FixedFont);
45     lineEdit()->setFont(ft);
46     setFont(ft);
47     lineEdit()->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
48     QFontMetrics fm(ft);
49     setFrame(false);
50     QPalette palette;
51     palette.setColor(QPalette::Base, Qt::transparent); // palette.window().color());
52     setPalette(palette);
53     setTimeCodeFormat(KdenliveSettings::frametimecode(), true);
54     setValue(m_minimum);
55     setMinimumWidth(fm.horizontalAdvance(QStringLiteral("88:88:88:88")) + contentsMargins().right() + contentsMargins().left() + frameSize().width() -
56                     lineEdit()->contentsRect().width() + (int)QStyle::PM_SpinBoxFrameWidth + 6);
57 
58     setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Maximum);
59     setAccelerated(true);
60     connect(lineEdit(), &QLineEdit::editingFinished, this, &TimecodeDisplay::slotEditingFinished, Qt::DirectConnection);
61 }
62 
63 // virtual protected
stepEnabled() const64 QAbstractSpinBox::StepEnabled TimecodeDisplay::stepEnabled() const
65 {
66     QAbstractSpinBox::StepEnabled result = QAbstractSpinBox::StepNone;
67     if (m_value > m_minimum) {
68         result |= QAbstractSpinBox::StepDownEnabled;
69     }
70     if (m_maximum == -1 || m_value < m_maximum) {
71         result |= QAbstractSpinBox::StepUpEnabled;
72     }
73     return result;
74 }
75 
76 // virtual
stepBy(int steps)77 void TimecodeDisplay::stepBy(int steps)
78 {
79     int val = m_value + steps;
80     setValue(val);
81 }
82 
setTimeCodeFormat(bool frametimecode,bool init)83 void TimecodeDisplay::setTimeCodeFormat(bool frametimecode, bool init)
84 {
85     if (!init && m_frametimecode == frametimecode) {
86         return;
87     }
88     m_frametimecode = frametimecode;
89     lineEdit()->clear();
90     if (m_frametimecode) {
91         auto *valid = new QIntValidator(lineEdit());
92         valid->setBottom(0);
93         lineEdit()->setValidator(valid);
94         lineEdit()->setInputMask(QString());
95     } else {
96         lineEdit()->setInputMask(m_timecode.mask());
97         auto *valid = new MyValidator(lineEdit());
98         lineEdit()->setValidator(valid);
99     }
100     setValue(m_value);
101 }
102 
slotUpdateTimeCodeFormat()103 void TimecodeDisplay::slotUpdateTimeCodeFormat()
104 {
105     setTimeCodeFormat(KdenliveSettings::frametimecode());
106 }
107 
updateTimeCode(const Timecode & t)108 void TimecodeDisplay::updateTimeCode(const Timecode &t)
109 {
110     m_timecode = t;
111     setTimeCodeFormat(KdenliveSettings::frametimecode(), true);
112 }
113 
keyPressEvent(QKeyEvent * e)114 void TimecodeDisplay::keyPressEvent(QKeyEvent *e)
115 {
116     if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
117         e->setAccepted(true);
118         clearFocus();
119     } else {
120         QAbstractSpinBox::keyPressEvent(e);
121     }
122 }
123 
mouseReleaseEvent(QMouseEvent * e)124 void TimecodeDisplay::mouseReleaseEvent(QMouseEvent *e)
125 {
126     QAbstractSpinBox::mouseReleaseEvent(e);
127     if (!lineEdit()->underMouse()) {
128         clearFocus();
129     }
130 }
131 
wheelEvent(QWheelEvent * e)132 void TimecodeDisplay::wheelEvent(QWheelEvent *e)
133 {
134     QAbstractSpinBox::wheelEvent(e);
135     if (hasFocus()) {
136         clearFocus();
137     } else {
138         slotEditingFinished();
139     }
140 }
141 
enterEvent(QEvent * e)142 void TimecodeDisplay::enterEvent(QEvent *e)
143 {
144     QAbstractSpinBox::enterEvent(e);
145     setFrame(true);
146 }
147 
leaveEvent(QEvent * e)148 void TimecodeDisplay::leaveEvent(QEvent *e)
149 {
150     QAbstractSpinBox::leaveEvent(e);
151     setFrame(false);
152 }
153 
maximum() const154 int TimecodeDisplay::maximum() const
155 {
156     return m_maximum;
157 }
158 
minimum() const159 int TimecodeDisplay::minimum() const
160 {
161     return m_minimum;
162 }
163 
getValue() const164 int TimecodeDisplay::getValue() const
165 {
166     return m_value;
167 }
168 
gentime() const169 GenTime TimecodeDisplay::gentime() const
170 {
171     return {m_value, m_timecode.fps()};
172 }
173 
timecode() const174 Timecode TimecodeDisplay::timecode() const
175 {
176     return m_timecode;
177 }
178 
setRange(int min,int max)179 void TimecodeDisplay::setRange(int min, int max)
180 {
181     m_minimum = min;
182     m_maximum = max;
183 }
184 
setValue(const QString & value)185 void TimecodeDisplay::setValue(const QString &value)
186 {
187     setValue(m_timecode.getFrameCount(value));
188 }
189 
setValue(int value)190 void TimecodeDisplay::setValue(int value)
191 {
192     if (m_maximum > 0) {
193         value = qBound(m_minimum, value, m_maximum);
194     } else {
195         value = qMax(m_minimum, value);
196     }
197 
198     if (m_frametimecode) {
199         if (value == m_value && !lineEdit()->text().isEmpty()) {
200             return;
201         }
202         m_value = value;
203         lineEdit()->setText(QString::number(value - m_minimum));
204     } else {
205         if (value == m_value && lineEdit()->text() != QLatin1String(":::")) {
206             return;
207         }
208         m_value = value;
209         lineEdit()->setText(m_timecode.getTimecodeFromFrames(m_offset + value - m_minimum));
210     }
211     emit timeCodeUpdated();
212 }
213 
setValue(const GenTime & value)214 void TimecodeDisplay::setValue(const GenTime &value)
215 {
216     setValue((int)value.frames(m_timecode.fps()));
217 }
218 
slotEditingFinished()219 void TimecodeDisplay::slotEditingFinished()
220 {
221     lineEdit()->deselect();
222     if (m_frametimecode) {
223         setValue(lineEdit()->text().toInt() + m_minimum);
224     } else {
225         setValue(m_timecode.getFrameCount(lineEdit()->text()) + m_minimum - m_offset);
226     }
227     emit timeCodeEditingFinished(m_value);
228 }
229 
displayText() const230 const QString TimecodeDisplay::displayText() const
231 {
232     return lineEdit()->displayText();
233 }
234 
selectAll()235 void TimecodeDisplay::selectAll()
236 {
237     lineEdit()->selectAll();
238 }
239 
setOffset(int offset)240 void TimecodeDisplay::setOffset(int offset)
241 {
242     m_offset = GenTime(offset/1000.).frames(m_timecode.fps());
243     // Update timecode display
244     if (!m_frametimecode) {
245         lineEdit()->setText(m_timecode.getTimecodeFromFrames(m_offset + m_value - m_minimum));
246         emit timeCodeUpdated();
247     }
248 }
249