]> git.sesse.net Git - kdenlive/blob - src/keyframeedit.cpp
Allow resetting all parameters of the selected keyframe at once
[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, int minVal, int maxVal, Timecode tc, int active_keyframe, QWidget* parent) :
28         QWidget(parent),
29         m_min(minFrame),
30         m_max(maxFrame),
31         m_minVal(minVal),
32         m_maxVal(maxVal),
33         m_timecode(tc),
34         m_previousPos(0),
35         m_active_keyframe(active_keyframe)
36 {
37     kDebug() << " / / / /MODIFIED KFR: " << m_active_keyframe;
38     setupUi(this);
39     if (m_max == -1) {
40         // special case: keyframe for tracks, do not allow keyframes
41         button_add->setEnabled(false);
42         button_delete->setEnabled(false);
43         keyframe_seek->setEnabled(false);
44         widgetTable->setHidden(true);
45     }
46     m_params.append(e.cloneNode().toElement());
47     keyframe_list->setFont(KGlobalSettings::generalFont());
48     keyframe_seek->setChecked(KdenliveSettings::keyframeseek());
49     connect(keyframe_seek, SIGNAL(stateChanged(int)), this, SLOT(slotSetSeeking(int)));
50
51     buttonKeyframes->setIcon(KIcon("list-add"));
52     button_add->setIcon(KIcon("list-add"));
53     button_add->setToolTip(i18n("Add keyframe"));
54     button_delete->setIcon(KIcon("list-remove"));
55     button_delete->setToolTip(i18n("Delete keyframe"));
56     buttonResetKeyframe->setIcon(KIcon("edit-undo"));
57     connect(keyframe_list, SIGNAL(itemSelectionChanged()), this, SLOT(slotAdjustKeyframeInfo()));
58     connect(keyframe_list, SIGNAL(cellChanged(int, int)), this, SLOT(slotGenerateParams(int, int)));
59     setupParam();
60
61     keyframe_list->resizeRowsToContents();
62     keyframe_list->resizeColumnsToContents();
63     //keyframe_list->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
64     connect(button_delete, SIGNAL(clicked()), this, SLOT(slotDeleteKeyframe()));
65     connect(button_add, SIGNAL(clicked()), this, SLOT(slotAddKeyframe()));
66     connect(buttonKeyframes, SIGNAL(clicked()), this, SLOT(slotKeyframeMode()));
67     connect(buttonResetKeyframe, SIGNAL(clicked()), this, SLOT(slotResetKeyframe()));
68     //connect(keyframe_list, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(slotSaveCurrentParam(QTreeWidgetItem *, int)));
69     connect(keyframe_pos, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframePos(int)));
70     //connect(keyframe_val, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframeValue(int)));
71     keyframe_pos->setPageStep(1);
72     if (!keyframe_list->currentItem()) {
73         keyframe_list->setCurrentCell(0, 0);
74         keyframe_list->selectRow(0);
75     }
76     // ensure the keyframe list shows at least 3 lines
77     keyframe_list->setMinimumHeight(QFontInfo(keyframe_list->font()).pixelSize() * 9);
78     /*m_delegate = new KeyItemDelegate(minVal, maxVal);
79     keyframe_list->setItemDelegate(m_delegate);*/
80
81     // Do not show keyframe table if only one keyframe exists at the beginning
82     if (keyframe_list->rowCount() < 2 && getPos(0) == m_min && m_max != -1)
83         widgetTable->setHidden(true);
84     else
85         buttonKeyframes->setHidden(true);
86 }
87
88 KeyframeEdit::~KeyframeEdit()
89 {
90     keyframe_list->blockSignals(true);
91     keyframe_list->clear();
92     QLayoutItem *child;
93     while ((child = m_slidersLayout->takeAt(0)) != 0) {
94         QWidget *wid = child->widget();
95         delete child;
96         if (wid)
97             delete wid;
98     }
99     //delete m_delegate;
100 }
101
102 void KeyframeEdit::addParameter(QDomElement e)
103 {
104     keyframe_list->blockSignals(true);
105     m_params.append(e.cloneNode().toElement());
106     QDomNode na = e.firstChildElement("name");
107     QString paramName = i18n(na.toElement().text().toUtf8().data());
108     int columnId = keyframe_list->columnCount();
109     keyframe_list->insertColumn(columnId);
110     keyframe_list->setHorizontalHeaderItem(columnId, new QTableWidgetItem(paramName));
111
112     DoubleParameterWidget *doubleparam = new DoubleParameterWidget(paramName, 0,
113             m_params.at(columnId).attribute("min").toInt(), m_params.at(columnId).attribute("max").toInt(),
114             m_params.at(columnId).attribute("default").toInt(), m_params.at(columnId).attribute("suffix"), this);
115     connect(doubleparam, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframeValue(int)));
116     m_slidersLayout->addWidget(doubleparam, columnId, 0);
117
118     QStringList frames = e.attribute("keyframes").split(";", QString::SkipEmptyParts);
119     for (int i = 0; i < frames.count(); i++) {
120         int frame = frames.at(i).section(':', 0, 0).toInt();
121         bool found = false;
122         int j;
123         for (j = 0; j < keyframe_list->rowCount(); j++) {
124             int currentPos = getPos(j);
125             if (frame == currentPos) {
126                 keyframe_list->setItem(j, columnId, new QTableWidgetItem(frames.at(i).section(':', 1, 1)));
127                 found = true;
128                 break;
129             } else if (currentPos > frame) {
130                 break;
131             }
132         }
133         if (!found) {
134             keyframe_list->insertRow(j);
135             keyframe_list->setVerticalHeaderItem(j, new QTableWidgetItem(getPosString(frame)));
136             keyframe_list->setItem(j, columnId, new QTableWidgetItem(frames.at(i).section(':', 1, 1)));
137             keyframe_list->resizeRowToContents(j);
138         }
139     }
140     keyframe_list->resizeColumnsToContents();
141     keyframe_list->blockSignals(false);
142     slotAdjustKeyframeInfo(false);
143 }
144
145 void KeyframeEdit::setupParam()
146 {
147     keyframe_list->blockSignals(true);
148     keyframe_list->clear();
149     int col = keyframe_list->columnCount();
150     QDomNode na = m_params.at(0).firstChildElement("name");
151     QString paramName = i18n(na.toElement().text().toUtf8().data());
152     kDebug() << " INSERT COL: " << col << ", " << paramName;
153     keyframe_list->insertColumn(col);
154     keyframe_list->setHorizontalHeaderItem(col, new QTableWidgetItem(paramName));
155     m_slidersLayout = new QGridLayout(param_sliders);
156
157     DoubleParameterWidget *doubleparam = new DoubleParameterWidget(paramName, 0,
158             m_params.at(0).attribute("min").toInt(), m_params.at(0).attribute("max").toInt(),
159             m_params.at(0).attribute("default").toInt(), m_params.at(0).attribute("suffix"), this);
160     connect(doubleparam, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframeValue(int)));
161     m_slidersLayout->addWidget(doubleparam, 0, 0);
162
163     keyframe_list->setSelectionBehavior(QAbstractItemView::SelectRows);
164     keyframe_list->setSelectionMode(QAbstractItemView::SingleSelection);
165
166     QStringList frames = m_params.at(0).attribute("keyframes").split(";", QString::SkipEmptyParts);
167     setEnabled(frames.count() > 0);
168     for (int i = 0; i < frames.count(); i++) {
169         keyframe_list->insertRow(i);
170         int currentpos = frames.at(i).section(':', 0, 0).toInt();
171         keyframe_list->setVerticalHeaderItem(i, new QTableWidgetItem(getPosString(currentpos)));
172         keyframe_list->setItem(i, col, new QTableWidgetItem(frames.at(i).section(':', 1, 1)));
173         if ((m_active_keyframe > -1) && (m_active_keyframe == currentpos)) {
174             keyframe_list->setCurrentCell(i, 0);
175             keyframe_list->selectRow(i);
176         }
177         //item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled);
178     }
179     /*QTreeWidgetItem *first = keyframe_list->topLevelItem(0);
180     if (first) keyframe_list->setCurrentItem(first);*/
181     keyframe_list->blockSignals(false);
182     //keyframe_list->setCurrentCell(0, 0);
183     slotAdjustKeyframeInfo(false);
184     button_delete->setEnabled(keyframe_list->rowCount() > 1);
185 }
186
187 void KeyframeEdit::slotDeleteKeyframe()
188 {
189     if (keyframe_list->rowCount() < 2)
190         return;
191     int col = keyframe_list->currentColumn();
192     int row = keyframe_list->currentRow();
193     keyframe_list->removeRow(keyframe_list->currentRow());
194     row = qMin(row, keyframe_list->rowCount() - 1);
195     keyframe_list->setCurrentCell(row, col);
196     keyframe_list->selectRow(row);
197     generateAllParams();
198     button_delete->setEnabled(keyframe_list->rowCount() > 1);
199 }
200
201 void KeyframeEdit::slotAddKeyframe()
202 {
203     keyframe_list->blockSignals(true);
204     QTableWidgetItem *item = keyframe_list->currentItem();
205     int row = keyframe_list->currentRow();
206     int col = keyframe_list->currentColumn();
207     int newrow = row;
208     int pos1 = getPos(row);
209
210     int result;
211     kDebug() << "// ADD KF: " << row << ", MAX: " << keyframe_list->rowCount() << ", POS: " << pos1;
212     if (row < (keyframe_list->rowCount() - 1)) {
213         result = pos1 + (getPos(row + 1) - pos1) / 2;
214         newrow++;
215     } else if (row == 0) {
216         if (pos1 == m_min) {
217             result = m_max - 1;
218             newrow++;
219         } else {
220             result = m_min;
221         }
222     } else {
223         int pos2 = getPos(row - 1);
224         result = pos2 + (pos1 - pos2) / 2;
225     }
226
227     keyframe_list->insertRow(newrow);
228     keyframe_list->setVerticalHeaderItem(newrow, new QTableWidgetItem(getPosString(result)));
229     for (int i = 0; i < keyframe_list->columnCount(); i++)
230         keyframe_list->setItem(newrow, i, new QTableWidgetItem(keyframe_list->item(item->row(), i)->text()));
231
232     keyframe_list->resizeRowsToContents();
233     //keyframe_list->resizeRowToContents(newrow);
234     slotAdjustKeyframeInfo();
235     keyframe_list->blockSignals(false);
236     generateAllParams();
237     button_delete->setEnabled(keyframe_list->rowCount() > 1);
238     keyframe_list->setCurrentCell(newrow, col);
239     keyframe_list->selectRow(newrow);
240     //slotGenerateParams(newrow, 0);
241 }
242
243 void KeyframeEdit::slotGenerateParams(int row, int column)
244 {
245     if (column == -1) {
246         // position of keyframe changed
247         QTableWidgetItem *item = keyframe_list->item(row, 0);
248         if (item == NULL)
249             return;
250
251         int pos = getPos(row);
252         if (pos <= m_min)
253             pos = m_min;
254         if (m_max != -1 && pos > m_max)
255             pos = m_max;
256         QString val = getPosString(pos);
257         if (val != keyframe_list->verticalHeaderItem(row)->text())
258             keyframe_list->verticalHeaderItem(row)->setText(val);
259
260         for (int col = 0; col < keyframe_list->horizontalHeader()->count(); col++) {
261             item = keyframe_list->item(row, col);
262             int v = item->text().toInt();
263             if (v >= m_params.at(col).attribute("max").toInt())
264                 item->setText(m_params.at(col).attribute("max"));
265             if (v <= m_params.at(col).attribute("min").toInt())
266                 item->setText(m_params.at(col).attribute("min"));
267             QString keyframes;
268             for (int i = 0; i < keyframe_list->rowCount(); i++) {
269                 if (keyframe_list->item(i, col))
270                     keyframes.append(QString::number(getPos(i)) + ':' + keyframe_list->item(i, col)->text() + ';');
271             }
272             m_params[col].setAttribute("keyframes", keyframes);
273         }
274
275         emit parameterChanged();
276         return;
277
278     }
279     QTableWidgetItem *item = keyframe_list->item(row, column);
280     if (item == NULL)
281         return;
282
283     int pos = getPos(row);
284     if (pos <= m_min)
285         pos = m_min;
286     if (m_max != -1 && pos > m_max)
287         pos = m_max;
288     /*QList<QTreeWidgetItem *> duplicates = keyframe_list->findItems(val, Qt::MatchExactly, 0);
289     duplicates.removeAll(item);
290     if (!duplicates.isEmpty()) {
291         // Trying to insert a keyframe at existing value, revert it
292         val = m_timecode.getTimecodeFromFrames(m_previousPos);
293     }*/
294     QString val = getPosString(pos);
295     if (val != keyframe_list->verticalHeaderItem(row)->text())
296         keyframe_list->verticalHeaderItem(row)->setText(val);
297
298     int v = item->text().toInt();
299     if (v >= m_params.at(column).attribute("max").toInt())
300         item->setText(m_params.at(column).attribute("max"));
301     if (v <= m_params.at(column).attribute("min").toInt())
302         item->setText(m_params.at(column).attribute("min"));
303     slotAdjustKeyframeInfo(false);
304
305     QString keyframes;
306     for (int i = 0; i < keyframe_list->rowCount(); i++) {
307         if (keyframe_list->item(i, column))
308             keyframes.append(QString::number(getPos(i)) + ':' + keyframe_list->item(i, column)->text() + ';');
309     }
310     m_params[column].setAttribute("keyframes", keyframes);
311     emit parameterChanged();
312 }
313
314 void KeyframeEdit::generateAllParams()
315 {
316     for (int col = 0; col < keyframe_list->columnCount(); col++) {
317         QString keyframes;
318         for (int i = 0; i < keyframe_list->rowCount(); i++) {
319             if (keyframe_list->item(i, col))
320                 keyframes.append(QString::number(getPos(i)) + ':' + keyframe_list->item(i, col)->text() + ';');
321         }
322         m_params[col].setAttribute("keyframes", keyframes);
323     }
324     emit parameterChanged();
325 }
326
327 const QString KeyframeEdit::getValue(const QString &name)
328 {
329     for (int col = 0; col < keyframe_list->columnCount(); col++) {
330         QDomNode na = m_params.at(col).firstChildElement("name");
331         QString paramName = i18n(na.toElement().text().toUtf8().data());
332         //kDebug() << paramName << " == " << name;
333         if (paramName == name)
334             return m_params.at(col).attribute("keyframes");
335     }
336     return QString();
337 }
338
339 void KeyframeEdit::slotAdjustKeyframeInfo(bool seek)
340 {
341     QTableWidgetItem *item = keyframe_list->currentItem();
342     if (!item)
343         return;
344     int min = m_min;
345     int max = m_max;
346     QTableWidgetItem *above = keyframe_list->item(item->row() - 1, item->column());
347     QTableWidgetItem *below = keyframe_list->item(item->row() + 1, item->column());
348     if (above)
349         min = getPos(above->row()) + 1;
350     if (below)
351         max = getPos(below->row()) - 1;
352
353     keyframe_pos->blockSignals(true);
354     keyframe_pos->setRange(min, max);
355     keyframe_pos->setValue(getPos(item->row()));
356     keyframe_pos->blockSignals(false);
357     for (int col = 0; col < keyframe_list->columnCount(); col++) {
358         DoubleParameterWidget *doubleparam = static_cast <DoubleParameterWidget*>(m_slidersLayout->itemAtPosition(col, 0)->widget());
359         if (!doubleparam)
360             continue;
361         doubleparam->blockSignals(true);
362         if (keyframe_list->item(item->row(), col)) {
363             doubleparam->setValue(keyframe_list->item(item->row(), col)->text().toInt());
364         } else {
365             kDebug() << "Null pointer exception caught: http://www.kdenlive.org/mantis/view.php?id=1771";
366         }
367         doubleparam->blockSignals(false);
368     }
369     if (KdenliveSettings::keyframeseek() && seek)
370         emit seekToPos(keyframe_pos->value() - m_min);
371 }
372
373 void KeyframeEdit::slotAdjustKeyframePos(int value)
374 {
375     QTableWidgetItem *item = keyframe_list->currentItem();
376     keyframe_list->verticalHeaderItem(item->row())->setText(getPosString(value));
377     slotGenerateParams(item->row(), -1);
378     if (KdenliveSettings::keyframeseek())
379         emit seekToPos(value - m_min);
380 }
381
382 void KeyframeEdit::slotAdjustKeyframeValue(int /*value*/)
383 {
384     QTableWidgetItem *item = keyframe_list->currentItem();
385     for (int col = 0; col < keyframe_list->columnCount(); col++) {
386         DoubleParameterWidget *doubleparam = static_cast <DoubleParameterWidget*>(m_slidersLayout->itemAtPosition(col, 0)->widget());
387         if (!doubleparam)
388             continue;
389         int val = doubleparam->getValue();
390         QTableWidgetItem *nitem = keyframe_list->item(item->row(), col);
391         if (nitem->text().toInt() != val)
392             nitem->setText(QString::number(val));
393     }
394     //keyframe_list->item(item->row() - 1, item->column());
395
396 }
397
398 int KeyframeEdit::getPos(int row)
399 {
400     if (KdenliveSettings::frametimecode())
401         return keyframe_list->verticalHeaderItem(row)->text().toInt();
402     else
403         return m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(row)->text());
404 }
405
406 QString KeyframeEdit::getPosString(int pos)
407 {
408     if (KdenliveSettings::frametimecode())
409         return QString::number(pos);
410     else
411         return m_timecode.getTimecodeFromFrames(pos);
412 }
413
414 void KeyframeEdit::slotSetSeeking(int state)
415 {
416     KdenliveSettings::setKeyframeseek(state == Qt::Checked);
417 }
418
419 void KeyframeEdit::updateTimecodeFormat()
420 {
421     for (int row = 0; row < keyframe_list->rowCount(); ++row) {
422         QString pos = keyframe_list->verticalHeaderItem(row)->text();
423         if (KdenliveSettings::frametimecode())
424             keyframe_list->verticalHeaderItem(row)->setText(QString::number(m_timecode.getFrameCount(pos)));
425         else
426             keyframe_list->verticalHeaderItem(row)->setText(m_timecode.getTimecodeFromFrames(pos.toInt()));
427     }
428 }
429
430 void KeyframeEdit::slotKeyframeMode()
431 {
432     widgetTable->setHidden(false);
433     buttonKeyframes->setHidden(true);
434     slotAddKeyframe();
435 }
436
437 void KeyframeEdit::slotResetKeyframe()
438 {
439     for (int col = 0; col < keyframe_list->columnCount(); ++col) {
440         DoubleParameterWidget *doubleparam = qobject_cast<DoubleParameterWidget*>(m_slidersLayout->itemAtPosition(col, 0)->widget());
441         if (doubleparam)
442             doubleparam->slotReset();
443     }
444 }
445
446
447 /*void KeyframeEdit::slotSaveCurrentParam(QTreeWidgetItem *item, int column)
448 {
449     if (item && column == 0) m_previousPos = m_timecode.getFrameCount(item->text(0));
450 }*/
451