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