]> git.sesse.net Git - kdenlive/blob - src/geometryval.cpp
[PATCH 3/6] Don't bother null checking before calling delete.
[kdenlive] / src / geometryval.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 "geometryval.h"
19 #include "graphicsscenerectmove.h"
20 #include "kdenlivesettings.h"
21
22 #include <KDebug>
23
24 #include <QGraphicsView>
25 #include <QVBoxLayout>
26 #include <QGraphicsRectItem>
27 #include <QMenu>
28
29
30 Geometryval::Geometryval(const MltVideoProfile profile, QWidget* parent) :
31         QWidget(parent),
32         m_profile(profile),
33         paramRect(NULL),
34         m_geom(NULL),
35         m_path(NULL),
36         m_fixedMode(false)
37 {
38     ui.setupUi(this);
39     QVBoxLayout* vbox = new QVBoxLayout(ui.widget);
40     QGraphicsView *view = new QGraphicsView(this);
41     view->setBackgroundBrush(QBrush(Qt::black));
42     vbox->addWidget(view);
43     vbox->setContentsMargins(0, 0, 0, 0);
44
45     QVBoxLayout* vbox2 = new QVBoxLayout(ui.keyframeWidget);
46     m_helper = new KeyframeHelper(this);
47     vbox2->addWidget(m_helper);
48     vbox2->setContentsMargins(0, 0, 0, 0);
49
50     connect(m_helper, SIGNAL(positionChanged(int)), this, SLOT(slotPositionChanged(int)));
51
52     scene = new GraphicsSceneRectMove(this);
53     scene->setTool(TITLE_SELECT);
54     view->setScene(scene);
55     QGraphicsRectItem *m_frameBorder = new QGraphicsRectItem(QRectF(0, 0, profile.width, profile.height));
56     m_frameBorder->setZValue(-1100);
57     m_frameBorder->setBrush(QColor(255, 255, 0, 30));
58     m_frameBorder->setPen(QPen(QBrush(QColor(255, 255, 255, 255)), 1.0, Qt::DashLine));
59     scene->addItem(m_frameBorder);
60
61     ui.buttonNext->setIcon(KIcon("media-skip-forward"));
62     ui.buttonNext->setToolTip(i18n("Go to next keyframe"));
63     ui.buttonPrevious->setIcon(KIcon("media-skip-backward"));
64     ui.buttonPrevious->setToolTip(i18n("Go to previous keyframe"));
65     ui.buttonAdd->setIcon(KIcon("document-new"));
66     ui.buttonAdd->setToolTip(i18n("Add keyframe"));
67     ui.buttonDelete->setIcon(KIcon("edit-delete"));
68     ui.buttonDelete->setToolTip(i18n("Delete keyframe"));
69
70     QMenu *configMenu = new QMenu(i18n("Misc..."), this);
71     ui.buttonMenu->setIcon(KIcon("system-run"));
72     ui.buttonMenu->setMenu(configMenu);
73     ui.buttonMenu->setPopupMode(QToolButton::QToolButton::InstantPopup);
74
75
76     m_scaleMenu = new QMenu(i18n("Resize..."), this);
77     configMenu->addMenu(m_scaleMenu);
78     m_scaleMenu->addAction(i18n("50%"), this, SLOT(slotResize50()));
79     m_scaleMenu->addAction(i18n("100%"), this, SLOT(slotResize100()));
80     m_scaleMenu->addAction(i18n("200%"), this, SLOT(slotResize200()));
81
82
83     m_alignMenu = new QMenu(i18n("Align..."), this);
84     configMenu->addMenu(m_alignMenu);
85     m_alignMenu->addAction(i18n("Center"), this, SLOT(slotAlignCenter()));
86     m_alignMenu->addAction(i18n("Hor. Center"), this, SLOT(slotAlignHCenter()));
87     m_alignMenu->addAction(i18n("Vert. Center"), this, SLOT(slotAlignVCenter()));
88     m_alignMenu->addAction(i18n("Right"), this, SLOT(slotAlignRight()));
89     m_alignMenu->addAction(i18n("Left"), this, SLOT(slotAlignLeft()));
90     m_alignMenu->addAction(i18n("Top"), this, SLOT(slotAlignTop()));
91     m_alignMenu->addAction(i18n("Bottom"), this, SLOT(slotAlignBottom()));
92
93
94     m_syncAction = configMenu->addAction(i18n("Sync timeline cursor"), this, SLOT(slotSyncCursor()));
95     m_syncAction->setCheckable(true);
96     m_syncAction->setChecked(KdenliveSettings::transitionfollowcursor());
97
98     //scene->setSceneRect(0, 0, profile.width * 2, profile.height * 2);
99     //view->fitInView(m_frameBorder, Qt::KeepAspectRatio);
100     const double sc = 100.0 / profile.height * 0.8;
101     QRectF srect = view->sceneRect();
102     view->setSceneRect(srect.x(), -srect.height() / 3 + 10, srect.width(), srect.height() + srect.height() / 3 * 2 - 10);
103     scene->setZoom(sc);
104     view->centerOn(m_frameBorder);
105     connect(ui.buttonNext , SIGNAL(clicked()) , this , SLOT(slotNextFrame()));
106     connect(ui.buttonPrevious , SIGNAL(clicked()) , this , SLOT(slotPreviousFrame()));
107     connect(ui.buttonDelete , SIGNAL(clicked()) , this , SLOT(slotDeleteFrame()));
108     connect(ui.buttonAdd , SIGNAL(clicked()) , this , SLOT(slotAddFrame()));
109     connect(scene, SIGNAL(actionFinished()), this, SLOT(slotUpdateTransitionProperties()));
110 }
111
112 void Geometryval::slotAlignCenter()
113 {
114     int pos = ui.spinPos->value();
115     Mlt::GeometryItem item;
116     int error = m_geom->fetch(&item, pos);
117     if (error || item.key() == false) {
118         // no keyframe under cursor
119         return;
120     }
121     paramRect->setPos((m_profile.width - paramRect->rect().width()) / 2, (m_profile.height - paramRect->rect().height()) / 2);
122     slotUpdateTransitionProperties();
123 }
124
125 void Geometryval::slotAlignHCenter()
126 {
127     int pos = ui.spinPos->value();
128     Mlt::GeometryItem item;
129     int error = m_geom->fetch(&item, pos);
130     if (error || item.key() == false) {
131         // no keyframe under cursor
132         return;
133     }
134     paramRect->setPos((m_profile.width - paramRect->rect().width()) / 2, paramRect->pos().y());
135     slotUpdateTransitionProperties();
136 }
137
138 void Geometryval::slotAlignVCenter()
139 {
140     int pos = ui.spinPos->value();
141     Mlt::GeometryItem item;
142     int error = m_geom->fetch(&item, pos);
143     if (error || item.key() == false) {
144         // no keyframe under cursor
145         return;
146     }
147     paramRect->setPos(paramRect->pos().x(), (m_profile.height - paramRect->rect().height()) / 2);
148     slotUpdateTransitionProperties();
149 }
150
151 void Geometryval::slotAlignTop()
152 {
153     int pos = ui.spinPos->value();
154     Mlt::GeometryItem item;
155     int error = m_geom->fetch(&item, pos);
156     if (error || item.key() == false) {
157         // no keyframe under cursor
158         return;
159     }
160     paramRect->setPos(paramRect->pos().x(), 0);
161     slotUpdateTransitionProperties();
162 }
163
164 void Geometryval::slotAlignBottom()
165 {
166     int pos = ui.spinPos->value();
167     Mlt::GeometryItem item;
168     int error = m_geom->fetch(&item, pos);
169     if (error || item.key() == false) {
170         // no keyframe under cursor
171         return;
172     }
173     paramRect->setPos(paramRect->pos().x(), m_profile.height - paramRect->rect().height());
174     slotUpdateTransitionProperties();
175 }
176
177 void Geometryval::slotAlignLeft()
178 {
179     int pos = ui.spinPos->value();
180     Mlt::GeometryItem item;
181     int error = m_geom->fetch(&item, pos);
182     if (error || item.key() == false) {
183         // no keyframe under cursor
184         return;
185     }
186     paramRect->setPos(0, paramRect->pos().y());
187     slotUpdateTransitionProperties();
188 }
189
190 void Geometryval::slotAlignRight()
191 {
192     int pos = ui.spinPos->value();
193     Mlt::GeometryItem item;
194     int error = m_geom->fetch(&item, pos);
195     if (error || item.key() == false) {
196         // no keyframe under cursor
197         return;
198     }
199     paramRect->setPos(m_profile.width - paramRect->rect().width(), paramRect->pos().y());
200     slotUpdateTransitionProperties();
201 }
202
203 void Geometryval::slotResize50()
204 {
205     int pos = ui.spinPos->value();
206     Mlt::GeometryItem item;
207     int error = m_geom->fetch(&item, pos);
208     if (error || item.key() == false) {
209         // no keyframe under cursor
210         return;
211     }
212     paramRect->setRect(0, 0, m_profile.width / 2, m_profile.height / 2);
213     slotUpdateTransitionProperties();
214 }
215
216 void Geometryval::slotResize100()
217 {
218     int pos = ui.spinPos->value();
219     Mlt::GeometryItem item;
220     int error = m_geom->fetch(&item, pos);
221     if (error || item.key() == false) {
222         // no keyframe under cursor
223         return;
224     }
225     paramRect->setRect(0, 0, m_profile.width, m_profile.height);
226     slotUpdateTransitionProperties();
227 }
228
229 void Geometryval::slotResize200()
230 {
231     int pos = ui.spinPos->value();
232     Mlt::GeometryItem item;
233     int error = m_geom->fetch(&item, pos);
234     if (error || item.key() == false) {
235         // no keyframe under cursor
236         return;
237     }
238     paramRect->setRect(0, 0, m_profile.width * 2, m_profile.height * 2);
239     slotUpdateTransitionProperties();
240 }
241
242 void Geometryval::slotTransparencyChanged(int transp)
243 {
244     int pos = ui.spinPos->value();
245     Mlt::GeometryItem item;
246     int error = m_geom->fetch(&item, pos);
247     if (error || item.key() == false) {
248         // no keyframe under cursor
249         return;
250     }
251     item.mix(transp);
252     paramRect->setBrush(QColor(255, 0, 0, transp));
253     m_geom->insert(item);
254     emit parameterChanged();
255 }
256
257 void Geometryval::slotSyncCursor()
258 {
259     KdenliveSettings::setTransitionfollowcursor(m_syncAction->isChecked());
260 }
261
262 void Geometryval::slotPositionChanged(int pos, bool seek)
263 {
264     if (seek && KdenliveSettings::transitionfollowcursor()) emit seekToPos(pos);
265     ui.spinPos->setValue(pos);
266     m_helper->setValue(pos);
267     Mlt::GeometryItem item;
268     int error = m_geom->fetch(&item, pos);
269     if (error || item.key() == false) {
270         // no keyframe under cursor, adjust buttons
271         ui.buttonAdd->setEnabled(true);
272         ui.buttonDelete->setEnabled(false);
273         ui.widget->setEnabled(false);
274         ui.spinTransp->setEnabled(false);
275         m_scaleMenu->setEnabled(false);
276         m_alignMenu->setEnabled(false);
277     } else {
278         ui.buttonAdd->setEnabled(false);
279         ui.buttonDelete->setEnabled(true);
280         ui.widget->setEnabled(true);
281         ui.spinTransp->setEnabled(true);
282         m_scaleMenu->setEnabled(true);
283         m_alignMenu->setEnabled(true);
284     }
285     paramRect->setPos(item.x(), item.y());
286     paramRect->setRect(0, 0, item.w(), item.h());
287     ui.spinTransp->setValue(item.mix());
288     paramRect->setBrush(QColor(255, 0, 0, item.mix()));
289 }
290
291 void Geometryval::slotDeleteFrame()
292 {
293     // check there is more than one keyframe
294     Mlt::GeometryItem item;
295     const int pos = ui.spinPos->value();
296     int error = m_geom->next_key(&item, pos + 1);
297     if (error) {
298         error = m_geom->prev_key(&item, pos - 1);
299         if (error || item.frame() == pos) return;
300     }
301
302     m_geom->remove(ui.spinPos->value());
303     ui.buttonAdd->setEnabled(true);
304     ui.buttonDelete->setEnabled(false);
305     ui.widget->setEnabled(false);
306     ui.spinTransp->setEnabled(false);
307     m_scaleMenu->setEnabled(false);
308     m_alignMenu->setEnabled(false);
309     m_helper->update();
310     slotPositionChanged(pos, false);
311     updateTransitionPath();
312     emit parameterChanged();
313 }
314
315 void Geometryval::slotAddFrame()
316 {
317     int pos = ui.spinPos->value();
318     Mlt::GeometryItem item;
319     item.frame(pos);
320     item.x(paramRect->pos().x());
321     item.y(paramRect->pos().y());
322     item.w(paramRect->rect().width());
323     item.h(paramRect->rect().height());
324     item.mix(ui.spinTransp->value());
325     m_geom->insert(item);
326     ui.buttonAdd->setEnabled(false);
327     ui.buttonDelete->setEnabled(true);
328     ui.widget->setEnabled(true);
329     ui.spinTransp->setEnabled(true);
330     m_scaleMenu->setEnabled(true);
331     m_alignMenu->setEnabled(true);
332     m_helper->update();
333     emit parameterChanged();
334 }
335
336 void Geometryval::slotNextFrame()
337 {
338     Mlt::GeometryItem item;
339     int error = m_geom->next_key(&item, m_helper->value() + 1);
340     kDebug() << "// SEEK TO NEXT KFR: " << error;
341     if (error) {
342         // Go to end
343         ui.spinPos->setValue(ui.spinPos->maximum());
344         return;
345     }
346     int pos = item.frame();
347     ui.spinPos->setValue(pos);
348 }
349
350 void Geometryval::slotPreviousFrame()
351 {
352     Mlt::GeometryItem item;
353     int error = m_geom->prev_key(&item, m_helper->value() - 1);
354     kDebug() << "// SEEK TO NEXT KFR: " << error;
355     if (error) return;
356     int pos = item.frame();
357     ui.spinPos->setValue(pos);
358 }
359
360
361 QDomElement Geometryval::getParamDesc()
362 {
363     param.setAttribute("value", m_geom->serialise());
364     kDebug() << " / / UPDATING TRANSITION VALUE: " << param.attribute("value");
365     return param;
366 }
367
368 void Geometryval::setupParam(const QDomElement& par, int minFrame, int maxFrame)
369 {
370     param = par;
371     QString val = par.attribute("value");
372     if (par.attribute("fixed") == "1") {
373         m_fixedMode = true;
374         ui.buttonPrevious->setHidden(true);
375         ui.buttonNext->setHidden(true);
376         ui.buttonDelete->setHidden(true);
377         ui.buttonAdd->setHidden(true);
378         ui.spinTransp->setMaximum(500);
379         ui.label_pos->setHidden(true);
380         m_helper->setHidden(true);
381         ui.spinPos->setHidden(true);
382     }
383     char *tmp = (char *) qstrdup(val.toUtf8().data());
384     if (m_geom) m_geom->parse(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
385     else m_geom = new Mlt::Geometry(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
386     delete[] tmp;
387
388     //kDebug() << " / / UPDATING TRANSITION VALUE: " << m_geom->serialise();
389     //read param her and set rect
390     if (!m_fixedMode) {
391         m_helper->setKeyGeometry(m_geom, maxFrame - minFrame - 1);
392         m_helper->update();
393         /*QDomDocument doc;
394         doc.appendChild(doc.importNode(par, true));
395         kDebug() << "IMPORTED TRANS: " << doc.toString();*/
396         ui.spinPos->setMaximum(maxFrame - minFrame - 1);
397         if (m_path == NULL) {
398             m_path = new QGraphicsPathItem();
399             m_path->setPen(QPen(Qt::red));
400             scene->addItem(m_path);
401         }
402         updateTransitionPath();
403     }
404     Mlt::GeometryItem item;
405
406     m_geom->fetch(&item, 0);
407     delete paramRect;
408     paramRect = new QGraphicsRectItem(QRectF(0, 0, item.w(), item.h()));
409     paramRect->setPos(item.x(), item.y());
410     paramRect->setZValue(0);
411
412     paramRect->setPen(QPen(QBrush(QColor(255, 0, 0, 255)), 1.0));
413     scene->addItem(paramRect);
414     slotPositionChanged(0, false);
415     if (!m_fixedMode) {
416         connect(ui.spinPos, SIGNAL(valueChanged(int)), this , SLOT(slotPositionChanged(int)));
417     }
418     connect(ui.spinTransp, SIGNAL(valueChanged(int)), this , SLOT(slotTransparencyChanged(int)));
419 }
420
421 void Geometryval::updateTransitionPath()
422 {
423     if (m_fixedMode) return;
424     Mlt::GeometryItem item;
425     int pos = 0;
426     int counter = 0;
427     QPainterPath path;
428     while (true) {
429         if (m_geom->next_key(&item, pos) == 1) break;
430         pos = item.frame();
431         if (counter == 0) {
432             path.moveTo(item.x() + item.w() / 2, item.y() + item.h() / 2);
433         } else {
434             path.lineTo(item.x() + item.w() / 2, item.y() + item.h() / 2);
435         }
436         counter++;
437         pos++;
438     }
439     m_path->setPath(path);
440 }
441
442 void Geometryval::slotUpdateTransitionProperties()
443 {
444     int pos = ui.spinPos->value();
445     Mlt::GeometryItem item;
446     int error = m_geom->next_key(&item, pos);
447     if (error || item.frame() != pos) {
448         // no keyframe under cursor
449         return;
450     }
451     item.x(paramRect->pos().x());
452     item.y(paramRect->pos().y());
453     item.w(paramRect->rect().width());
454     item.h(paramRect->rect().height());
455     m_geom->insert(item);
456     updateTransitionPath();
457     emit parameterChanged();
458 }