]> git.sesse.net Git - kdenlive/blob - src/keyframeedit.cpp
Improve keyframe editor, fix random keyframe sometimes inserted
[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 maxFrame, int minVal, int maxVal, Timecode tc, const QString paramName, QWidget* parent) :
28         QWidget(parent),
29         m_param(e),
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     keyframe_list->setFont(KGlobalSettings::generalFont());
38     keyframe_list->setHeaderLabels(QStringList() << i18n("Position") << (paramName.isEmpty() ? i18n("Value") : paramName));
39     //setResizeMode(1, QHeaderView::Interactive);
40     button_add->setIcon(KIcon("list-add"));
41     button_add->setToolTip(i18n("Add keyframe"));
42     button_delete->setIcon(KIcon("list-remove"));
43     button_delete->setToolTip(i18n("Delete keyframe"));
44     connect(keyframe_list, SIGNAL(itemSelectionChanged()/*itemClicked(QTreeWidgetItem *, int)*/), this, SLOT(slotAdjustKeyframeInfo()));
45     keyframe_val->setRange(m_minVal, m_maxVal);
46     setupParam();
47
48     keyframe_list->header()->resizeSections(QHeaderView::ResizeToContents);
49     connect(button_delete, SIGNAL(clicked()), this, SLOT(slotDeleteKeyframe()));
50     connect(button_add, SIGNAL(clicked()), this, SLOT(slotAddKeyframe()));
51     connect(keyframe_list, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotGenerateParams(QTreeWidgetItem *, int)));
52     connect(keyframe_list, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(slotSaveCurrentParam(QTreeWidgetItem *, int)));
53     connect(keyframe_pos, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframePos(int)));
54     connect(keyframe_val, SIGNAL(valueChanged(int)), this, SLOT(slotAdjustKeyframeValue(int)));
55     keyframe_pos->setPageStep(1);
56     m_delegate = new KeyItemDelegate(minVal, maxVal);
57     keyframe_list->setItemDelegate(m_delegate);
58 }
59
60 KeyframeEdit::~KeyframeEdit()
61 {
62     keyframe_list->blockSignals(true);
63     keyframe_list->clear();
64     delete m_delegate;
65 }
66
67 void KeyframeEdit::setupParam(QDomElement e)
68 {
69     if (!e.isNull()) m_param = e;
70     keyframe_list->clear();
71     QStringList frames = m_param.attribute("keyframes").split(";", QString::SkipEmptyParts);
72     for (int i = 0; i < frames.count(); i++) {
73         QString framePos = m_timecode.getTimecodeFromFrames(frames.at(i).section(':', 0, 0).toInt());
74         QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << framePos << frames.at(i).section(':', 1, 1));
75         item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled);
76         keyframe_list->addTopLevelItem(item);
77     }
78     QTreeWidgetItem *first = keyframe_list->topLevelItem(0);
79     if (first) keyframe_list->setCurrentItem(first);
80     slotAdjustKeyframeInfo();
81     button_delete->setEnabled(keyframe_list->topLevelItemCount() > 1);
82 }
83
84 void KeyframeEdit::slotDeleteKeyframe()
85 {
86     if (keyframe_list->topLevelItemCount() < 2) return;
87     QTreeWidgetItem *item = keyframe_list->currentItem();
88     if (item) {
89         delete item;
90         slotGenerateParams();
91     }
92     button_delete->setEnabled(keyframe_list->topLevelItemCount() > 1);
93 }
94
95 void KeyframeEdit::slotAddKeyframe()
96 {
97     keyframe_list->blockSignals(true);
98     int pos2;
99     QTreeWidgetItem *item = keyframe_list->currentItem();
100     if (item == NULL) return;
101     int ix = keyframe_list->indexOfTopLevelItem(item);
102     int pos1 = m_timecode.getFrameCount(item->text(0));
103     QTreeWidgetItem *below = keyframe_list->topLevelItem(ix + 1);
104     if (below == NULL) below = keyframe_list->topLevelItem(ix - 1);
105     if (below == NULL) {
106         if (pos1 == 0) pos2 = m_max;
107         else pos2 = 0;
108     } else {
109         pos2 = m_timecode.getFrameCount(below->text(0));
110     }
111
112     int result = (pos1 + pos2) / 2;
113     if (result > pos1) ix++;
114     QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList() << m_timecode.getTimecodeFromFrames(result) << item->text(1));
115     newItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled);
116     keyframe_list->insertTopLevelItem(ix, newItem);
117     keyframe_list->setCurrentItem(newItem);
118     slotAdjustKeyframeInfo();
119     keyframe_list->blockSignals(false);
120     button_delete->setEnabled(keyframe_list->topLevelItemCount() > 1);
121     slotGenerateParams();
122 }
123
124 void KeyframeEdit::slotGenerateParams(QTreeWidgetItem *item, int column)
125 {
126     if (item) {
127         if (column == 0) {
128             QString val = item->text(0);
129             int pos = m_timecode.getFrameCount(val);
130             if (pos <= 0) {
131                 pos = 0;
132                 val = m_timecode.getTimecodeFromFrames(pos);
133             }
134             if (pos > m_max) {
135                 pos = m_max;
136                 val = m_timecode.getTimecodeFromFrames(pos);
137             }
138             QList<QTreeWidgetItem *> duplicates = keyframe_list->findItems(val, Qt::MatchExactly, 0);
139             duplicates.removeAll(item);
140             if (!duplicates.isEmpty()) {
141                 // Trying to insert a keyframe at existing value, revert it
142                 val = m_timecode.getTimecodeFromFrames(m_previousPos);
143             }
144             if (val != item->text(0)) item->setText(0, val);
145         }
146         if (column == 1) {
147             if (item->text(1).toInt() >= m_param.attribute("max").toInt()) item->setText(1, m_param.attribute("max"));
148             if (item->text(1).toInt() <= m_param.attribute("min").toInt()) item->setText(1, m_param.attribute("min"));
149         }
150     }
151     QString keyframes;
152     for (int i = 0; i < keyframe_list->topLevelItemCount(); i++) {
153         QTreeWidgetItem *item = keyframe_list->topLevelItem(i);
154         keyframes.append(QString::number(m_timecode.getFrameCount(item->text(0))) + ':' + item->text(1) + ';');
155     }
156     m_param.setAttribute("keyframes", keyframes);
157     emit parameterChanged();
158 }
159
160 void KeyframeEdit::slotAdjustKeyframeInfo()
161 {
162     QTreeWidgetItem *item = keyframe_list->currentItem();
163     if (!item) return;
164     int min = 0;
165     int max = m_max;
166     QTreeWidgetItem *above = keyframe_list->itemAbove(item);
167     QTreeWidgetItem *below = keyframe_list->itemBelow(item);
168     if (above) min = m_timecode.getFrameCount(above->text(0)) + 1;
169     if (below) max = m_timecode.getFrameCount(below->text(0)) - 1;
170     keyframe_pos->blockSignals(true);
171     keyframe_pos->setRange(min, max);
172     keyframe_pos->setValue(m_timecode.getFrameCount(item->text(0)));
173     keyframe_pos->blockSignals(false);
174     keyframe_val->blockSignals(true);
175     keyframe_val->setValue(item->text(1).toInt());
176     keyframe_val->blockSignals(false);
177 }
178
179 void KeyframeEdit::slotAdjustKeyframePos(int value)
180 {
181     QTreeWidgetItem *item = keyframe_list->currentItem();
182     item->setText(0, m_timecode.getTimecodeFromFrames(value));
183 }
184
185 void KeyframeEdit::slotAdjustKeyframeValue(int value)
186 {
187     QTreeWidgetItem *item = keyframe_list->currentItem();
188     item->setText(1, QString::number(value));
189 }
190
191 void KeyframeEdit::slotSaveCurrentParam(QTreeWidgetItem *item, int column)
192 {
193     if (item && column == 0) m_previousPos = m_timecode.getFrameCount(item->text(0));
194 }
195