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