]> git.sesse.net Git - kdenlive/blob - src/effectstackedit.cpp
7acae5dd662dc3e9f020a6b35b34b199c4dd4727
[kdenlive] / src / effectstackedit.cpp
1 /***************************************************************************
2                           effecstackedit.cpp  -  description
3                              -------------------
4     begin                : Feb 15 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 "effectstackedit.h"
19 #include "ui_listval_ui.h"
20 #include "ui_boolval_ui.h"
21 #include "ui_wipeval_ui.h"
22 #include "ui_urlval_ui.h"
23 #include "complexparameter.h"
24 #include "geometryval.h"
25 #include "positionedit.h"
26 #include "projectlist.h"
27 #include "effectslist.h"
28 #include "kdenlivesettings.h"
29 #include "profilesdialog.h"
30 #include "kis_curve_widget.h"
31 #include "kis_cubic_curve.h"
32 #include "choosecolorwidget.h"
33 #include "geometrywidget.h"
34 #include "colortools.h"
35 #include "doubleparameterwidget.h"
36 #include "cornerswidget.h"
37 #include "beziercurve/beziersplinewidget.h"
38 #ifdef QJSON
39 #include "rotoscoping/rotowidget.h"
40 #endif
41
42 #include <KDebug>
43 #include <KLocale>
44 #include <KFileDialog>
45
46 #include <QVBoxLayout>
47 #include <QLabel>
48 #include <QPushButton>
49 #include <QCheckBox>
50 #include <QScrollArea>
51
52 // For QDomNode debugging (output into files); leaving here as sample code.
53 //#define DEBUG_ESE
54
55
56 class Boolval: public QWidget, public Ui::Boolval_UI
57 {
58 };
59
60 class Listval: public QWidget, public Ui::Listval_UI
61 {
62 };
63
64 class Wipeval: public QWidget, public Ui::Wipeval_UI
65 {
66 };
67
68 class Urlval: public QWidget, public Ui::Urlval_UI
69 {
70 };
71
72 QMap<QString, QImage> EffectStackEdit::iconCache;
73
74 EffectStackEdit::EffectStackEdit(Monitor *monitor, QWidget *parent) :
75     QScrollArea(parent),
76     m_in(0),
77     m_out(0),
78     m_frameSize(QPoint()),
79     m_keyframeEditor(NULL),
80     m_monitor(monitor)
81 {
82     m_baseWidget = new QWidget(this);
83     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
84     setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
85     setFrameStyle(QFrame::NoFrame);
86     setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding));
87
88     setWidget(m_baseWidget);
89     setWidgetResizable(true);
90     m_vbox = new QVBoxLayout(m_baseWidget);
91     m_vbox->setContentsMargins(0, 0, 0, 0);
92     m_vbox->setSpacing(0);
93     //wid->show();
94 }
95
96 EffectStackEdit::~EffectStackEdit()
97 {
98     iconCache.clear();
99     delete m_baseWidget;
100 }
101
102 void EffectStackEdit::setFrameSize(QPoint p)
103 {
104     m_frameSize = p;
105     QDomNodeList namenode = m_params.elementsByTagName("parameter");
106     for (int i = 0; i < namenode.count() ; i++) {
107         QDomNode pa = namenode.item(i);
108         QDomNode na = pa.firstChildElement("name");
109         QString type = pa.attributes().namedItem("type").nodeValue();
110         QString paramName = i18n(na.toElement().text().toUtf8().data());
111
112         if (type == "geometry" && !KdenliveSettings::on_monitor_effects()) {
113             Geometryval *geom = ((Geometryval*)m_valueItems[paramName+"geometry"]);
114             geom->setFrameSize(m_frameSize);
115             break;
116         }
117     }
118 }
119
120 void EffectStackEdit::updateTimecodeFormat()
121 {
122     if (m_keyframeEditor)
123         m_keyframeEditor->updateTimecodeFormat();
124
125     QDomNodeList namenode = m_params.elementsByTagName("parameter");
126     for (int i = 0; i < namenode.count() ; i++) {
127         QDomNode pa = namenode.item(i);
128         QDomNode na = pa.firstChildElement("name");
129         QString type = pa.attributes().namedItem("type").nodeValue();
130         QString paramName = i18n(na.toElement().text().toUtf8().data());
131
132         if (type == "geometry") {
133             if (KdenliveSettings::on_monitor_effects()) {
134                 GeometryWidget *geom = (GeometryWidget*)m_valueItems[paramName+"geometry"];
135                 geom->updateTimecodeFormat();
136             } else {
137                 Geometryval *geom = ((Geometryval*)m_valueItems[paramName+"geometry"]);
138                 geom->updateTimecodeFormat();
139             }
140             break;
141         }
142         if (type == "position") {
143             PositionEdit *posi = ((PositionEdit*)m_valueItems[paramName+"position"]);
144             posi->updateTimecodeFormat();
145             break;
146         }
147     }
148 }
149
150 void EffectStackEdit::meetDependency(const QString& name, QString type, QString value)
151 {
152     if (type == "curve") {
153         KisCurveWidget *curve = (KisCurveWidget*)m_valueItems[name];
154         if (curve) {
155             int color = value.toInt();
156             curve->setPixmap(QPixmap::fromImage(ColorTools::rgbCurvePlane(curve->size(), (ColorTools::ColorsRGB)(color == 3 ? 4 : color), 0.8)));
157         }
158     } else if (type == "bezier_spline") {
159         BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems[name];
160         if (widget) {
161             widget->setMode((BezierSplineWidget::CurveModes)((int)(value.toDouble() * 10)));
162         }
163     }
164 }
165
166 void EffectStackEdit::updateProjectFormat(MltVideoProfile profile, Timecode t)
167 {
168     m_profile = profile;
169     m_timecode = t;
170 }
171
172 void EffectStackEdit::updateParameter(const QString &name, const QString &value)
173 {
174     m_params.setAttribute(name, value);
175
176     if (name == "disable") {
177         // if effect is disabled, disable parameters widget
178         bool enabled = value.toInt() == 0 || !KdenliveSettings::disable_effect_parameters();
179         setEnabled(enabled);
180         emit effectStateChanged(enabled);
181     }
182 }
183
184 void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, int out, bool isEffect)
185 {
186     clearAllItems();
187     if (m_keyframeEditor) delete m_keyframeEditor;
188     m_keyframeEditor = NULL;
189     m_params = d;
190     m_in = in;
191     m_out = out;
192     if (m_params.isNull()) {
193         kDebug() << "// EMPTY EFFECT STACK";
194         return;
195     }
196
197     QDomNodeList namenode = m_params.elementsByTagName("parameter");
198 #ifdef DEBUG_ESE
199     QFile debugFile("/tmp/namenodes.txt");
200     if (debugFile.open(QFile::WriteOnly | QFile::Truncate)) {
201         QTextStream out(&debugFile);
202         QTextStream out2(stdout);
203         for (int i = 0; i < namenode.size(); i++) {
204             out << i << ": \n";
205             namenode.at(i).save(out, 2);
206             out2 << i << ": \n";
207             namenode.at(i).save(out2, 2);
208         }
209     }
210 #endif
211     QDomElement e = m_params.toElement();
212     const int minFrame = e.attribute("start").toInt();
213     const int maxFrame = e.attribute("end").toInt();
214
215     bool disable = d.attribute("disable") == "1" && KdenliveSettings::disable_effect_parameters();
216     setEnabled(!disable);
217
218     bool stretch = true;
219
220
221     for (int i = 0; i < namenode.count() ; i++) {
222         QDomElement pa = namenode.item(i).toElement();
223         QDomElement na = pa.firstChildElement("name");
224         QDomElement commentElem = pa.firstChildElement("comment");
225         QString type = pa.attribute("type");
226         QString paramName = i18n(na.text().toUtf8().data());
227         QString comment;
228         if (!commentElem.isNull())
229             comment = i18n(commentElem.text().toUtf8().data());
230         QWidget * toFillin = new QWidget(m_baseWidget);
231         QString value = pa.attribute("value").isNull() ?
232                         pa.attribute("default") : pa.attribute("value");
233
234         /** Currently supported parameter types are:
235             * constant (=double): a slider with an integer value (use the "factor" attribute to divide the value so that you can get a double
236             * list: a combobox containing a list of values to choose
237             * bool: a checkbox
238             * complex: designed for keyframe parameters, but old and not finished, do not use
239             * geometry: a rectangle that can be moved & resized, with possible keyframes, used in composite transition
240             * keyframe: a list widget with a list of entries (position and value)
241             * color: a color chooser button
242             * position: a slider representing the position of a frame in the current clip
243             * curve: a single curve representing multiple points
244             * wipe: a widget designed for the wipe transition, allowing to choose a position (left, right, top,...)
245         */
246
247         if (type == "double" || type == "constant") {
248             int min;
249             int max;
250             if (pa.attribute("min").startsWith('%'))
251                 min = (int) ProfilesDialog::getStringEval(m_profile, pa.attribute("min"));
252             else
253                 min = pa.attribute("min").toInt();
254             if (pa.attribute("max").startsWith('%'))
255                 max = (int) ProfilesDialog::getStringEval(m_profile, pa.attribute("max"));
256             else
257                 max = pa.attribute("max").toInt();
258
259             DoubleParameterWidget *doubleparam = new DoubleParameterWidget(paramName, (int)(value.toDouble() + 0.5), min, max,
260                     pa.attribute("default").toInt(), comment, pa.attribute("suffix"), this);
261             m_vbox->addWidget(doubleparam);
262             m_valueItems[paramName] = doubleparam;
263             connect(doubleparam, SIGNAL(valueChanged(int)), this, SLOT(collectAllParameters()));
264             connect(this, SIGNAL(showComments(bool)), doubleparam, SLOT(slotShowComment(bool)));
265         } else if (type == "list") {
266             Listval *lsval = new Listval;
267             lsval->setupUi(toFillin);
268             QStringList listitems = pa.attribute("paramlist").split(',');
269             QDomElement list = pa.firstChildElement("paramlistdisplay");
270             QStringList listitemsdisplay;
271             if (!list.isNull()) listitemsdisplay = i18n(list.text().toUtf8().data()).split(',');
272             else listitemsdisplay = i18n(pa.attribute("paramlistdisplay").toUtf8().data()).split(',');
273             if (listitemsdisplay.count() != listitems.count())
274                 listitemsdisplay = listitems;
275             lsval->list->setIconSize(QSize(30, 30));
276             for (int i = 0; i < listitems.count(); i++) {
277                 lsval->list->addItem(listitemsdisplay.at(i), listitems.at(i));
278                 QString entry = listitems.at(i);
279                 if (!entry.isEmpty() && (entry.endsWith(".png") || entry.endsWith(".pgm"))) {
280                     if (!EffectStackEdit::iconCache.contains(entry)) {
281                         QImage pix(entry);
282                         EffectStackEdit::iconCache[entry] = pix.scaled(30, 30);
283                     }
284                     lsval->list->setItemIcon(i, QPixmap::fromImage(iconCache[entry]));
285                 }
286             }
287             if (!value.isEmpty()) lsval->list->setCurrentIndex(listitems.indexOf(value));
288             lsval->name->setText(paramName);
289             lsval->labelComment->setText(comment);
290             lsval->widgetComment->setHidden(true);
291             m_valueItems[paramName] = lsval;
292             connect(lsval->list, SIGNAL(currentIndexChanged(int)) , this, SLOT(collectAllParameters()));
293             if (!comment.isEmpty())
294                 connect(this, SIGNAL(showComments(bool)), lsval->widgetComment, SLOT(setVisible(bool)));
295             m_uiItems.append(lsval);
296         } else if (type == "bool") {
297             Boolval *bval = new Boolval;
298             bval->setupUi(toFillin);
299             bval->checkBox->setCheckState(value == "0" ? Qt::Unchecked : Qt::Checked);
300             bval->name->setText(paramName);
301             bval->labelComment->setText(comment);
302             bval->widgetComment->setHidden(true);
303             m_valueItems[paramName] = bval;
304             connect(bval->checkBox, SIGNAL(stateChanged(int)) , this, SLOT(collectAllParameters()));
305             if (!comment.isEmpty())
306                 connect(this, SIGNAL(showComments(bool)), bval->widgetComment, SLOT(setVisible(bool)));
307             m_uiItems.append(bval);
308         } else if (type == "complex") {
309             ComplexParameter *pl = new ComplexParameter;
310             pl->setupParam(d, pa.attribute("name"), 0, 100);
311             m_vbox->addWidget(pl);
312             m_valueItems[paramName+"complex"] = pl;
313             connect(pl, SIGNAL(parameterChanged()), this, SLOT(collectAllParameters()));
314         } else if (type == "geometry") {
315             if (KdenliveSettings::on_monitor_effects()) {
316                 GeometryWidget *geometry = new GeometryWidget(m_monitor, m_timecode, pos, isEffect, this);
317                 geometry->slotShowScene(!disable);
318                 // connect this before setupParam to make sure the monitor scene shows up at startup
319                 connect(geometry, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
320                 connect(geometry, SIGNAL(parameterChanged()), this, SLOT(collectAllParameters()));
321                 if (minFrame == maxFrame)
322                     geometry->setupParam(pa, m_in, m_out);
323                 else
324                     geometry->setupParam(pa, minFrame, maxFrame);
325                 m_vbox->addWidget(geometry);
326                 m_valueItems[paramName+"geometry"] = geometry;
327                 connect(geometry, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
328                 connect(this, SIGNAL(syncEffectsPos(int)), geometry, SLOT(slotSyncPosition(int)));
329                 connect(this, SIGNAL(effectStateChanged(bool)), geometry, SLOT(slotShowScene(bool)));
330             } else {
331                 Geometryval *geo = new Geometryval(m_profile, m_timecode, m_frameSize, pos);
332                 if (minFrame == maxFrame)
333                     geo->setupParam(pa, m_in, m_out);
334                 else
335                     geo->setupParam(pa, minFrame, maxFrame);
336                 m_vbox->addWidget(geo);
337                 m_valueItems[paramName+"geometry"] = geo;
338                 connect(geo, SIGNAL(parameterChanged()), this, SLOT(collectAllParameters()));
339                 connect(geo, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
340                 connect(this, SIGNAL(syncEffectsPos(int)), geo, SLOT(slotSyncPosition(int)));
341             }
342         } else if (type == "keyframe" || type == "simplekeyframe") {
343             // keyframe editor widget
344             if (m_keyframeEditor == NULL) {
345                 KeyframeEdit *geo;
346                 if (pa.attribute("widget") == "corners") {
347                     // we want a corners-keyframe-widget
348                     CornersWidget *corners = new CornersWidget(m_monitor, pa, m_in, m_in + m_out, m_timecode, e.attribute("active_keyframe", "-1").toInt(), this);
349                     corners->slotShowScene(!disable);
350                     connect(corners, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
351                     connect(this, SIGNAL(effectStateChanged(bool)), corners, SLOT(slotShowScene(bool)));
352                     connect(this, SIGNAL(syncEffectsPos(int)), corners, SLOT(slotSyncPosition(int)));
353                     geo = static_cast<KeyframeEdit *>(corners);
354                 } else {
355                     geo = new KeyframeEdit(pa, m_in, m_in + m_out, m_timecode, e.attribute("active_keyframe", "-1").toInt());
356                 }
357                 m_vbox->addWidget(geo);
358                 m_valueItems[paramName+"keyframe"] = geo;
359                 m_keyframeEditor = geo;
360                 connect(geo, SIGNAL(parameterChanged()), this, SLOT(collectAllParameters()));
361                 connect(geo, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
362                 connect(this, SIGNAL(showComments(bool)), geo, SIGNAL(showComments(bool)));
363             } else {
364                 // we already have a keyframe editor, so just add another column for the new param
365                 m_keyframeEditor->addParameter(pa);
366             }
367         } else if (type == "color") {
368             if (value.startsWith('#'))
369                 value = value.replace('#', "0x");
370             bool ok;
371             ChooseColorWidget *choosecolor = new ChooseColorWidget(paramName, QColor(value.toUInt(&ok, 16)), this);
372             m_vbox->addWidget(choosecolor);
373             m_valueItems[paramName] = choosecolor;
374             connect(choosecolor, SIGNAL(displayMessage(const QString&, int)), this, SIGNAL(displayMessage(const QString&, int)));
375             connect(choosecolor, SIGNAL(modified()) , this, SLOT(collectAllParameters()));
376         } else if (type == "position") {
377             int pos = value.toInt();
378             if (d.attribute("id") == "fadein" || d.attribute("id") == "fade_from_black") {
379                 pos = pos - m_in;
380             } else if (d.attribute("id") == "fadeout" || d.attribute("id") == "fade_to_black") {
381                 // fadeout position starts from clip end
382                 pos = m_out - pos;
383             }
384             PositionEdit *posedit = new PositionEdit(paramName, pos, 0, m_out - m_in, m_timecode);
385             m_vbox->addWidget(posedit);
386             m_valueItems[paramName+"position"] = posedit;
387             connect(posedit, SIGNAL(parameterChanged()), this, SLOT(collectAllParameters()));
388         } else if (type == "curve") {
389             KisCurveWidget *curve = new KisCurveWidget(this);
390             curve->setMaxPoints(pa.attribute("max").toInt());
391             QList<QPointF> points;
392             int number = EffectsList::parameter(e, pa.attribute("number")).toInt();
393             QString inName = pa.attribute("inpoints");
394             QString outName = pa.attribute("outpoints");
395             int start = pa.attribute("min").toInt();
396             for (int j = start; j <= number; j++) {
397                 QString in = inName;
398                 in.replace("%i", QString::number(j));
399                 QString out = outName;
400                 out.replace("%i", QString::number(j));
401                 points << QPointF(EffectsList::parameter(e, in).toDouble(), EffectsList::parameter(e, out).toDouble());
402             }
403             if (!points.isEmpty())
404                 curve->setCurve(KisCubicCurve(points));
405             QSpinBox *spinin = new QSpinBox();
406             spinin->setRange(0, 1000);
407             QSpinBox *spinout = new QSpinBox();
408             spinout->setRange(0, 1000);
409             curve->setupInOutControls(spinin, spinout, 0, 1000);
410             m_vbox->addWidget(curve);
411             m_vbox->addWidget(spinin);
412             m_vbox->addWidget(spinout);
413
414             connect(curve, SIGNAL(modified()), this, SLOT(collectAllParameters()));
415             m_valueItems[paramName] = curve;
416
417             QString depends = pa.attribute("depends");
418             if (!depends.isEmpty())
419                 meetDependency(paramName, type, EffectsList::parameter(e, depends));
420         } else if (type == "bezier_spline") {
421             BezierSplineWidget *widget = new BezierSplineWidget(value, this);
422             stretch = false;
423             m_vbox->addWidget(widget);
424             m_valueItems[paramName] = widget;
425             connect(widget, SIGNAL(modified()), this, SLOT(collectAllParameters()));
426             QString depends = pa.attribute("depends");
427             if (!depends.isEmpty())
428                 meetDependency(paramName, type, EffectsList::parameter(e, depends));
429 #ifdef QJSON
430         } else if (type == "roto-spline") {
431             RotoWidget *roto = new RotoWidget(value, m_monitor, m_in, m_out, this);
432             roto->slotShowScene(!disable);
433             connect(roto, SIGNAL(valueChanged()), this, SLOT(collectAllParameters()));
434             connect(roto, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
435             connect(this, SIGNAL(syncEffectsPos(int)), roto, SLOT(slotSyncPosition(int)));
436             connect(this, SIGNAL(effectStateChanged(bool)), roto, SLOT(slotShowScene(bool)));
437             m_vbox->addWidget(roto);
438             m_valueItems[paramName] = roto;
439 #endif
440         } else if (type == "wipe") {
441             Wipeval *wpval = new Wipeval;
442             wpval->setupUi(toFillin);
443             wipeInfo w = getWipeInfo(value);
444             switch (w.start) {
445             case UP:
446                 wpval->start_up->setChecked(true);
447                 break;
448             case DOWN:
449                 wpval->start_down->setChecked(true);
450                 break;
451             case RIGHT:
452                 wpval->start_right->setChecked(true);
453                 break;
454             case LEFT:
455                 wpval->start_left->setChecked(true);
456                 break;
457             default:
458                 wpval->start_center->setChecked(true);
459                 break;
460             }
461             switch (w.end) {
462             case UP:
463                 wpval->end_up->setChecked(true);
464                 break;
465             case DOWN:
466                 wpval->end_down->setChecked(true);
467                 break;
468             case RIGHT:
469                 wpval->end_right->setChecked(true);
470                 break;
471             case LEFT:
472                 wpval->end_left->setChecked(true);
473                 break;
474             default:
475                 wpval->end_center->setChecked(true);
476                 break;
477             }
478             wpval->start_transp->setValue(w.startTransparency);
479             wpval->end_transp->setValue(w.endTransparency);
480             m_valueItems[paramName] = wpval;
481             connect(wpval->end_up, SIGNAL(clicked()), this, SLOT(collectAllParameters()));
482             connect(wpval->end_down, SIGNAL(clicked()), this, SLOT(collectAllParameters()));
483             connect(wpval->end_left, SIGNAL(clicked()), this, SLOT(collectAllParameters()));
484             connect(wpval->end_right, SIGNAL(clicked()), this, SLOT(collectAllParameters()));
485             connect(wpval->end_center, SIGNAL(clicked()), this, SLOT(collectAllParameters()));
486             connect(wpval->start_up, SIGNAL(clicked()), this, SLOT(collectAllParameters()));
487             connect(wpval->start_down, SIGNAL(clicked()), this, SLOT(collectAllParameters()));
488             connect(wpval->start_left, SIGNAL(clicked()), this, SLOT(collectAllParameters()));
489             connect(wpval->start_right, SIGNAL(clicked()), this, SLOT(collectAllParameters()));
490             connect(wpval->start_center, SIGNAL(clicked()), this, SLOT(collectAllParameters()));
491             connect(wpval->start_transp, SIGNAL(valueChanged(int)), this, SLOT(collectAllParameters()));
492             connect(wpval->end_transp, SIGNAL(valueChanged(int)), this, SLOT(collectAllParameters()));
493             //wpval->title->setTitle(na.toElement().text());
494             m_uiItems.append(wpval);
495         } else if (type == "url") {
496             Urlval *cval = new Urlval;
497             cval->setupUi(toFillin);
498             cval->label->setText(paramName);
499             cval->urlwidget->fileDialog()->setFilter(ProjectList::getExtensions());
500             m_valueItems[paramName] = cval;
501             cval->urlwidget->setUrl(KUrl(value));
502             connect(cval->urlwidget, SIGNAL(returnPressed()) , this, SLOT(collectAllParameters()));
503             connect(cval->urlwidget, SIGNAL(urlSelected(const KUrl&)) , this, SLOT(collectAllParameters()));
504             m_uiItems.append(cval);
505         } else {
506             delete toFillin;
507             toFillin = NULL;
508         }
509
510         if (toFillin)
511             m_vbox->addWidget(toFillin);
512     }
513
514     if (stretch)
515         m_vbox->addStretch();
516
517     if (m_keyframeEditor)
518         m_keyframeEditor->checkVisibleParam();
519 }
520
521 wipeInfo EffectStackEdit::getWipeInfo(QString value)
522 {
523     wipeInfo info;
524     QString start = value.section(';', 0, 0);
525     QString end = value.section(';', 1, 1).section('=', 1, 1);
526
527     if (start.startsWith("-100%,0"))
528         info.start = LEFT;
529     else if (start.startsWith("100%,0"))
530         info.start = RIGHT;
531     else if (start.startsWith("0%,100%"))
532         info.start = DOWN;
533     else if (start.startsWith("0%,-100%"))
534         info.start = UP;
535     else
536         info.start = CENTER;
537
538     if (start.count(':') == 2)
539         info.startTransparency = start.section(':', -1).toInt();
540     else
541         info.startTransparency = 100;
542
543     if (end.startsWith("-100%,0"))
544         info.end = LEFT;
545     else if (end.startsWith("100%,0"))
546         info.end = RIGHT;
547     else if (end.startsWith("0%,100%"))
548         info.end = DOWN;
549     else if (end.startsWith("0%,-100%"))
550         info.end = UP;
551     else
552         info.end = CENTER;
553
554     if (end.count(':') == 2)
555         info.endTransparency = end.section(':', -1).toInt();
556     else
557         info.endTransparency = 100;
558
559     return info;
560 }
561
562 QString EffectStackEdit::getWipeString(wipeInfo info)
563 {
564
565     QString start;
566     QString end;
567     switch (info.start) {
568     case LEFT:
569         start = "-100%,0%:100%x100%";
570         break;
571     case RIGHT:
572         start = "100%,0%:100%x100%";
573         break;
574     case DOWN:
575         start = "0%,100%:100%x100%";
576         break;
577     case UP:
578         start = "0%,-100%:100%x100%";
579         break;
580     default:
581         start = "0%,0%:100%x100%";
582         break;
583     }
584     start.append(':' + QString::number(info.startTransparency));
585
586     switch (info.end) {
587     case LEFT:
588         end = "-100%,0%:100%x100%";
589         break;
590     case RIGHT:
591         end = "100%,0%:100%x100%";
592         break;
593     case DOWN:
594         end = "0%,100%:100%x100%";
595         break;
596     case UP:
597         end = "0%,-100%:100%x100%";
598         break;
599     default:
600         end = "0%,0%:100%x100%";
601         break;
602     }
603     end.append(':' + QString::number(info.endTransparency));
604     return QString(start + ";-1=" + end);
605 }
606
607 void EffectStackEdit::collectAllParameters()
608 {
609     if (m_valueItems.isEmpty() || m_params.isNull()) return;
610     const QDomElement oldparam = m_params.cloneNode().toElement();
611     QDomElement newparam = oldparam.cloneNode().toElement();
612     QDomNodeList namenode = newparam.elementsByTagName("parameter");
613
614     for (int i = 0; i < namenode.count() ; i++) {
615         QDomNode pa = namenode.item(i);
616         QDomNode na = pa.firstChildElement("name");
617         QString type = pa.attributes().namedItem("type").nodeValue();
618         QString paramName = i18n(na.toElement().text().toUtf8().data());
619         if (type == "complex")
620             paramName.append("complex");
621         else if (type == "position")
622             paramName.append("position");
623         else if (type == "geometry")
624             paramName.append("geometry");
625         else if (type == "keyframe")
626             paramName.append("keyframe");
627         if (type != "simplekeyframe" && !m_valueItems.contains(paramName)) {
628             kDebug() << "// Param: " << paramName << " NOT FOUND";
629             continue;
630         }
631
632         QString setValue;
633         if (type == "double" || type == "constant") {
634             DoubleParameterWidget *doubleparam = (DoubleParameterWidget*)m_valueItems.value(paramName);
635             setValue = QString::number(doubleparam->getValue());
636         } else if (type == "list") {
637             KComboBox *box = ((Listval*)m_valueItems.value(paramName))->list;
638             setValue = box->itemData(box->currentIndex()).toString();
639         } else if (type == "bool") {
640             QCheckBox *box = ((Boolval*)m_valueItems.value(paramName))->checkBox;
641             setValue = box->checkState() == Qt::Checked ? "1" : "0" ;
642         } else if (type == "color") {
643             ChooseColorWidget *choosecolor = ((ChooseColorWidget*)m_valueItems.value(paramName));
644             setValue = choosecolor->getColor().name();
645         } else if (type == "complex") {
646             ComplexParameter *complex = ((ComplexParameter*)m_valueItems.value(paramName));
647             namenode.item(i) = complex->getParamDesc();
648         } else if (type == "geometry") {
649             if (KdenliveSettings::on_monitor_effects()) {
650                 GeometryWidget *geometry = ((GeometryWidget*)m_valueItems.value(paramName));
651                 namenode.item(i).toElement().setAttribute("value", geometry->getValue());
652             } else {
653                 Geometryval *geom = ((Geometryval*)m_valueItems.value(paramName));
654                 namenode.item(i).toElement().setAttribute("value", geom->getValue());
655             }
656         } else if (type == "position") {
657             PositionEdit *pedit = ((PositionEdit*)m_valueItems.value(paramName));
658             int pos = pedit->getPosition();
659             setValue = QString::number(pos);
660             if (newparam.attribute("id") == "fadein" || newparam.attribute("id") == "fade_from_black") {
661                 // Make sure duration is not longer than clip
662                 /*if (pos > m_out) {
663                     pos = m_out;
664                     pedit->setPosition(pos);
665                 }*/
666                 EffectsList::setParameter(newparam, "in", QString::number(m_in));
667                 EffectsList::setParameter(newparam, "out", QString::number(m_in + pos));
668                 setValue.clear();
669             } else if (newparam.attribute("id") == "fadeout" || newparam.attribute("id") == "fade_to_black") {
670                 // Make sure duration is not longer than clip
671                 /*if (pos > m_out) {
672                     pos = m_out;
673                     pedit->setPosition(pos);
674                 }*/
675                 EffectsList::setParameter(newparam, "in", QString::number(m_out - pos));
676                 EffectsList::setParameter(newparam, "out", QString::number(m_out));
677                 setValue.clear();
678             }
679         } else if (type == "curve") {
680             KisCurveWidget *curve = ((KisCurveWidget*)m_valueItems.value(paramName));
681             QList<QPointF> points = curve->curve().points();
682             QString number = pa.attributes().namedItem("number").nodeValue();
683             QString inName = pa.attributes().namedItem("inpoints").nodeValue();
684             QString outName = pa.attributes().namedItem("outpoints").nodeValue();
685             int off = pa.attributes().namedItem("min").nodeValue().toInt();
686             int end = pa.attributes().namedItem("max").nodeValue().toInt();
687             EffectsList::setParameter(newparam, number, QString::number(points.count()));
688             for (int j = 0; (j < points.count() && j + off <= end); j++) {
689                 QString in = inName;
690                 in.replace("%i", QString::number(j + off));
691                 QString out = outName;
692                 out.replace("%i", QString::number(j + off));
693                 EffectsList::setParameter(newparam, in, QString::number(points.at(j).x()));
694                 EffectsList::setParameter(newparam, out, QString::number(points.at(j).y()));
695             }
696             QString depends = pa.attributes().namedItem("depends").nodeValue();
697             if (!depends.isEmpty())
698                 meetDependency(paramName, type, EffectsList::parameter(newparam, depends));
699         } else if (type == "bezier_spline") {
700             BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems.value(paramName);
701             setValue = widget->spline();
702             QString depends = pa.attributes().namedItem("depends").nodeValue();
703             if (!depends.isEmpty())
704                 meetDependency(paramName, type, EffectsList::parameter(newparam, depends));
705 #ifdef QJSON
706         } else if (type == "roto-spline") {
707             RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems.value(paramName));
708             setValue = widget->getSpline();
709 #endif
710         } else if (type == "wipe") {
711             Wipeval *wp = (Wipeval*)m_valueItems.value(paramName);
712             wipeInfo info;
713             if (wp->start_left->isChecked())
714                 info.start = LEFT;
715             else if (wp->start_right->isChecked())
716                 info.start = RIGHT;
717             else if (wp->start_up->isChecked())
718                 info.start = UP;
719             else if (wp->start_down->isChecked())
720                 info.start = DOWN;
721             else if (wp->start_center->isChecked())
722                 info.start = CENTER;
723             else
724                 info.start = LEFT;
725             info.startTransparency = wp->start_transp->value();
726
727             if (wp->end_left->isChecked())
728                 info.end = LEFT;
729             else if (wp->end_right->isChecked())
730                 info.end = RIGHT;
731             else if (wp->end_up->isChecked())
732                 info.end = UP;
733             else if (wp->end_down->isChecked())
734                 info.end = DOWN;
735             else if (wp->end_center->isChecked())
736                 info.end = CENTER;
737             else
738                 info.end = RIGHT;
739             info.endTransparency = wp->end_transp->value();
740
741             setValue = getWipeString(info);
742         } else if ((type == "simplekeyframe" || type == "keyframe") && m_keyframeEditor) {
743             QDomElement elem = pa.toElement();
744             QString realName = i18n(na.toElement().text().toUtf8().data());
745             QString val = m_keyframeEditor->getValue(realName);
746             elem.setAttribute("keyframes", val);
747
748             if (m_keyframeEditor->isVisibleParam(realName))
749                 elem.setAttribute("intimeline", "1");
750             else if (elem.hasAttribute("intimeline"))
751                 elem.removeAttribute("intimeline");
752         } else if (type == "url") {
753             KUrlRequester *req = ((Urlval*)m_valueItems.value(paramName))->urlwidget;
754             setValue = req->url().path();
755         }
756
757         if (!setValue.isNull())
758             pa.attributes().namedItem("value").setNodeValue(setValue);
759
760     }
761     emit parameterChanged(oldparam, newparam);
762 }
763
764 void EffectStackEdit::clearAllItems()
765 {
766     blockSignals(true);
767     m_valueItems.clear();
768     m_uiItems.clear();
769     /*while (!m_items.isEmpty()) {
770         QWidget *die = m_items.takeFirst();
771         die->disconnect();
772         delete die;
773     }*/
774     //qDeleteAll(m_uiItems);
775     QLayoutItem *child;
776     while ((child = m_vbox->takeAt(0)) != 0) {
777         QWidget *wid = child->widget();
778         delete child;
779         if (wid) delete wid;
780     }
781     m_keyframeEditor = NULL;
782     blockSignals(false);
783 }
784
785 void EffectStackEdit::slotSyncEffectsPos(int pos)
786 {
787     emit syncEffectsPos(pos);
788 }