]> git.sesse.net Git - kdenlive/blob - src/simplekeyframes/simpletimelinewidget.cpp
rotoscoping timeline:
[kdenlive] / src / simplekeyframes / simpletimelinewidget.cpp
1 /***************************************************************************
2  *   Copyright (C) 2011 by Till Theato (root@ttill.de)                     *
3  *   This file is part of Kdenlive (www.kdenlive.org).                     *
4  *                                                                         *
5  *   Kdenlive is free software: you can redistribute it and/or modify      *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation, either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   Kdenlive is distributed in the hope that it will be useful,           *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with Kdenlive.  If not, see <http://www.gnu.org/licenses/>.     *
17  ***************************************************************************/
18
19 #include "simpletimelinewidget.h"
20 #include "kdenlivesettings.h"
21
22 #include <QStylePainter>
23 #include <QMouseEvent>
24
25 #include <KGlobalSettings>
26 #include <KColorScheme>
27
28
29 SimpleTimelineWidget::SimpleTimelineWidget(QWidget* parent) :
30         QWidget(parent),
31         m_duration(1),
32         m_position(0),
33         m_currentKeyframe(-1),
34         m_currentKeyframeOriginal(-1),
35         m_hoverKeyframe(-1),
36         m_lineHeight(10),
37         m_scale(1)
38 {
39     setMouseTracking(true);
40     setMinimumSize(QSize(150, 20));
41     setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum));
42     setFont(KGlobalSettings::toolBarFont());
43     QPalette p = palette();
44     KColorScheme scheme(p.currentColorGroup(), KColorScheme::Window, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
45     m_colSelected = scheme.decoration(KColorScheme::HoverColor).color();
46     m_colKeyframe = scheme.foreground(KColorScheme::LinkText).color();
47     m_colKeyframeBg = scheme.shade(KColorScheme::MidShade);
48 }
49
50 void SimpleTimelineWidget::setKeyframes(QList <int> keyframes)
51 {
52     m_keyframes = keyframes;
53     qSort(m_keyframes);
54     m_currentKeyframe = m_currentKeyframeOriginal = -1;
55     update();
56 }
57
58 void SimpleTimelineWidget::slotSetPosition(int pos)
59 {
60     m_position = pos;
61     update();
62 }
63
64 void SimpleTimelineWidget::slotAddKeyframe(int pos, int select)
65 {
66     if (pos < 0)
67         pos = m_position;
68
69     m_keyframes.append(pos);
70     qSort(m_keyframes);
71     if (select)
72         m_currentKeyframe = m_currentKeyframeOriginal = pos;
73     update();
74
75     emit keyframeAdded(pos);
76 }
77
78 void SimpleTimelineWidget::slotAddRemove()
79 {
80     if (m_keyframes.contains(m_position))
81         slotRemoveKeyframe(m_position);
82     else
83         slotAddKeyframe(m_position);
84 }
85
86 void SimpleTimelineWidget::slotRemoveKeyframe(int pos)
87 {
88     m_keyframes.removeAll(pos);
89     if (m_currentKeyframe == pos)
90         m_currentKeyframe = m_currentKeyframeOriginal = -1;
91     update();
92     emit keyframeRemoved(pos);
93 }
94
95 void SimpleTimelineWidget::setDuration(int dur)
96 {
97     m_duration = dur;
98 }
99
100 void SimpleTimelineWidget::slotGoToNext()
101 {
102     foreach (const int &keyframe, m_keyframes) {
103         if (keyframe > m_position) {
104             slotSetPosition(keyframe);
105             emit positionChanged(keyframe);
106             return;
107         }
108     }
109
110     // no keyframe after current position
111     slotSetPosition(m_duration);
112     emit positionChanged(m_duration);
113 }
114
115 void SimpleTimelineWidget::slotGoToPrev()
116 {
117     for (int i = m_keyframes.count() - 1; i >= 0; --i) {
118         if (m_keyframes.at(i) < m_position) {
119             slotSetPosition(m_keyframes.at(i));
120             emit positionChanged(m_keyframes.at(i));
121             return;
122         }
123     }
124
125     // no keyframe before current position
126     slotSetPosition(0);
127     emit positionChanged(0);
128 }
129
130 void SimpleTimelineWidget::mousePressEvent(QMouseEvent* event)
131 {
132     int pos = event->x() / m_scale;
133     if (event->y() < m_lineHeight && event->button() == Qt::LeftButton)  {
134         foreach(const int &keyframe, m_keyframes) {
135             if (qAbs(keyframe - pos) < 5) {
136                 m_currentKeyframeOriginal = keyframe;
137                 m_keyframes[m_keyframes.indexOf(keyframe)] = pos;
138                 m_currentKeyframe = pos;
139                 update();
140                 return;
141             }
142         }
143     }
144
145     // no keyframe next to mouse
146     m_currentKeyframe = m_currentKeyframeOriginal = -1;
147     m_position = pos;
148     emit positionChanged(pos);
149     update();
150 }
151
152 void SimpleTimelineWidget::mouseMoveEvent(QMouseEvent* event)
153 {
154     int pos = qBound(0, (int)(event->x() / m_scale), m_duration);
155     if (event->buttons() & Qt::LeftButton) {
156         if (m_currentKeyframe >= 0) {
157             if (!m_keyframes.contains(pos)) {
158                 // snap to position cursor
159                 if (KdenliveSettings::snaptopoints() && qAbs(pos - m_position) < 5 && !m_keyframes.contains(m_position))
160                     pos = m_position;
161                 // should we maybe sort here?
162                 m_keyframes[m_keyframes.indexOf(m_currentKeyframe)] = pos;
163                 m_currentKeyframe = pos;
164                 emit keyframeMoving(m_currentKeyframeOriginal, m_currentKeyframe);
165             }
166         } else {
167             m_position = pos;
168             emit positionChanged(pos);
169         }
170         update();
171         return;
172     } else {
173         if (event->y() < m_lineHeight) {
174             foreach(const int &keyframe, m_keyframes) {
175                 if (qAbs(keyframe - pos) < 5) {
176                     m_hoverKeyframe = keyframe;
177                     setCursor(Qt::PointingHandCursor);
178                     update();
179                     return;
180                 }
181             }
182         }
183
184         if (m_hoverKeyframe != -1) {
185             m_hoverKeyframe = -1;
186             setCursor(Qt::ArrowCursor);
187             update();
188         }
189     }
190 }
191
192 void SimpleTimelineWidget::mouseReleaseEvent(QMouseEvent* event)
193 {
194     Q_UNUSED(event)
195
196     if (m_currentKeyframe >= 0) {
197         qSort(m_keyframes);
198         emit keyframeMoved(m_currentKeyframeOriginal, m_currentKeyframe);
199     }
200 }
201
202 void SimpleTimelineWidget::wheelEvent(QWheelEvent* event)
203 {
204     int change = event->delta() < 0 ? -1 : 1;
205     if (m_currentKeyframe > 0) {
206         m_currentKeyframe = qBound(0, m_currentKeyframe + change, m_duration);
207         emit keyframeMoved(m_currentKeyframeOriginal, m_currentKeyframe);
208     } else {
209         m_position = qBound(0, m_position + change, m_duration);
210         emit positionChanged(m_position);
211     }
212     update();
213 }
214
215 void SimpleTimelineWidget::paintEvent(QPaintEvent* event)
216 {
217     Q_UNUSED(event)
218
219     QStylePainter p(this);
220     m_scale = width() / (double)(m_duration);
221     p.translate(0, m_lineHeight);
222
223     p.setPen(m_colKeyframe);
224     p.setBrush(m_colKeyframeBg);
225
226     /*
227      * keyframes
228      */
229     QPolygonF keyframe = QPolygonF() << QPointF(0, -10) << QPointF(-4, -6) << QPointF(0, -2) << QPointF(4, -6);
230     QPolygonF tmp;
231     foreach (const int &pos, m_keyframes) {
232         tmp = keyframe;
233         tmp.translate(pos * m_scale, 0);
234         if (pos == m_currentKeyframe || pos == m_hoverKeyframe)
235             p.setBrush(m_colSelected);
236
237         p.drawConvexPolygon(tmp);
238         p.drawLine(QLineF(0, -1, 0, 5).translated(pos * m_scale, 0));
239
240         if (pos == m_currentKeyframe || pos == m_hoverKeyframe)
241             p.setBrush(m_colKeyframeBg);
242     }
243
244     p.setPen(palette().dark().color());
245
246     /*
247      * Time-"line"
248      */
249     p.drawLine(0, 0, width(), 0);
250
251     /*
252      * current position
253      */
254     QPolygonF position = QPolygonF() << QPointF(0, 1) << QPointF(5, 6) << QPointF(-5, 6);
255     position.translate(m_position * m_scale, 0);
256     p.setBrush(palette().dark().color());
257     p.drawConvexPolygon(position);
258 }
259
260 #include "simpletimelinewidget.moc"