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