]> git.sesse.net Git - kdenlive/blob - src/keyframeedit.cpp
Fix keyframeeditor for effects like vignette:
[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 "kdenlivesettings.h"
20
21 #include <KDebug>
22 #include <KGlobalSettings>
23
24 #include <QHeaderView>
25
26
27 KeyframeEdit::KeyframeEdit(QDomElement e, int minFrame, int maxFrame, int minVal, int maxVal, Timecode tc, 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 {
36     setupUi(this);
37     m_params.append(e);
38     keyframe_list->setFont(KGlobalSettings::generalFont());
39     keyframe_seek->setChecked(KdenliveSettings::keyframeseek());
40     connect(keyframe_seek, SIGNAL(stateChanged(int)), this, SLOT(slotSetSeeking(int)));
41
42     button_add->setIcon(KIcon("list-add"));
43     button_add->setToolTip(i18n("Add keyframe"));
44     button_delete->setIcon(KIcon("list-remove"));
45     button_delete->setToolTip(i18n("Delete keyframe"));
46     connect(keyframe_list, SIGNAL(itemSelectionChanged()), this, SLOT(slotAdjustKeyframeInfo()));
47     //keyframe_val->setRange(m_minVal, m_maxVal);
48     setupParam();
49     //keyframe_list->sortItems(0);
50     keyframe_list->resizeRowsToContents();
51     keyframe_list->resizeColumnsToContents();
52     keyframe_list->setSelectionBehavior(QAbstractItemView::SelectRows);
53     //keyframe_list->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
54     connect(button_delete, SIGNAL(clicked()), this, SLOT(slotDeleteKeyframe()));
55     connect(button_add, SIGNAL(clicked()), this, SLOT(slotAddKeyframe()));
56     connect(keyframe_list, SIGNAL(cellChanged(int, int)), this, SLOT(slotGenerateParams(int, int)));
57     //connect(keyframe_list, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(slotSaveCurrentParam(QTreeWidgetItem *, int)));
58     connect(keyframe_pos, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframePos(int)));
59     //connect(keyframe_val, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframeValue(int)));
60     keyframe_pos->setPageStep(1);
61
62     /*m_delegate = new KeyItemDelegate(minVal, maxVal);
63     keyframe_list->setItemDelegate(m_delegate);*/
64 }
65
66 KeyframeEdit::~KeyframeEdit()
67 {
68     keyframe_list->blockSignals(true);
69     keyframe_list->clear();
70     QLayoutItem *child;
71     while ((child = m_slidersLayout->takeAt(0)) != 0) {
72         QWidget *wid = child->widget();
73         delete child;
74         if (wid) delete wid;
75     }
76     //delete m_delegate;
77 }
78
79 void KeyframeEdit::addParameter(QDomElement e)
80 {
81     keyframe_list->blockSignals(true);
82     m_params.append(e);
83     QDomNode na = e.firstChildElement("name");
84     QString paramName = i18n(na.toElement().text().toUtf8().data());
85     int columnId = keyframe_list->columnCount();
86     keyframe_list->insertColumn(columnId);
87     keyframe_list->setHorizontalHeaderItem(columnId, new QTableWidgetItem(paramName));
88     m_slidersLayout->addWidget(new QLabel(paramName), columnId, 0);
89     QSlider *sl = new QSlider(Qt::Horizontal, this);
90     sl->setRange(m_params.at(columnId).attribute("min").toInt(), m_params.at(columnId).attribute("max").toInt());
91     connect(sl, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframeValue(int)));
92     m_slidersLayout->addWidget(sl, columnId, 1);
93
94     QStringList frames = e.attribute("keyframes").split(";", QString::SkipEmptyParts);
95     for (int i = 0; i < frames.count(); i++) {
96         int frame = frames.at(i).section(':', 0, 0).toInt();
97         bool found = false;
98         int j;
99         for (j = 0; j < keyframe_list->rowCount(); j++) {
100             int currentPos = m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(j)->text());
101             if (frame == currentPos) {
102                 keyframe_list->setItem(j, columnId, new QTableWidgetItem(frames.at(i).section(':', 1, 1)));
103                 found = true;
104                 break;
105             } else if (currentPos > frame) {
106                 break;
107             }
108         }
109         if (!found) {
110             keyframe_list->insertRow(j);
111             keyframe_list->setVerticalHeaderItem(j, new QTableWidgetItem(m_timecode.getTimecodeFromFrames(frame)));
112             keyframe_list->setItem(j, columnId, new QTableWidgetItem(frames.at(i).section(':', 1, 1)));
113             keyframe_list->resizeRowToContents(j);
114         }
115     }
116     keyframe_list->resizeColumnsToContents();
117     keyframe_list->selectRow(0);
118     keyframe_list->blockSignals(false);
119     slotAdjustKeyframeInfo(false);
120 }
121
122 void KeyframeEdit::setupParam()
123 {
124     keyframe_list->blockSignals(true);
125     keyframe_list->clear();
126     int col = keyframe_list->columnCount();
127     QDomNode na = m_params.at(0).firstChildElement("name");
128     QString paramName = i18n(na.toElement().text().toUtf8().data());
129     kDebug() << " INSERT COL: " << col << ", " << paramName;
130     keyframe_list->insertColumn(col);
131     keyframe_list->setHorizontalHeaderItem(col, new QTableWidgetItem(paramName));
132     m_slidersLayout = new QGridLayout;
133     m_slidersLayout->addWidget(new QLabel(paramName), 0, 0);
134     QSlider *sl = new QSlider(Qt::Horizontal, this);
135     sl->setRange(m_params.at(0).attribute("min").toInt(), m_params.at(0).attribute("max").toInt());
136     connect(sl, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframeValue(int)));
137     m_slidersLayout->addWidget(sl, 0, 1);
138     param_sliders->setLayout(m_slidersLayout);
139     QStringList frames = m_params.at(0).attribute("keyframes").split(";", QString::SkipEmptyParts);
140     for (int i = 0; i < frames.count(); i++) {
141         keyframe_list->insertRow(i);
142         QString framePos = m_timecode.getTimecodeFromFrames(frames.at(i).section(':', 0, 0).toInt());
143         keyframe_list->setVerticalHeaderItem(i, new QTableWidgetItem(framePos));
144         keyframe_list->setItem(i, col, new QTableWidgetItem(frames.at(i).section(':', 1, 1)));
145         //item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled);
146     }
147     /*QTreeWidgetItem *first = keyframe_list->topLevelItem(0);
148     if (first) keyframe_list->setCurrentItem(first);*/
149     keyframe_list->blockSignals(false);
150     keyframe_list->setCurrentCell(0, 0);
151     //slotAdjustKeyframeInfo();
152     button_delete->setEnabled(keyframe_list->rowCount() > 1);
153 }
154
155 void KeyframeEdit::slotDeleteKeyframe()
156 {
157     if (keyframe_list->rowCount() < 2) return;
158     int col = keyframe_list->currentColumn();
159     int row = keyframe_list->currentRow();
160     keyframe_list->removeRow(keyframe_list->currentRow());
161     row = qMin(row, keyframe_list->rowCount() - 1);
162     keyframe_list->setCurrentCell(row, col);
163     generateAllParams();
164     button_delete->setEnabled(keyframe_list->rowCount() > 1);
165 }
166
167 void KeyframeEdit::slotAddKeyframe()
168 {
169     keyframe_list->blockSignals(true);
170     QTableWidgetItem *item = keyframe_list->currentItem();
171     int row = keyframe_list->currentRow();
172     int col = keyframe_list->currentColumn();
173     int newrow = row;
174     int pos1 = m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(row)->text());
175     int result;
176     kDebug() << "// ADD KF: " << row << ", MAX: " << keyframe_list->rowCount() << ", POS: " << pos1;
177     if (row < (keyframe_list->rowCount() - 1)) {
178         int pos2 = m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(row + 1)->text());
179         result = pos1 + (pos2 - pos1) / 2;
180         newrow++;
181     } else if (row == 0) {
182         if (pos1 == m_min) {
183             result = m_max - 1;
184             newrow++;
185         } else {
186             result = m_min;
187         }
188     } else {
189         int pos2 = m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(row - 1)->text());
190         result = pos2 + (pos1 - pos2) / 2;
191     }
192
193     keyframe_list->insertRow(newrow);
194     keyframe_list->setVerticalHeaderItem(newrow, new QTableWidgetItem(m_timecode.getTimecodeFromFrames(result)));
195     for (int i = 0; i < keyframe_list->columnCount(); i++) {
196         keyframe_list->setItem(newrow, i, new QTableWidgetItem(keyframe_list->item(item->row(), i)->text()));
197     }
198     keyframe_list->resizeRowsToContents();
199     //keyframe_list->resizeRowToContents(newrow);
200     slotAdjustKeyframeInfo();
201     keyframe_list->blockSignals(false);
202     generateAllParams();
203     button_delete->setEnabled(keyframe_list->rowCount() > 1);
204     keyframe_list->setCurrentCell(newrow, col);
205     //slotGenerateParams(newrow, 0);
206 }
207
208 void KeyframeEdit::slotGenerateParams(int row, int column)
209 {
210     if (column == -1) {
211         // position of keyframe changed
212         QTableWidgetItem *item = keyframe_list->item(row, 0);
213         if (item == NULL) return;
214         QString val = keyframe_list->verticalHeaderItem(row)->text();
215         int pos = m_timecode.getFrameCount(val);
216         if (pos <= m_min) {
217             pos = m_min;
218             val = m_timecode.getTimecodeFromFrames(pos);
219         }
220         if (pos > m_max) {
221             pos = m_max;
222             val = m_timecode.getTimecodeFromFrames(pos);
223         }
224         if (val != keyframe_list->verticalHeaderItem(row)->text()) keyframe_list->verticalHeaderItem(row)->setText(val);
225
226         for (int col = 0; col < keyframe_list->horizontalHeader()->count(); col++) {
227             item = keyframe_list->item(row, col);
228             int v = item->text().toInt();
229             if (v >= m_params.at(col).attribute("max").toInt()) item->setText(m_params.at(col).attribute("max"));
230             if (v <= m_params.at(col).attribute("min").toInt()) item->setText(m_params.at(col).attribute("min"));
231             QString keyframes;
232             for (int i = 0; i < keyframe_list->rowCount(); i++) {
233                 if (keyframe_list->item(i, col)) keyframes.append(QString::number(m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(i)->text())) + ':' + keyframe_list->item(i, col)->text() + ';');
234             }
235             m_params[col].setAttribute("keyframes", keyframes);
236         }
237
238         emit parameterChanged();
239         return;
240
241     }
242     QTableWidgetItem *item = keyframe_list->item(row, column);
243     if (item == NULL) return;
244     QString val = keyframe_list->verticalHeaderItem(row)->text();
245     int pos = m_timecode.getFrameCount(val);
246     if (pos <= m_min) {
247         pos = m_min;
248         val = m_timecode.getTimecodeFromFrames(pos);
249     }
250     if (pos > m_max) {
251         pos = m_max;
252         val = m_timecode.getTimecodeFromFrames(pos);
253     }
254     /*QList<QTreeWidgetItem *> duplicates = keyframe_list->findItems(val, Qt::MatchExactly, 0);
255     duplicates.removeAll(item);
256     if (!duplicates.isEmpty()) {
257         // Trying to insert a keyframe at existing value, revert it
258         val = m_timecode.getTimecodeFromFrames(m_previousPos);
259     }*/
260     if (val != keyframe_list->verticalHeaderItem(row)->text()) keyframe_list->verticalHeaderItem(row)->setText(val);
261
262     int v = item->text().toInt();
263     if (v >= m_params.at(column).attribute("max").toInt()) item->setText(m_params.at(column).attribute("max"));
264     if (v <= m_params.at(column).attribute("min").toInt()) item->setText(m_params.at(column).attribute("min"));
265     slotAdjustKeyframeInfo(false);
266
267     QString keyframes;
268     for (int i = 0; i < keyframe_list->rowCount(); i++) {
269         if (keyframe_list->item(i, column)) keyframes.append(QString::number(m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(i)->text())) + ':' + keyframe_list->item(i, column)->text() + ';');
270     }
271     m_params[column].setAttribute("keyframes", keyframes);
272     emit parameterChanged();
273 }
274
275 void KeyframeEdit::generateAllParams()
276 {
277     for (int col = 0; col < keyframe_list->columnCount(); col++) {
278         QString keyframes;
279         for (int i = 0; i < keyframe_list->rowCount(); i++) {
280             if (keyframe_list->item(i, col)) keyframes.append(QString::number(m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(i)->text())) + ':' + keyframe_list->item(i, col)->text() + ';');
281         }
282         m_params[col].setAttribute("keyframes", keyframes);
283     }
284     emit parameterChanged();
285 }
286
287 void KeyframeEdit::slotAdjustKeyframeInfo(bool seek)
288 {
289     QTableWidgetItem *item = keyframe_list->currentItem();
290     if (!item) return;
291     int min = m_min;
292     int max = m_max;
293     QTableWidgetItem *above = keyframe_list->item(item->row() - 1, item->column());
294     QTableWidgetItem *below = keyframe_list->item(item->row() + 1, item->column());
295     if (above) min = m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(above->row())->text()) + 1;
296     if (below) max = m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(below->row())->text()) - 1;
297     keyframe_pos->blockSignals(true);
298     keyframe_pos->setRange(min, max);
299     keyframe_pos->setValue(m_timecode.getFrameCount(keyframe_list->verticalHeaderItem(item->row())->text()));
300     keyframe_pos->blockSignals(false);
301     for (int col = 0; col < keyframe_list->columnCount(); col++) {
302         QSlider *sl = static_cast <QSlider*>(m_slidersLayout->itemAtPosition(col, 1)->widget());
303         if (!sl) continue;
304         sl->blockSignals(true);
305         sl->setValue(keyframe_list->item(item->row(), col)->text().toInt());
306         sl->blockSignals(false);
307     }
308     if (KdenliveSettings::keyframeseek() && seek) emit seekToPos(keyframe_pos->value());
309 }
310
311 void KeyframeEdit::slotAdjustKeyframePos(int value)
312 {
313     QTableWidgetItem *item = keyframe_list->currentItem();
314     keyframe_list->verticalHeaderItem(item->row())->setText(m_timecode.getTimecodeFromFrames(value));
315     slotGenerateParams(item->row(), -1);
316     if (KdenliveSettings::keyframeseek()) emit seekToPos(value);
317 }
318
319 void KeyframeEdit::slotAdjustKeyframeValue(int /*value*/)
320 {
321     QTableWidgetItem *item = keyframe_list->currentItem();
322     for (int col = 0; col < keyframe_list->columnCount(); col++) {
323         QSlider *sl = static_cast <QSlider*>(m_slidersLayout->itemAtPosition(col, 1)->widget());
324         if (!sl) continue;
325         int val = sl->value();
326         QTableWidgetItem *nitem = keyframe_list->item(item->row(), col);
327         if (nitem->text().toInt() != val) {
328             nitem->setText(QString::number(val));
329         }
330     }
331     //keyframe_list->item(item->row() - 1, item->column());
332
333 }
334
335 void KeyframeEdit::slotSetSeeking(int state)
336 {
337     KdenliveSettings::setKeyframeseek(state == Qt::Checked);
338 }
339
340
341 /*void KeyframeEdit::slotSaveCurrentParam(QTreeWidgetItem *item, int column)
342 {
343     if (item && column == 0) m_previousPos = m_timecode.getFrameCount(item->text(0));
344 }*/
345