1 /***************************************************************************
2 geomeytrval.cpp - description
5 copyright : (C) 2008 by Marco Gittler
6 email : g.marco@freenet.de
7 ***************************************************************************/
9 /***************************************************************************
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. *
16 ***************************************************************************/
20 #include <QGraphicsView>
21 #include <QVBoxLayout>
22 #include <QGraphicsScene>
23 #include <QGraphicsRectItem>
24 #include <QMouseEvent>
29 #include "graphicsscenerectmove.h"
30 #include "geometryval.h"
31 #include "kdenlivesettings.h"
33 Geometryval::Geometryval(const MltVideoProfile profile, QWidget* parent): QWidget(parent), m_profile(profile), m_geom(NULL), m_path(NULL), paramRect(NULL), m_fixedMode(false) {
35 QVBoxLayout* vbox = new QVBoxLayout(ui.widget);
36 QGraphicsView *view = new QGraphicsView(this);
37 view->setBackgroundBrush(QBrush(Qt::black));
38 vbox->addWidget(view);
39 vbox->setContentsMargins(0, 0, 0, 0);
41 QVBoxLayout* vbox2 = new QVBoxLayout(ui.keyframeWidget);
42 m_helper = new KeyframeHelper(this);
43 vbox2->addWidget(m_helper);
44 vbox2->setContentsMargins(0, 0, 0, 0);
46 connect(m_helper, SIGNAL(positionChanged(int)), this, SLOT(slotPositionChanged(int)));
48 scene = new GraphicsSceneRectMove(this);
49 scene->setTool(TITLE_SELECT);
50 view->setScene(scene);
51 double aspect = (double) profile.sample_aspect_num / profile.sample_aspect_den * profile.width / profile.height;
52 QGraphicsRectItem *m_frameBorder = new QGraphicsRectItem(QRectF(0, 0, profile.width, profile.height));
53 m_frameBorder->setZValue(-1100);
54 m_frameBorder->setBrush(QColor(255, 255, 0, 30));
55 m_frameBorder->setPen(QPen(QBrush(QColor(255, 255, 255, 255)), 1.0, Qt::DashLine));
56 scene->addItem(m_frameBorder);
58 ui.buttonNext->setIcon(KIcon("media-skip-forward"));
59 ui.buttonNext->setToolTip(i18n("Go to next keyframe"));
60 ui.buttonPrevious->setIcon(KIcon("media-skip-backward"));
61 ui.buttonPrevious->setToolTip(i18n("Go to previous keyframe"));
62 ui.buttonAdd->setIcon(KIcon("document-new"));
63 ui.buttonAdd->setToolTip(i18n("Add keyframe"));
64 ui.buttonDelete->setIcon(KIcon("edit-delete"));
65 ui.buttonDelete->setToolTip(i18n("Delete keyframe"));
67 QMenu *configMenu = new QMenu(i18n("Misc..."), this);
68 ui.buttonMenu->setIcon(KIcon("system-run"));
69 ui.buttonMenu->setMenu(configMenu);
70 ui.buttonMenu->setPopupMode(QToolButton::QToolButton::InstantPopup);
73 m_scaleMenu = new QMenu(i18n("Resize..."), this);
74 configMenu->addMenu(m_scaleMenu);
75 m_scaleMenu->addAction(i18n("50%"), this, SLOT(slotResize50()));
76 m_scaleMenu->addAction(i18n("100%"), this, SLOT(slotResize100()));
77 m_scaleMenu->addAction(i18n("200%"), this, SLOT(slotResize200()));
80 m_alignMenu = new QMenu(i18n("Align..."), this);
81 configMenu->addMenu(m_alignMenu);
82 m_alignMenu->addAction(i18n("Center"), this, SLOT(slotAlignCenter()));
83 m_alignMenu->addAction(i18n("Hor. Center"), this, SLOT(slotAlignHCenter()));
84 m_alignMenu->addAction(i18n("Vert. Center"), this, SLOT(slotAlignVCenter()));
85 m_alignMenu->addAction(i18n("Right"), this, SLOT(slotAlignRight()));
86 m_alignMenu->addAction(i18n("Left"), this, SLOT(slotAlignLeft()));
87 m_alignMenu->addAction(i18n("Top"), this, SLOT(slotAlignTop()));
88 m_alignMenu->addAction(i18n("Bottom"), this, SLOT(slotAlignBottom()));
91 m_syncAction = configMenu->addAction(i18n("Sync timeline cursor"), this, SLOT(slotSyncCursor()));
92 m_syncAction->setCheckable(true);
93 m_syncAction->setChecked(KdenliveSettings::transitionfollowcursor());
95 //scene->setSceneRect(0, 0, profile.width * 2, profile.height * 2);
96 //view->fitInView(m_frameBorder, Qt::KeepAspectRatio);
97 const double sc = 100.0 / profile.height * 0.8;
98 QRectF srect = view->sceneRect();
99 view->setSceneRect(srect.x(), -srect.height() / 3, srect.width(), srect.height() + srect.height() / 3 * 2);
101 view->centerOn(m_frameBorder);
102 connect(ui.buttonNext , SIGNAL(clicked()) , this , SLOT(slotNextFrame()));
103 connect(ui.buttonPrevious , SIGNAL(clicked()) , this , SLOT(slotPreviousFrame()));
104 connect(ui.buttonDelete , SIGNAL(clicked()) , this , SLOT(slotDeleteFrame()));
105 connect(ui.buttonAdd , SIGNAL(clicked()) , this , SLOT(slotAddFrame()));
106 connect(scene, SIGNAL(actionFinished()), this, SLOT(slotUpdateTransitionProperties()));
109 void Geometryval::slotAlignCenter() {
110 int pos = ui.spinPos->value();
111 Mlt::GeometryItem item;
112 int error = m_geom->fetch(&item, pos);
113 if (error || item.key() == false) {
114 // no keyframe under cursor
117 paramRect->setPos((m_profile.width - paramRect->rect().width()) / 2, (m_profile.height - paramRect->rect().height()) / 2);
118 slotUpdateTransitionProperties();
121 void Geometryval::slotAlignHCenter() {
122 int pos = ui.spinPos->value();
123 Mlt::GeometryItem item;
124 int error = m_geom->fetch(&item, pos);
125 if (error || item.key() == false) {
126 // no keyframe under cursor
129 paramRect->setPos((m_profile.width - paramRect->rect().width()) / 2, paramRect->pos().y());
130 slotUpdateTransitionProperties();
133 void Geometryval::slotAlignVCenter() {
134 int pos = ui.spinPos->value();
135 Mlt::GeometryItem item;
136 int error = m_geom->fetch(&item, pos);
137 if (error || item.key() == false) {
138 // no keyframe under cursor
141 paramRect->setPos(paramRect->pos().x(), (m_profile.height - paramRect->rect().height()) / 2);
142 slotUpdateTransitionProperties();
145 void Geometryval::slotAlignTop() {
146 int pos = ui.spinPos->value();
147 Mlt::GeometryItem item;
148 int error = m_geom->fetch(&item, pos);
149 if (error || item.key() == false) {
150 // no keyframe under cursor
153 paramRect->setPos(paramRect->pos().x(), 0);
154 slotUpdateTransitionProperties();
157 void Geometryval::slotAlignBottom() {
158 int pos = ui.spinPos->value();
159 Mlt::GeometryItem item;
160 int error = m_geom->fetch(&item, pos);
161 if (error || item.key() == false) {
162 // no keyframe under cursor
165 paramRect->setPos(paramRect->pos().x(), m_profile.height - paramRect->rect().height());
166 slotUpdateTransitionProperties();
169 void Geometryval::slotAlignLeft() {
170 int pos = ui.spinPos->value();
171 Mlt::GeometryItem item;
172 int error = m_geom->fetch(&item, pos);
173 if (error || item.key() == false) {
174 // no keyframe under cursor
177 paramRect->setPos(0, paramRect->pos().y());
178 slotUpdateTransitionProperties();
181 void Geometryval::slotAlignRight() {
182 int pos = ui.spinPos->value();
183 Mlt::GeometryItem item;
184 int error = m_geom->fetch(&item, pos);
185 if (error || item.key() == false) {
186 // no keyframe under cursor
189 paramRect->setPos(m_profile.width - paramRect->rect().width(), paramRect->pos().y());
190 slotUpdateTransitionProperties();
193 void Geometryval::slotResize50() {
194 int pos = ui.spinPos->value();
195 Mlt::GeometryItem item;
196 int error = m_geom->fetch(&item, pos);
197 if (error || item.key() == false) {
198 // no keyframe under cursor
201 paramRect->setRect(0, 0, m_profile.width / 2, m_profile.height / 2);
202 slotUpdateTransitionProperties();
205 void Geometryval::slotResize100() {
206 int pos = ui.spinPos->value();
207 Mlt::GeometryItem item;
208 int error = m_geom->fetch(&item, pos);
209 if (error || item.key() == false) {
210 // no keyframe under cursor
213 paramRect->setRect(0, 0, m_profile.width, m_profile.height);
214 slotUpdateTransitionProperties();
217 void Geometryval::slotResize200() {
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
225 paramRect->setRect(0, 0, m_profile.width * 2, m_profile.height * 2);
226 slotUpdateTransitionProperties();
229 void Geometryval::slotTransparencyChanged(int transp) {
230 int pos = ui.spinPos->value();
231 Mlt::GeometryItem item;
232 int error = m_geom->fetch(&item, pos);
233 if (error || item.key() == false) {
234 // no keyframe under cursor
238 paramRect->setBrush(QColor(255, 0, 0, transp));
239 m_geom->insert(item);
240 emit parameterChanged();
243 void Geometryval::slotSyncCursor() {
244 KdenliveSettings::setTransitionfollowcursor(m_syncAction->isChecked());
247 void Geometryval::slotPositionChanged(int pos, bool seek) {
248 if (seek && KdenliveSettings::transitionfollowcursor()) emit seekToPos(pos);
249 ui.spinPos->setValue(pos);
250 m_helper->setValue(pos);
251 Mlt::GeometryItem item;
252 int error = m_geom->fetch(&item, pos);
253 if (error || item.key() == false) {
254 // no keyframe under cursor, adjust buttons
255 ui.buttonAdd->setEnabled(true);
256 ui.buttonDelete->setEnabled(false);
257 ui.widget->setEnabled(false);
258 ui.spinTransp->setEnabled(false);
259 m_scaleMenu->setEnabled(false);
260 m_alignMenu->setEnabled(false);
262 ui.buttonAdd->setEnabled(false);
263 ui.buttonDelete->setEnabled(true);
264 ui.widget->setEnabled(true);
265 ui.spinTransp->setEnabled(true);
266 m_scaleMenu->setEnabled(true);
267 m_alignMenu->setEnabled(true);
269 paramRect->setPos(item.x(), item.y());
270 paramRect->setRect(0, 0, item.w(), item.h());
271 ui.spinTransp->setValue(item.mix());
272 paramRect->setBrush(QColor(255, 0, 0, item.mix()));
275 void Geometryval::slotDeleteFrame() {
276 // check there is more than one keyframe
277 Mlt::GeometryItem item;
278 const int pos = ui.spinPos->value();
279 int error = m_geom->next_key(&item, pos + 1);
281 error = m_geom->prev_key(&item, pos - 1);
282 if (error || item.frame() == pos) return;
285 m_geom->remove(ui.spinPos->value());
286 ui.buttonAdd->setEnabled(true);
287 ui.buttonDelete->setEnabled(false);
288 ui.widget->setEnabled(false);
289 ui.spinTransp->setEnabled(false);
290 m_scaleMenu->setEnabled(false);
291 m_alignMenu->setEnabled(false);
295 void Geometryval::slotAddFrame() {
296 int pos = ui.spinPos->value();
297 Mlt::GeometryItem item;
299 item.x(paramRect->pos().x());
300 item.y(paramRect->pos().y());
301 item.w(paramRect->rect().width());
302 item.h(paramRect->rect().height());
303 item.mix(ui.spinTransp->value());
304 m_geom->insert(item);
305 ui.buttonAdd->setEnabled(false);
306 ui.buttonDelete->setEnabled(true);
307 ui.widget->setEnabled(true);
308 ui.spinTransp->setEnabled(true);
309 m_scaleMenu->setEnabled(true);
310 m_alignMenu->setEnabled(true);
314 void Geometryval::slotNextFrame() {
315 Mlt::GeometryItem item;
316 int error = m_geom->next_key(&item, m_helper->value() + 1);
317 kDebug() << "// SEEK TO NEXT KFR: " << error;
320 ui.spinPos->setValue(ui.spinPos->maximum());
323 int pos = item.frame();
324 ui.spinPos->setValue(pos);
327 void Geometryval::slotPreviousFrame() {
328 Mlt::GeometryItem item;
329 int error = m_geom->prev_key(&item, m_helper->value() - 1);
330 kDebug() << "// SEEK TO NEXT KFR: " << error;
332 int pos = item.frame();
333 ui.spinPos->setValue(pos);
337 QDomElement Geometryval::getParamDesc() {
338 param.setAttribute("value", m_geom->serialise());
339 kDebug() << " / / UPDATING TRANSITION VALUE: " << param.attribute("value");
343 void Geometryval::setupParam(const QDomElement& par, int minFrame, int maxFrame) {
345 QString val = par.attribute("value");
346 if (par.attribute("fixed") == "1") {
349 char *tmp = (char *) qstrdup(val.toUtf8().data());
350 if (m_geom) m_geom->parse(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
351 else m_geom = new Mlt::Geometry(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
354 //kDebug() << " / / UPDATING TRANSITION VALUE: " << m_geom->serialise();
355 //read param her and set rect
357 m_helper->setHidden(true);
358 ui.spinPos->setHidden(true);
360 m_helper->setKeyGeometry(m_geom, maxFrame - minFrame - 1);
363 doc.appendChild(doc.importNode(par, true));
364 kDebug() << "IMPORTED TRANS: " << doc.toString();*/
365 ui.spinPos->setMaximum(maxFrame - minFrame - 1);
366 if (m_path == NULL) {
367 m_path = new QGraphicsPathItem();
368 m_path->setPen(QPen(Qt::red));
369 scene->addItem(m_path);
371 updateTransitionPath();
373 Mlt::GeometryItem item;
375 m_geom->fetch(&item, 0);
376 if (paramRect) delete paramRect;
377 paramRect = new QGraphicsRectItem(QRectF(0, 0, item.w(), item.h()));
378 paramRect->setPos(item.x(), item.y());
379 paramRect->setZValue(0);
381 paramRect->setPen(QPen(QBrush(QColor(255, 0, 0, 255)), 1.0));
382 scene->addItem(paramRect);
383 slotPositionChanged(0, false);
385 connect(ui.spinPos, SIGNAL(valueChanged(int)), this , SLOT(slotPositionChanged(int)));
386 connect(ui.spinTransp, SIGNAL(valueChanged(int)), this , SLOT(slotTransparencyChanged(int)));
390 void Geometryval::updateTransitionPath() {
391 if (m_fixedMode) return;
392 Mlt::GeometryItem item;
397 if (m_geom->next_key(&item, pos) == 1) break;
400 path.moveTo(item.x() + item.w() / 2, item.y() + item.h() / 2);
402 path.lineTo(item.x() + item.w() / 2, item.y() + item.h() / 2);
407 m_path->setPath(path);
410 void Geometryval::slotUpdateTransitionProperties() {
411 int pos = ui.spinPos->value();
412 Mlt::GeometryItem item;
413 int error = m_geom->next_key(&item, pos);
414 if (error || item.frame() != pos) {
415 // no keyframe under cursor
418 item.x(paramRect->pos().x());
419 item.y(paramRect->pos().y());
420 item.w(paramRect->rect().width());
421 item.h(paramRect->rect().height());
422 m_geom->insert(item);
423 updateTransitionPath();
424 emit parameterChanged();