]> git.sesse.net Git - kdenlive/blob - src/keyframeedit.cpp
Cleanup keyframe editor code
[kdenlive] / src / keyframeedit.cpp
1 /***************************************************************************
2                           geomeytrval.cpp  -  description
3                              -------------------
4     begin                : 03 Aug 2008
5     copyright            : (C) 2008 by Marco Gittler
6     email                : g.marco@freenet.de
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include "keyframeedit.h"
19 #include "doubleparameterwidget.h"
20 #include "kdenlivesettings.h"
21
22 #include <KDebug>
23 #include <KGlobalSettings>
24
25 #include <QHeaderView>
26
27 KeyframeEdit::KeyframeEdit(QDomElement e, int minFrame, int maxFrame, Timecode tc, int activeKeyframe, QWidget* parent) :
28         QWidget(parent),
29         m_min(minFrame),
30         m_max(maxFrame),
31         m_timecode(tc)
32 {
33     setupUi(this);
34     if (m_max == -1) {
35         // special case: keyframe for tracks, do not allow keyframes
36         widgetTable->setHidden(true);
37     }
38     keyframe_list->setFont(KGlobalSettings::generalFont());
39     buttonSeek->setChecked(KdenliveSettings::keyframeseek());
40     connect(buttonSeek, SIGNAL(toggled(bool)), this, SLOT(slotSetSeeking(bool)));
41
42     buttonKeyframes->setIcon(KIcon("list-add"));
43     button_add->setIcon(KIcon("list-add"));
44     button_add->setToolTip(i18n("Add keyframe"));
45     button_delete->setIcon(KIcon("list-remove"));
46     button_delete->setToolTip(i18n("Delete keyframe"));
47     buttonResetKeyframe->setIcon(KIcon("edit-undo"));
48     buttonSeek->setIcon(KIcon("insert-link"));
49     connect(keyframe_list, SIGNAL(itemSelectionChanged()), this, SLOT(slotAdjustKeyframeInfo()));
50     connect(keyframe_list, SIGNAL(cellChanged(int, int)), this, SLOT(slotGenerateParams(int, int)));
51
52     m_slidersLayout = new QGridLayout(param_sliders);
53     keyframe_list->setSelectionBehavior(QAbstractItemView::SelectRows);
54     keyframe_list->setSelectionMode(QAbstractItemView::SingleSelection);
55     addParameter(e, activeKeyframe);
56     keyframe_list->resizeRowsToContents();
57
58     //keyframe_list->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
59     connect(button_delete, SIGNAL(clicked()), this, SLOT(slotDeleteKeyframe()));
60     connect(button_add, SIGNAL(clicked()), this, SLOT(slotAddKeyframe()));
61     connect(buttonKeyframes, SIGNAL(clicked()), this, SLOT(slotKeyframeMode()));
62     connect(buttonResetKeyframe, SIGNAL(clicked()), this, SLOT(slotResetKeyframe()));
63     connect(keyframe_pos, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframePos(int)));
64
65     //connect(keyframe_list, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(slotSaveCurrentParam(QTreeWidgetItem *, int)));
66
67     keyframe_pos->setPageStep(1);
68     if (!keyframe_list->currentItem()) {
69         keyframe_list->setCurrentCell(0, 0);
70         keyframe_list->selectRow(0);
71     }
72     // ensure the keyframe list shows at least 3 lines
73     keyframe_list->setMinimumHeight(QFontInfo(keyframe_list->font()).pixelSize() * 9);
74
75     // Do not show keyframe table if only one keyframe exists at the beginning
76     if (keyframe_list->rowCount() < 2 && getPos(0) == m_min && m_max != -1)
77         widgetTable->setHidden(true);
78     else
79         buttonKeyframes->setHidden(true);
80 }
81
82 KeyframeEdit::~KeyframeEdit()
83 {
84     keyframe_list->blockSignals(true);
85     keyframe_list->clear();
86     QLayoutItem *child;
87     while ((child = m_slidersLayout->takeAt(0)) != 0) {
88         QWidget *wid = child->widget();
89         delete child;
90         if (wid)
91             delete wid;
92     }
93 }
94
95 void KeyframeEdit::addParameter(QDomElement e, int activeKeyframe)
96 {
97     keyframe_list->blockSignals(true);
98     m_params.append(e.cloneNode().toElement());
99     QDomNode na = e.firstChildElement("name");
100     QString paramName = i18n(na.toElement().text().toUtf8().data());
101     int columnId = keyframe_list->columnCount();
102     keyframe_list->insertColumn(columnId);
103     keyframe_list->setHorizontalHeaderItem(columnId, new QTableWidgetItem(paramName));
104
105     DoubleParameterWidget *doubleparam = new DoubleParameterWidget(paramName, 0,
106             m_params.at(columnId).attribute("min").toInt(), m_params.at(columnId).attribute("max").toInt(),
107             m_params.at(columnId).attribute("default").toInt(), m_params.at(columnId).attribute("suffix"), this);
108     connect(doubleparam, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframeValue(int)));
109     m_slidersLayout->addWidget(doubleparam, columnId, 0);
110
111     QStringList frames = e.attribute("keyframes").split(";", QString::SkipEmptyParts);
112     for (int i = 0; i < frames.count(); i++) {
113         int frame = frames.at(i).section(':', 0, 0).toInt();
114         bool found = false;
115         int j;
116         for (j = 0; j < keyframe_list->rowCount(); j++) {
117             int currentPos = getPos(j);
118             if (frame == currentPos) {
119                 keyframe_list->setItem(j, columnId, new QTableWidgetItem(frames.at(i).section(':', 1, 1)));
120                 found = true;
121                 break;
122             } else if (currentPos > frame) {
123                 break;
124             }
125         }
126         if (!found) {
127             keyframe_list->insertRow(j);
128             keyframe_list->setVerticalHeaderItem(j, new QTableWidgetItem(getPosString(frame)));
129             keyframe_list->setItem(j, columnId, new QTableWidgetItem(frames.at(i).section(':', 1, 1)));
130             keyframe_list->resizeRowToContents(j);
131         }
132         if ((activeKeyframe > -1) && (activeKeyframe == frame)) {
133             keyframe_list->setCurrentCell(i, columnId);
134             keyframe_list->selectRow(i);
135         }
136     }
137     keyframe_list->resizeColumnsToContents();
138     keyframe_list->blockSignals(false);
139     slotAdjustKeyframeInfo(false);
140     button_delete->setEnabled(keyframe_list->rowCount() > 1);
141 }
142
143 void KeyframeEdit::slotDeleteKeyframe()
144 {
145     if (keyframe_list->rowCount() < 2)
146         return;
147     int col = keyframe_list->currentColumn();
148     int row = keyframe_list->currentRow();
149     keyframe_list->removeRow(keyframe_list->currentRow());
150     row = qMin(row, keyframe_list->rowCount() - 1);
151     keyframe_list->setCurrentCell(row, col);
152     keyframe_list->selectRow(row);
153     generateAllParams();
154     button_delete->setEnabled(keyframe_list->rowCount() > 1);
155 }
156
157 void KeyframeEdit::slotAddKeyframe()
158 {
159     keyframe_list->blockSignals(true);
160     QTableWidgetItem *item = keyframe_list->currentItem();
161     int row = keyframe_list->currentRow();
162     int col = keyframe_list->currentColumn();
163     int newrow = row;
164     int pos1 = getPos(row);
165
166     int result;
167     if (row < (keyframe_list->rowCount() - 1)) {
168         result = pos1 + (getPos(row + 1) - pos1) / 2;
169         newrow++;
170     } else if (row == 0) {
171         if (pos1 == m_min) {
172             result = m_max - 1;
173             newrow++;
174         } else {
175             result = m_min;
176         }
177     } else {
178         int pos2 = getPos(row - 1);
179         result = pos2 + (pos1 - pos2) / 2;
180     }
181
182     keyframe_list->insertRow(newrow);
183     keyframe_list->setVerticalHeaderItem(newrow, new QTableWidgetItem(getPosString(result)));
184     for (int i = 0; i < keyframe_list->columnCount(); i++)
185         keyframe_list->setItem(newrow, i, new QTableWidgetItem(keyframe_list->item(item->row(), i)->text()));
186
187     keyframe_list->resizeRowsToContents();
188     slotAdjustKeyframeInfo();
189     keyframe_list->blockSignals(false);
190     generateAllParams();
191     button_delete->setEnabled(keyframe_list->rowCount() > 1);
192     keyframe_list->setCurrentCell(newrow, col);
193     keyframe_list->selectRow(newrow);
194     //slotGenerateParams(newrow, 0);
195 }
196
197 void KeyframeEdit::slotGenerateParams(int row, int column)
198 {
199     if (column == -1) {
200         // position of keyframe changed
201         QTableWidgetItem *item = keyframe_list->item(row, 0);
202         if (item == NULL)
203             return;
204
205         int pos = getPos(row);
206         if (pos <= m_min)
207             pos = m_min;
208         if (m_max != -1 && pos > m_max)
209             pos = m_max;
210         QString val = getPosString(pos);
211         if (val != keyframe_list->verticalHeaderItem(row)->text())
212             keyframe_list->verticalHeaderItem(row)->setText(val);
213
214         for (int col = 0; col < keyframe_list->horizontalHeader()->count(); col++) {
215             item = keyframe_list->item(row, col);
216             int v = item->text().toInt();
217             if (v >= m_params.at(col).attribute("max").toInt())
218                 item->setText(m_params.at(col).attribute("max"));
219             if (v <= m_params.at(col).attribute("min").toInt())
220                 item->setText(m_params.at(col).attribute("min"));
221             QString keyframes;
222             for (int i = 0; i < keyframe_list->rowCount(); i++) {
223                 if (keyframe_list->item(i, col))
224                     keyframes.append(QString::number(getPos(i)) + ':' + keyframe_list->item(i, col)->text() + ';');
225             }
226             m_params[col].setAttribute("keyframes", keyframes);
227         }
228
229         emit parameterChanged();
230         return;
231
232     }
233     QTableWidgetItem *item = keyframe_list->item(row, column);
234     if (item == NULL)
235         return;
236
237     int pos = getPos(row);
238     if (pos <= m_min)
239         pos = m_min;
240     if (m_max != -1 && pos > m_max)
241         pos = m_max;
242     /*QList<QTreeWidgetItem *> duplicates = keyframe_list->findItems(val, Qt::MatchExactly, 0);
243     duplicates.removeAll(item);
244     if (!duplicates.isEmpty()) {
245         // Trying to insert a keyframe at existing value, revert it
246         val = m_timecode.getTimecodeFromFrames(m_previousPos);
247     }*/
248     QString val = getPosString(pos);
249     if (val != keyframe_list->verticalHeaderItem(row)->text())
250         keyframe_list->verticalHeaderItem(row)->setText(val);
251
252     int v = item->text().toInt();
253     if (v >= m_params.at(column).attribute("max").toInt())
254         item->setText(m_params.at(column).attribute("max"));
255     if (v <= m_params.at(column).attribute("min").toInt())
256         item->setText(m_params.at(column).attribute("min"));
257     slotAdjustKeyframeInfo(false);
258
259     QString keyframes;
260     for (int i = 0; i < keyframe_list->rowCount(); i++) {
261         if (keyframe_list->item(i, column))
262             keyframes.append(QString::number(getPos(i)) + ':' + keyframe_list->item(i, column)->text() + ';');
263     }
264     m_params[column].setAttribute("keyframes", keyframes);
265     emit parameterChanged();
266 }
267
268 void KeyframeEdit::generateAllParams()
269 {
270     for (int col = 0; col < keyframe_list->columnCount(); col++) {
271         QString keyframes;
272         for (int i = 0; i < keyframe_list->rowCount(); i++) {
273             if (keyframe_list->item(i, col))
274                 keyframes.append(QString::number(getPos(i)) + ':' + keyframe_list->item(i, col)->text() + ';');
275         }
276         m_params[col].setAttribute("keyframes", keyframes);
277     }
278     emit parameterChanged();
279 }
280
281 const QString KeyframeEdit::getValue(const QString &name)
282 {
283     for (int col = 0; col < keyframe_list->columnCount(); col++) {
284         QDomNode na = m_params.at(col).firstChildElement("name");
285         QString paramName = i18n(na.toElement().text().toUtf8().data());
286         if (paramName == name)
287             return m_params.at(col).attribute("keyframes");
288     }
289     return QString();
290 }
291
292 void KeyframeEdit::slotAdjustKeyframeInfo(bool seek)
293 {
294     QTableWidgetItem *item = keyframe_list->currentItem();
295     if (!item)
296         return;
297     int min = m_min;
298     int max = m_max;
299     QTableWidgetItem *above = keyframe_list->item(item->row() - 1, item->column());
300     QTableWidgetItem *below = keyframe_list->item(item->row() + 1, item->column());
301     if (above)
302         min = getPos(above->row()) + 1;
303     if (below)
304         max = getPos(below->row()) - 1;
305
306     keyframe_pos->blockSignals(true);
307     keyframe_pos->setRange(min, max);
308     keyframe_pos->setValue(getPos(item->row()));
309     keyframe_pos->blockSignals(false);
310     for (int col = 0; col < keyframe_list->columnCount(); col++) {
311         DoubleParameterWidget *doubleparam = static_cast <DoubleParameterWidget*>(m_slidersLayout->itemAtPosition(col, 0)->widget());
312         if (!doubleparam)
313             continue;
314         doubleparam->blockSignals(true);
315         if (keyframe_list->item(item->row(), col)) {
316             doubleparam->setValue(keyframe_list->item(item->row(), col)->text().toInt());
317         } else {
318             kDebug() << "Null pointer exception caught: http://www.kdenlive.org/mantis/view.php?id=1771";
319         }
320         doubleparam->blockSignals(false);
321     }
322     if (KdenliveSettings::keyframeseek() && seek)
323         emit seekToPos(keyframe_pos->value() - m_min);
324 }
325
326 void KeyframeEdit::slotAdjustKeyframePos(int value)
327 {
328     QTableWidgetItem *item = keyframe_list->currentItem();
329     keyframe_list->verticalHeaderItem(item->row())->setText(getPosString(value));
330     slotGenerateParams(item->row(), -1);
331     if (KdenliveSettings::keyframeseek())
332         emit seekToPos(value - m_min);
333 }
334
335 void KeyframeEdit::slotAdjustKeyframeValue(int value)
336 {
337     Q_UNUSED(value);
338
339     QTableWidgetItem *item = keyframe_list->currentItem();
340     for (int col = 0; col < keyframe_list->columnCount(); col++) {
341         DoubleParameterWidget *doubleparam = static_cast <DoubleParameterWidget*>(m_slidersLayout->itemAtPosition(col, 0)->widget());
342         if (!doubleparam)
343             continue;
344         int val = doubleparam->getValue();
345         QTableWidgetItem *nitem = keyframe_list->item(item->row(), col);
346         if (nitem->text().toInt() != val)
347             nitem->setText(QString::number(val));
348     }
349     //keyframe_list->item(item->row() - 1, item->column());
350
351 }
352
353 int KeyframeEdit::getPos(int row)
354 {
355     if (KdenliveSettings::frametimecode())
356         return keyframe_list->verticalHeaderItem(row)->text().toInt();
357     else
358         return m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(row)->text());
359 }
360
361 QString KeyframeEdit::getPosString(int pos)
362 {
363     if (KdenliveSettings::frametimecode())
364         return QString::number(pos);
365     else
366         return m_timecode.getTimecodeFromFrames(pos);
367 }
368
369 void KeyframeEdit::slotSetSeeking(bool seek)
370 {
371     KdenliveSettings::setKeyframeseek(seek);
372 }
373
374 void KeyframeEdit::updateTimecodeFormat()
375 {
376     for (int row = 0; row < keyframe_list->rowCount(); ++row) {
377         QString pos = keyframe_list->verticalHeaderItem(row)->text();
378         if (KdenliveSettings::frametimecode())
379             keyframe_list->verticalHeaderItem(row)->setText(QString::number(m_timecode.getFrameCount(pos)));
380         else
381             keyframe_list->verticalHeaderItem(row)->setText(m_timecode.getTimecodeFromFrames(pos.toInt()));
382     }
383 }
384
385 void KeyframeEdit::slotKeyframeMode()
386 {
387     widgetTable->setHidden(false);
388     buttonKeyframes->setHidden(true);
389     slotAddKeyframe();
390 }
391
392 void KeyframeEdit::slotResetKeyframe()
393 {
394     for (int col = 0; col < keyframe_list->columnCount(); ++col) {
395         DoubleParameterWidget *doubleparam = static_cast<DoubleParameterWidget*>(m_slidersLayout->itemAtPosition(col, 0)->widget());
396         if (doubleparam)
397             doubleparam->slotReset();
398     }
399 }
400
401 #include "keyframeedit.moc"