]> git.sesse.net Git - kdenlive/blob - src/effectstack/collapsibleeffect.cpp
Fix crashed in group effects
[kdenlive] / src / effectstack / collapsibleeffect.cpp
1 /***************************************************************************
2  *   Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20
21 #include "collapsibleeffect.h"
22
23 #include "ui_listval_ui.h"
24 #include "ui_boolval_ui.h"
25 #include "ui_wipeval_ui.h"
26 #include "ui_urlval_ui.h"
27 #include "ui_keywordval_ui.h"
28 #include "ui_fontval_ui.h"
29 #include "complexparameter.h"
30 #include "geometryval.h"
31 #include "positionedit.h"
32 #include "projectlist.h"
33 #include "effectslist.h"
34 #include "kdenlivesettings.h"
35 #include "profilesdialog.h"
36 #include "kis_curve_widget.h"
37 #include "kis_cubic_curve.h"
38 #include "choosecolorwidget.h"
39 #include "geometrywidget.h"
40 #include "colortools.h"
41 #include "doubleparameterwidget.h"
42 #include "cornerswidget.h"
43 #include "dragvalue.h"
44 #include "beziercurve/beziersplinewidget.h"
45 #ifdef USE_QJSON
46 #include "rotoscoping/rotowidget.h"
47 #endif
48
49 #include <QInputDialog>
50 #include <QDialog>
51 #include <QMenu>
52 #include <QVBoxLayout>
53 #include <KDebug>
54 #include <KGlobalSettings>
55 #include <KLocale>
56 #include <KMessageBox>
57 #include <KStandardDirs>
58 #include <KFileDialog>
59 #include <KUrlRequester>
60
61 class Boolval: public QWidget, public Ui::Boolval_UI
62 {
63 };
64
65 class Listval: public QWidget, public Ui::Listval_UI
66 {
67 };
68
69 class Wipeval: public QWidget, public Ui::Wipeval_UI
70 {
71 };
72
73 class Urlval: public QWidget, public Ui::Urlval_UI
74 {
75 };
76
77 class Keywordval: public QWidget, public Ui::Keywordval_UI
78 {
79 };
80
81 class Fontval: public QWidget, public Ui::Fontval_UI
82 {
83 };
84
85 QMap<QString, QImage> CollapsibleEffect::iconCache;
86
87 void clearLayout(QLayout *layout)
88 {
89     QLayoutItem *item;
90     while((item = layout->takeAt(0))) {
91         if (item->layout()) {
92             clearLayout(item->layout());
93             delete item->layout();
94         }
95         if (item->widget()) {
96             delete item->widget();
97         }
98         delete item;
99     }
100 }
101
102 MySpinBox::MySpinBox(QWidget * parent):
103     QSpinBox(parent)
104 {
105     setFocusPolicy(Qt::StrongFocus);
106 }
107
108 void MySpinBox::focusInEvent(QFocusEvent*)
109 {
110      setFocusPolicy(Qt::WheelFocus);
111 }
112
113 void MySpinBox::focusOutEvent(QFocusEvent*)
114 {
115      setFocusPolicy(Qt::StrongFocus);
116 }
117
118
119 CollapsibleEffect::CollapsibleEffect(QDomElement effect, QDomElement original_effect, ItemInfo info, int ix, EffectMetaInfo *metaInfo, bool lastEffect, bool isGroup, QWidget * parent) :
120         QWidget(parent),
121         m_paramWidget(NULL),
122         m_effect(effect),
123         m_original_effect(original_effect),
124         m_lastEffect(lastEffect),
125         m_isGroup(isGroup),
126         m_active(false)
127 {
128     //setMouseTracking(true);
129     setupUi(this);
130     frame->setBackgroundRole(QPalette::Midlight);
131     frame->setAutoFillBackground(true);
132     setFont(KGlobalSettings::smallestReadableFont());
133    
134     buttonUp->setIcon(KIcon("go-up"));
135     buttonUp->setToolTip(i18n("Move effect up"));
136     if (!lastEffect) {
137         buttonDown->setIcon(KIcon("go-down"));
138         buttonDown->setToolTip(i18n("Move effect down"));
139     }
140     buttonDel->setIcon(KIcon("edit-delete"));
141     buttonDel->setToolTip(i18n("Delete effect"));
142
143     buttonUp->setVisible(false);
144     buttonDown->setVisible(false);
145     buttonDel->setVisible(false);
146     
147     /*buttonReset->setIcon(KIcon("view-refresh"));
148     buttonReset->setToolTip(i18n("Reset effect"));*/
149     //checkAll->setToolTip(i18n("Enable/Disable all effects"));
150     //buttonShowComments->setIcon(KIcon("help-about"));
151     //buttonShowComments->setToolTip(i18n("Show additional information for the parameters"));
152     m_menu = new QMenu;
153     m_menu->addAction(KIcon("view-refresh"), i18n("Reset effect"), this, SLOT(slotResetEffect()));
154     m_menu->addAction(KIcon("document-save"), i18n("Save effect"), this, SLOT(slotSaveEffect()));
155     
156     if (!m_isGroup) {
157         QDomElement namenode = m_effect.firstChildElement("name");
158         if (namenode.isNull()) return;
159         title->setText(i18n(namenode.text().toUtf8().data()));
160         QString type = m_effect.attribute("type", QString());
161         KIcon icon;
162         if (type == "audio") icon = KIcon("kdenlive-show-audio");
163         else if (m_effect.attribute("tag") == "region") icon = KIcon("kdenlive-mask-effect");
164         else if (type == "custom") icon = KIcon("kdenlive-custom-effect");
165         else icon = KIcon("kdenlive-show-video");
166         title->setIcon(icon);
167         m_menu->addAction(KIcon("folder-new"), i18n("Create Group"), this, SLOT(slotCreateGroup()));
168         setupWidget(info, ix, metaInfo);
169     }
170     else {
171         setAcceptDrops(true);
172         title->setText(i18n("Effect Group"));
173         title->setIcon(KIcon("folder"));
174     }
175     
176     title->setMenu(m_menu);
177     
178     if (m_effect.attribute("disable") == "1") {
179         enabledBox->setCheckState(Qt::Unchecked);
180         title->setEnabled(false);
181     }
182     else {
183         enabledBox->setCheckState(Qt::Checked);
184     }
185
186     connect(collapseButton, SIGNAL(clicked()), this, SLOT(slotSwitch()));
187     connect(enabledBox, SIGNAL(toggled(bool)), this, SLOT(slotEnable(bool)));
188     connect(buttonUp, SIGNAL(clicked()), this, SLOT(slotEffectUp()));
189     connect(buttonDown, SIGNAL(clicked()), this, SLOT(slotEffectDown()));
190     connect(buttonDel, SIGNAL(clicked()), this, SLOT(slotDeleteEffect()));
191
192     Q_FOREACH( QSpinBox * sp, findChildren<QSpinBox*>() ) {
193         sp->installEventFilter( this );
194         sp->setFocusPolicy( Qt::StrongFocus );
195     }
196     Q_FOREACH( KComboBox * cb, findChildren<KComboBox*>() ) {
197         cb->installEventFilter( this );
198         cb->setFocusPolicy( Qt::StrongFocus );
199     }
200     Q_FOREACH( QProgressBar * cb, findChildren<QProgressBar*>() ) {
201         cb->installEventFilter( this );
202         cb->setFocusPolicy( Qt::StrongFocus );
203     }
204 }
205
206 CollapsibleEffect::~CollapsibleEffect()
207 {
208     if (m_paramWidget) delete m_paramWidget;
209     delete m_menu;
210 }
211
212 void CollapsibleEffect::slotCreateGroup()
213 {
214     emit createGroup(m_paramWidget->index());
215 }
216
217 bool CollapsibleEffect::eventFilter( QObject * o, QEvent * e ) 
218 {
219     if (e->type() == QEvent::Wheel) {
220         QWheelEvent *we = static_cast<QWheelEvent *>(e);
221         if (we->modifiers() != Qt::NoModifier) {
222             e->accept();
223             return false;
224         }
225         if (qobject_cast<QAbstractSpinBox*>(o)) {
226             if(qobject_cast<QAbstractSpinBox*>(o)->focusPolicy() == Qt::WheelFocus)
227             {
228                 e->accept();
229                 return false;
230             }
231             else
232             {
233                 e->ignore();
234                 return true;
235             }
236         }
237         if (qobject_cast<KComboBox*>(o)) {
238             if(qobject_cast<KComboBox*>(o)->focusPolicy() == Qt::WheelFocus)
239             {
240                 e->accept();
241                 return false;
242             }
243             else
244             {
245                 e->ignore();
246                 return true;
247             }
248         }
249         if (qobject_cast<QProgressBar*>(o)) {
250             if(qobject_cast<QProgressBar*>(o)->focusPolicy() == Qt::WheelFocus)
251             {
252                 e->accept();
253                 return false;
254             }
255             else
256             {
257                 e->ignore();
258                 return true;
259             }
260         }
261     }
262     return QWidget::eventFilter(o, e);
263 }
264
265 QDomElement CollapsibleEffect::effect() const
266 {
267     return m_effect;
268 }
269
270 void CollapsibleEffect::setActive(bool activate)
271 {
272     m_active = activate;
273     frame->setBackgroundRole(m_active ? QPalette::Mid : QPalette::Midlight);
274     frame->setAutoFillBackground(activate);
275 }
276
277 void CollapsibleEffect::mouseDoubleClickEvent ( QMouseEvent * event )
278 {
279     if (frame->underMouse() && collapseButton->isEnabled()) slotSwitch();
280     QWidget::mouseDoubleClickEvent(event);
281 }
282
283 void CollapsibleEffect::mousePressEvent ( QMouseEvent *event )
284 {
285     if (!m_active && m_paramWidget) emit activateEffect(m_paramWidget->index());
286     QWidget::mousePressEvent(event);
287 }
288
289 void CollapsibleEffect::enterEvent ( QEvent * event )
290 {
291     if (m_paramWidget == NULL || m_paramWidget->index() > 0) buttonUp->setVisible(true);
292     if (!m_lastEffect) buttonDown->setVisible(true);
293     buttonDel->setVisible(true);
294     if (!m_active) frame->setBackgroundRole(QPalette::Midlight);
295     frame->setAutoFillBackground(true);
296     QWidget::enterEvent(event);
297 }
298
299 void CollapsibleEffect::leaveEvent ( QEvent * event )
300 {
301     buttonUp->setVisible(false);
302     buttonDown->setVisible(false);
303     buttonDel->setVisible(false);
304     if (!m_active) frame->setAutoFillBackground(false);
305     QWidget::leaveEvent(event);
306 }
307
308 void CollapsibleEffect::slotEnable(bool enable)
309 {
310     title->setEnabled(enable);
311     if (m_isGroup) {
312         QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
313         if (vbox == NULL) return;
314         for (int i = 0; i < vbox->count(); i++) {
315             CollapsibleEffect *e = static_cast<CollapsibleEffect *>(vbox->itemAt(i)->widget());
316             if (e) e->enabledBox->setChecked(enable);// slotEnable(enable);
317         }
318     } else {
319         m_effect.setAttribute("disable", enable ? 0 : 1);
320         if (enable || KdenliveSettings::disable_effect_parameters()) {
321             widgetFrame->setEnabled(enable);
322         }
323         emit effectStateChanged(!enable, m_paramWidget->index());
324     }
325 }
326
327 void CollapsibleEffect::slotDeleteEffect()
328 {
329     emit deleteEffect(m_effect, m_paramWidget->index());
330 }
331
332 void CollapsibleEffect::slotEffectUp()
333 {
334     emit changeEffectPosition(m_paramWidget->index(), true);
335 }
336
337 void CollapsibleEffect::slotEffectDown()
338 {
339     emit changeEffectPosition(m_paramWidget->index(), false);
340 }
341
342 void CollapsibleEffect::slotSaveEffect()
343 {
344     QString name = QInputDialog::getText(this, i18n("Save Effect"), i18n("Name for saved effect: "));
345     if (name.isEmpty()) return;
346     QString path = KStandardDirs::locateLocal("appdata", "effects/", true);
347     path = path + name + ".xml";
348     if (QFile::exists(path)) if (KMessageBox::questionYesNo(this, i18n("File %1 already exists.\nDo you want to overwrite it?", path)) == KMessageBox::No) return;
349
350     QDomDocument doc;
351     QDomElement effect = m_effect.cloneNode().toElement();
352     doc.appendChild(doc.importNode(effect, true));
353     effect = doc.firstChild().toElement();
354     effect.removeAttribute("kdenlive_ix");
355     effect.setAttribute("id", name);
356     effect.setAttribute("type", "custom");
357     QDomElement effectname = effect.firstChildElement("name");
358     effect.removeChild(effectname);
359     effectname = doc.createElement("name");
360     QDomText nametext = doc.createTextNode(name);
361     effectname.appendChild(nametext);
362     effect.insertBefore(effectname, QDomNode());
363     QDomElement effectprops = effect.firstChildElement("properties");
364     effectprops.setAttribute("id", name);
365     effectprops.setAttribute("type", "custom");
366
367     QFile file(path);
368     if (file.open(QFile::WriteOnly | QFile::Truncate)) {
369         QTextStream out(&file);
370         out << doc.toString();
371     }
372     file.close();
373     emit reloadEffects();
374 }
375
376 void CollapsibleEffect::slotResetEffect()
377 {
378     emit resetEffect(m_paramWidget->index());
379 }
380
381 void CollapsibleEffect::slotSwitch()
382 {
383     bool enable = !widgetFrame->isVisible();
384     slotShow(enable);
385 }
386
387 void CollapsibleEffect::slotShow(bool show)
388 {
389     widgetFrame->setVisible(show);
390     if (show) {
391         collapseButton->setArrowType(Qt::DownArrow);
392         m_original_effect.removeAttribute("k_collapsed");
393     }
394     else {
395         collapseButton->setArrowType(Qt::RightArrow);
396         m_original_effect.setAttribute("k_collapsed", 1);
397     }
398 }
399
400 void CollapsibleEffect::addGroupEffect(CollapsibleEffect *effect)
401 {
402     QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
403     if (vbox == NULL) {
404         vbox = new QVBoxLayout();
405         vbox->setContentsMargins(10, 0, 0, 0);
406         vbox->setSpacing(2);
407         widgetFrame->setLayout(vbox);
408     }
409     vbox->addWidget(effect);
410 }
411
412 int CollapsibleEffect::index() const
413 {
414     if (m_paramWidget) return m_paramWidget->index();
415     return 0;
416 }
417
418 int CollapsibleEffect::effectIndex() const
419 {
420     if (m_effect.isNull()) return -1;
421     return m_effect.attribute("kdenlive_ix").toInt();
422 }
423
424 void CollapsibleEffect::updateWidget(ItemInfo info, int index, QDomElement effect, EffectMetaInfo *metaInfo)
425 {
426     if (m_paramWidget) {
427         // cleanup
428         delete m_paramWidget;
429         m_paramWidget = NULL;
430     }
431     m_effect = effect;
432     setupWidget(info, index, metaInfo);
433 }
434
435 void CollapsibleEffect::setupWidget(ItemInfo info, int index, EffectMetaInfo *metaInfo)
436 {
437     if (m_effect.isNull()) {
438 //         kDebug() << "// EMPTY EFFECT STACK";
439         return;
440     }
441
442     if (m_effect.attribute("tag") == "region") {
443         QVBoxLayout *vbox = new QVBoxLayout(widgetFrame);
444         vbox->setContentsMargins(0, 0, 0, 0);
445         vbox->setSpacing(2);
446         QDomNodeList effects =  m_effect.elementsByTagName("effect");
447         QDomNodeList origin_effects =  m_original_effect.elementsByTagName("effect");
448         QWidget *container = new QWidget(widgetFrame);
449         vbox->addWidget(container);
450         m_paramWidget = new ParameterContainer(m_effect.toElement(), info, metaInfo, index, container);
451         for (int i = 0; i < effects.count(); i++) {
452             CollapsibleEffect *coll = new CollapsibleEffect(effects.at(i).toElement(), origin_effects.at(i).toElement(), info, i, metaInfo, container);
453             m_subParamWidgets.append(coll);
454             //container = new QWidget(widgetFrame);
455             vbox->addWidget(coll);
456             //p = new ParameterContainer(effects.at(i).toElement(), info, isEffect, container);
457         }
458         
459     }
460     else {
461         m_paramWidget = new ParameterContainer(m_effect, info, metaInfo, index, widgetFrame);
462         if (m_effect.firstChildElement("parameter").isNull()) {
463             // Effect has no parameter, don't allow expand
464             collapseButton->setEnabled(false);
465             widgetFrame->setVisible(false);            
466         }
467     }
468     if (collapseButton->isEnabled()) slotShow(!m_effect.hasAttribute("k_collapsed"));
469     connect (m_paramWidget, SIGNAL(parameterChanged(const QDomElement, const QDomElement, int)), this, SIGNAL(parameterChanged(const QDomElement, const QDomElement, int)));
470     
471     connect(m_paramWidget, SIGNAL(startFilterJob(QString,QString,QString,QString,QString,QString)), this, SIGNAL(startFilterJob(QString,QString,QString,QString,QString,QString)));
472     
473     connect (this, SIGNAL(syncEffectsPos(int)), m_paramWidget, SIGNAL(syncEffectsPos(int)));
474     connect (this, SIGNAL(effectStateChanged(bool)), m_paramWidget, SIGNAL(effectStateChanged(bool)));
475     connect (m_paramWidget, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
476     connect (m_paramWidget, SIGNAL(seekTimeline(int)), this, SIGNAL(seekTimeline(int)));
477     
478     
479 }
480
481 void CollapsibleEffect::updateTimecodeFormat()
482 {
483     m_paramWidget->updateTimecodeFormat();
484     if (!m_subParamWidgets.isEmpty()) {
485         // we have a group
486         for (int i = 0; i < m_subParamWidgets.count(); i++)
487             m_subParamWidgets.at(i)->updateTimecodeFormat();
488     }
489 }
490
491 void CollapsibleEffect::slotSyncEffectsPos(int pos)
492 {
493     emit syncEffectsPos(pos);
494 }
495
496 void CollapsibleEffect::dragEnterEvent(QDragEnterEvent *event)
497 {
498     if (event->mimeData()->hasFormat("kdenlive/effectslist"))
499         event->acceptProposedAction();
500 }
501
502 void CollapsibleEffect::dropEvent(QDropEvent *event)
503 {
504     const QString effects = QString::fromUtf8(event->mimeData()->data("kdenlive/effectslist"));
505     //event->acceptProposedAction();
506     QDomDocument doc;
507     doc.setContent(effects, true);
508     const QDomElement e = doc.documentElement();
509     int ix = e.attribute("kdenlive_ix").toInt();
510     emit moveEffect(ix, this);
511     event->setDropAction(Qt::MoveAction);
512     event->accept();
513 }
514
515 ParameterContainer::ParameterContainer(QDomElement effect, ItemInfo info, EffectMetaInfo *metaInfo, int index, QWidget * parent) :
516         m_index(index),
517         m_keyframeEditor(NULL),
518         m_geometryWidget(NULL),
519         m_metaInfo(metaInfo),
520         m_effect(effect)
521 {
522     m_in = info.cropStart.frames(KdenliveSettings::project_fps());
523     m_out = (info.cropStart + info.cropDuration).frames(KdenliveSettings::project_fps()) - 1;
524
525     QDomNodeList namenode = effect.childNodes(); //elementsByTagName("parameter");
526     
527     QDomElement e = effect.toElement();
528     int minFrame = e.attribute("start").toInt();
529     int maxFrame = e.attribute("end").toInt();
530     // In transitions, maxFrame is in fact one frame after the end of transition
531     if (maxFrame > 0) maxFrame --;
532
533     bool disable = effect.attribute("disable") == "1" && KdenliveSettings::disable_effect_parameters();
534     parent->setEnabled(!disable);
535
536     bool stretch = true;
537     m_vbox = new QVBoxLayout(parent);
538     m_vbox->setContentsMargins(0, 0, 0, 0);
539     m_vbox->setSpacing(2);
540
541     for (int i = 0; i < namenode.count() ; i++) {
542         QDomElement pa = namenode.item(i).toElement();
543         if (pa.tagName() != "parameter") continue;
544         QDomElement na = pa.firstChildElement("name");
545         QDomElement commentElem = pa.firstChildElement("comment");
546         QString type = pa.attribute("type");
547         QString paramName = na.isNull() ? pa.attribute("name") : i18n(na.text().toUtf8().data());
548         QString comment;
549         if (!commentElem.isNull())
550             comment = i18n(commentElem.text().toUtf8().data());
551         QWidget * toFillin = new QWidget(parent);
552         QString value = pa.attribute("value").isNull() ?
553                         pa.attribute("default") : pa.attribute("value");
554
555
556         /** See effects/README for info on the different types */
557
558         if (type == "double" || type == "constant") {
559             double min;
560             double max;
561             if (pa.attribute("min").contains('%'))
562                 min = ProfilesDialog::getStringEval(m_metaInfo->profile, pa.attribute("min"), m_metaInfo->frameSize);
563             else
564                 min = pa.attribute("min").toDouble();
565             if (pa.attribute("max").contains('%'))
566                 max = ProfilesDialog::getStringEval(m_metaInfo->profile, pa.attribute("max"), m_metaInfo->frameSize);
567             else
568                 max = pa.attribute("max").toDouble();
569
570             DoubleParameterWidget *doubleparam = new DoubleParameterWidget(paramName, value.toDouble(), min, max,
571                     pa.attribute("default").toDouble(), comment, -1, pa.attribute("suffix"), pa.attribute("decimals").toInt(), parent);
572             doubleparam->setFocusPolicy(Qt::StrongFocus);
573             m_vbox->addWidget(doubleparam);
574             m_valueItems[paramName] = doubleparam;
575             connect(doubleparam, SIGNAL(valueChanged(double)), this, SLOT(slotCollectAllParameters()));
576             connect(this, SIGNAL(showComments(bool)), doubleparam, SLOT(slotShowComment(bool)));
577         } else if (type == "list") {
578             Listval *lsval = new Listval;
579             lsval->setupUi(toFillin);
580             lsval->list->setFocusPolicy(Qt::StrongFocus);
581             QStringList listitems = pa.attribute("paramlist").split(';');
582             if (listitems.count() == 1) {
583                 // probably custom effect created before change to ';' as separator
584                 listitems = pa.attribute("paramlist").split(",");
585             }
586             QDomElement list = pa.firstChildElement("paramlistdisplay");
587             QStringList listitemsdisplay;
588             if (!list.isNull()) {
589                 listitemsdisplay = i18n(list.text().toUtf8().data()).split(',');
590             } else {
591                 listitemsdisplay = i18n(pa.attribute("paramlistdisplay").toUtf8().data()).split(',');
592             }
593             if (listitemsdisplay.count() != listitems.count())
594                 listitemsdisplay = listitems;
595             lsval->list->setIconSize(QSize(30, 30));
596             for (int i = 0; i < listitems.count(); i++) {
597                 lsval->list->addItem(listitemsdisplay.at(i), listitems.at(i));
598                 QString entry = listitems.at(i);
599                 if (!entry.isEmpty() && (entry.endsWith(".png") || entry.endsWith(".pgm"))) {
600                     if (!CollapsibleEffect::iconCache.contains(entry)) {
601                         QImage pix(entry);
602                         CollapsibleEffect::iconCache[entry] = pix.scaled(30, 30);
603                     }
604                     lsval->list->setItemIcon(i, QPixmap::fromImage(CollapsibleEffect::iconCache[entry]));
605                 }
606             }
607             if (!value.isEmpty()) lsval->list->setCurrentIndex(listitems.indexOf(value));
608             lsval->name->setText(paramName);
609             lsval->labelComment->setText(comment);
610             lsval->widgetComment->setHidden(true);
611             m_valueItems[paramName] = lsval;
612             connect(lsval->list, SIGNAL(currentIndexChanged(int)) , this, SLOT(slotCollectAllParameters()));
613             if (!comment.isEmpty())
614                 connect(this, SIGNAL(showComments(bool)), lsval->widgetComment, SLOT(setVisible(bool)));
615             m_uiItems.append(lsval);
616         } else if (type == "bool") {
617             Boolval *bval = new Boolval;
618             bval->setupUi(toFillin);
619             bval->checkBox->setCheckState(value == "0" ? Qt::Unchecked : Qt::Checked);
620             bval->name->setText(paramName);
621             bval->labelComment->setText(comment);
622             bval->widgetComment->setHidden(true);
623             m_valueItems[paramName] = bval;
624             connect(bval->checkBox, SIGNAL(stateChanged(int)) , this, SLOT(slotCollectAllParameters()));
625             if (!comment.isEmpty())
626                 connect(this, SIGNAL(showComments(bool)), bval->widgetComment, SLOT(setVisible(bool)));
627             m_uiItems.append(bval);
628         } else if (type == "complex") {
629             ComplexParameter *pl = new ComplexParameter;
630             pl->setupParam(effect, pa.attribute("name"), 0, 100);
631             m_vbox->addWidget(pl);
632             m_valueItems[paramName+"complex"] = pl;
633             connect(pl, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
634         } else if (type == "geometry") {
635             if (KdenliveSettings::on_monitor_effects()) {
636                 m_geometryWidget = new GeometryWidget(m_metaInfo->monitor, m_metaInfo->timecode, 0, true, effect.hasAttribute("showrotation"), parent);
637                 m_geometryWidget->setFrameSize(m_metaInfo->frameSize);
638                 m_geometryWidget->slotShowScene(!disable);
639                 // connect this before setupParam to make sure the monitor scene shows up at startup
640                 connect(m_geometryWidget, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
641                 connect(m_geometryWidget, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
642                 if (minFrame == maxFrame)
643                     m_geometryWidget->setupParam(pa, m_in, m_out);
644                 else
645                     m_geometryWidget->setupParam(pa, minFrame, maxFrame);
646                 m_vbox->addWidget(m_geometryWidget);
647                 m_valueItems[paramName+"geometry"] = m_geometryWidget;
648                 connect(m_geometryWidget, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
649                 connect(this, SIGNAL(syncEffectsPos(int)), m_geometryWidget, SLOT(slotSyncPosition(int)));
650                 connect(this, SIGNAL(effectStateChanged(bool)), m_geometryWidget, SLOT(slotShowScene(bool)));
651             } else {
652                 Geometryval *geo = new Geometryval(m_metaInfo->profile, m_metaInfo->timecode, m_metaInfo->frameSize, 0);
653                 if (minFrame == maxFrame)
654                     geo->setupParam(pa, m_in, m_out);
655                 else
656                     geo->setupParam(pa, minFrame, maxFrame);
657                 m_vbox->addWidget(geo);
658                 m_valueItems[paramName+"geometry"] = geo;
659                 connect(geo, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
660                 connect(geo, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
661                 connect(this, SIGNAL(syncEffectsPos(int)), geo, SLOT(slotSyncPosition(int)));
662             }
663         } else if (type == "addedgeometry") {
664             // this is a parameter that should be linked to the geometry widget, for example rotation, shear, ...
665             if (m_geometryWidget) m_geometryWidget->addParameter(pa);
666         } else if (type == "keyframe" || type == "simplekeyframe") {
667             // keyframe editor widget
668             if (m_keyframeEditor == NULL) {
669                 KeyframeEdit *geo;
670                 if (pa.attribute("widget") == "corners") {
671                     // we want a corners-keyframe-widget
672                     CornersWidget *corners = new CornersWidget(m_metaInfo->monitor, pa, m_in, m_out, m_metaInfo->timecode, e.attribute("active_keyframe", "-1").toInt(), parent);
673                     corners->slotShowScene(!disable);
674                     connect(corners, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
675                     connect(this, SIGNAL(effectStateChanged(bool)), corners, SLOT(slotShowScene(bool)));
676                     connect(this, SIGNAL(syncEffectsPos(int)), corners, SLOT(slotSyncPosition(int)));
677                     geo = static_cast<KeyframeEdit *>(corners);
678                 } else {
679                     geo = new KeyframeEdit(pa, m_in, m_out, m_metaInfo->timecode, e.attribute("active_keyframe", "-1").toInt());
680                 }
681                 m_vbox->addWidget(geo);
682                 m_valueItems[paramName+"keyframe"] = geo;
683                 m_keyframeEditor = geo;
684                 connect(geo, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
685                 connect(geo, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
686                 connect(this, SIGNAL(showComments(bool)), geo, SIGNAL(showComments(bool)));
687             } else {
688                 // we already have a keyframe editor, so just add another column for the new param
689                 m_keyframeEditor->addParameter(pa);
690             }
691         } else if (type == "color") {
692             if (value.startsWith('#'))
693                 value = value.replace('#', "0x");
694             ChooseColorWidget *choosecolor = new ChooseColorWidget(paramName, value, parent);
695             m_vbox->addWidget(choosecolor);
696             m_valueItems[paramName] = choosecolor;
697             connect(choosecolor, SIGNAL(displayMessage(const QString&, int)), this, SIGNAL(displayMessage(const QString&, int)));
698             connect(choosecolor, SIGNAL(modified()) , this, SLOT(slotCollectAllParameters()));
699         } else if (type == "position") {
700             int pos = value.toInt();
701             if (effect.attribute("id") == "fadein" || effect.attribute("id") == "fade_from_black") {
702                 pos = pos - m_in;
703             } else if (effect.attribute("id") == "fadeout" || effect.attribute("id") == "fade_to_black") {
704                 // fadeout position starts from clip end
705                 pos = m_out - pos;
706             }
707             PositionEdit *posedit = new PositionEdit(paramName, pos, 0, m_out - m_in, m_metaInfo->timecode);
708             m_vbox->addWidget(posedit);
709             m_valueItems[paramName+"position"] = posedit;
710             connect(posedit, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
711         } else if (type == "curve") {
712             KisCurveWidget *curve = new KisCurveWidget(parent);
713             curve->setMaxPoints(pa.attribute("max").toInt());
714             QList<QPointF> points;
715             int number = EffectsList::parameter(e, pa.attribute("number")).toInt();
716             QString inName = pa.attribute("inpoints");
717             QString outName = pa.attribute("outpoints");
718             int start = pa.attribute("min").toInt();
719             for (int j = start; j <= number; j++) {
720                 QString in = inName;
721                 in.replace("%i", QString::number(j));
722                 QString out = outName;
723                 out.replace("%i", QString::number(j));
724                 points << QPointF(EffectsList::parameter(e, in).toDouble(), EffectsList::parameter(e, out).toDouble());
725             }
726             if (!points.isEmpty())
727                 curve->setCurve(KisCubicCurve(points));
728             MySpinBox *spinin = new MySpinBox();
729             spinin->setRange(0, 1000);
730             MySpinBox *spinout = new MySpinBox();
731             spinout->setRange(0, 1000);
732             curve->setupInOutControls(spinin, spinout, 0, 1000);
733             m_vbox->addWidget(curve);
734             m_vbox->addWidget(spinin);
735             m_vbox->addWidget(spinout);
736
737             connect(curve, SIGNAL(modified()), this, SLOT(slotCollectAllParameters()));
738             m_valueItems[paramName] = curve;
739
740             QString depends = pa.attribute("depends");
741             if (!depends.isEmpty())
742                 meetDependency(paramName, type, EffectsList::parameter(e, depends));
743         } else if (type == "bezier_spline") {
744             BezierSplineWidget *widget = new BezierSplineWidget(value, parent);
745             stretch = false;
746             m_vbox->addWidget(widget);
747             m_valueItems[paramName] = widget;
748             connect(widget, SIGNAL(modified()), this, SLOT(slotCollectAllParameters()));
749             QString depends = pa.attribute("depends");
750             if (!depends.isEmpty())
751                 meetDependency(paramName, type, EffectsList::parameter(e, depends));
752 #ifdef USE_QJSON
753         } else if (type == "roto-spline") {
754             RotoWidget *roto = new RotoWidget(value, m_metaInfo->monitor, info, m_metaInfo->timecode, parent);
755             roto->slotShowScene(!disable);
756             connect(roto, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
757             connect(roto, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
758             connect(roto, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
759             connect(this, SIGNAL(syncEffectsPos(int)), roto, SLOT(slotSyncPosition(int)));
760             connect(this, SIGNAL(effectStateChanged(bool)), roto, SLOT(slotShowScene(bool)));
761             m_vbox->addWidget(roto);
762             m_valueItems[paramName] = roto;
763 #endif
764         } else if (type == "wipe") {
765             Wipeval *wpval = new Wipeval;
766             wpval->setupUi(toFillin);
767             wipeInfo w = getWipeInfo(value);
768             switch (w.start) {
769             case UP:
770                 wpval->start_up->setChecked(true);
771                 break;
772             case DOWN:
773                 wpval->start_down->setChecked(true);
774                 break;
775             case RIGHT:
776                 wpval->start_right->setChecked(true);
777                 break;
778             case LEFT:
779                 wpval->start_left->setChecked(true);
780                 break;
781             default:
782                 wpval->start_center->setChecked(true);
783                 break;
784             }
785             switch (w.end) {
786             case UP:
787                 wpval->end_up->setChecked(true);
788                 break;
789             case DOWN:
790                 wpval->end_down->setChecked(true);
791                 break;
792             case RIGHT:
793                 wpval->end_right->setChecked(true);
794                 break;
795             case LEFT:
796                 wpval->end_left->setChecked(true);
797                 break;
798             default:
799                 wpval->end_center->setChecked(true);
800                 break;
801             }
802             wpval->start_transp->setValue(w.startTransparency);
803             wpval->end_transp->setValue(w.endTransparency);
804             m_valueItems[paramName] = wpval;
805             connect(wpval->end_up, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
806             connect(wpval->end_down, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
807             connect(wpval->end_left, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
808             connect(wpval->end_right, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
809             connect(wpval->end_center, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
810             connect(wpval->start_up, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
811             connect(wpval->start_down, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
812             connect(wpval->start_left, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
813             connect(wpval->start_right, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
814             connect(wpval->start_center, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
815             connect(wpval->start_transp, SIGNAL(valueChanged(int)), this, SLOT(slotCollectAllParameters()));
816             connect(wpval->end_transp, SIGNAL(valueChanged(int)), this, SLOT(slotCollectAllParameters()));
817             //wpval->title->setTitle(na.toElement().text());
818             m_uiItems.append(wpval);
819         } else if (type == "url") {
820             Urlval *cval = new Urlval;
821             cval->setupUi(toFillin);
822             cval->label->setText(paramName);
823             cval->urlwidget->fileDialog()->setFilter(ProjectList::getExtensions());
824             m_valueItems[paramName] = cval;
825             cval->urlwidget->setUrl(KUrl(value));
826             connect(cval->urlwidget, SIGNAL(returnPressed()) , this, SLOT(slotCollectAllParameters()));
827             connect(cval->urlwidget, SIGNAL(urlSelected(const KUrl&)) , this, SLOT(slotCollectAllParameters()));
828             m_uiItems.append(cval);
829         } else if (type == "keywords") {
830             Keywordval* kval = new Keywordval;
831             kval->setupUi(toFillin);
832             kval->label->setText(paramName);
833             kval->lineeditwidget->setText(value);
834             QDomElement klistelem = pa.firstChildElement("keywords");
835             QDomElement kdisplaylistelem = pa.firstChildElement("keywordsdisplay");
836             QStringList keywordlist;
837             QStringList keyworddisplaylist;
838             if (!klistelem.isNull()) {
839                 keywordlist = klistelem.text().split(';');
840                 keyworddisplaylist = i18n(kdisplaylistelem.text().toUtf8().data()).split(';');
841             }
842             if (keyworddisplaylist.count() != keywordlist.count()) {
843                 keyworddisplaylist = keywordlist;
844             }
845             for (int i = 0; i < keywordlist.count(); i++) {
846                 kval->comboboxwidget->addItem(keyworddisplaylist.at(i), keywordlist.at(i));
847             }
848             // Add disabled user prompt at index 0
849             kval->comboboxwidget->insertItem(0, i18n("<select a keyword>"), "");
850             kval->comboboxwidget->model()->setData( kval->comboboxwidget->model()->index(0,0), QVariant(Qt::NoItemFlags), Qt::UserRole -1);
851             kval->comboboxwidget->setCurrentIndex(0);
852             m_valueItems[paramName] = kval;
853             connect(kval->lineeditwidget, SIGNAL(editingFinished()) , this, SLOT(collectAllParameters()));
854             connect(kval->comboboxwidget, SIGNAL(activated (const QString&)), this, SLOT(collectAllParameters()));
855             m_uiItems.append(kval);
856         } else if (type == "fontfamily") {
857             Fontval* fval = new Fontval;
858             fval->setupUi(toFillin);
859             fval->name->setText(paramName);
860             fval->fontfamilywidget->setCurrentFont(QFont(value));
861             m_valueItems[paramName] = fval;
862             connect(fval->fontfamilywidget, SIGNAL(currentFontChanged(const QFont &)), this, SLOT(collectAllParameters())) ;
863             m_uiItems.append(fval);
864         } else if (type == "filterjob") {
865             QVBoxLayout *l= new QVBoxLayout(toFillin);
866             QPushButton *button = new QPushButton(paramName, toFillin);
867             l->addWidget(button);
868             m_valueItems[paramName] = button;
869             connect(button, SIGNAL(pressed()), this, SLOT(slotStartFilterJobAction()));   
870         } else {
871             delete toFillin;
872             toFillin = NULL;
873         }
874
875         if (toFillin)
876             m_vbox->addWidget(toFillin);
877     }
878
879     if (stretch)
880         m_vbox->addStretch();
881
882     if (m_keyframeEditor)
883         m_keyframeEditor->checkVisibleParam();
884
885     // Make sure all doubleparam spinboxes have the same width, looks much better
886     QList<DoubleParameterWidget *> allWidgets = findChildren<DoubleParameterWidget *>();
887     int minSize = 0;
888     for (int i = 0; i < allWidgets.count(); i++) {
889         if (minSize < allWidgets.at(i)->spinSize()) minSize = allWidgets.at(i)->spinSize();
890     }
891     for (int i = 0; i < allWidgets.count(); i++) {
892         allWidgets.at(i)->setSpinSize(minSize);
893     }
894 }
895
896 ParameterContainer::~ParameterContainer()
897 {
898     //clearLayout(m_vbox);
899     delete m_vbox;
900 }
901
902 void ParameterContainer::meetDependency(const QString& name, QString type, QString value)
903 {
904     if (type == "curve") {
905         KisCurveWidget *curve = (KisCurveWidget*)m_valueItems[name];
906         if (curve) {
907             int color = value.toInt();
908             curve->setPixmap(QPixmap::fromImage(ColorTools::rgbCurvePlane(curve->size(), (ColorTools::ColorsRGB)(color == 3 ? 4 : color), 0.8)));
909         }
910     } else if (type == "bezier_spline") {
911         BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems[name];
912         if (widget) {
913             widget->setMode((BezierSplineWidget::CurveModes)((int)(value.toDouble() * 10)));
914         }
915     }
916 }
917
918 wipeInfo ParameterContainer::getWipeInfo(QString value)
919 {
920     wipeInfo info;
921     // Convert old geometry values that used a comma as separator
922     if (value.contains(',')) value.replace(',','/');
923     QString start = value.section(';', 0, 0);
924     QString end = value.section(';', 1, 1).section('=', 1, 1);
925     if (start.startsWith("-100%/0"))
926         info.start = LEFT;
927     else if (start.startsWith("100%/0"))
928         info.start = RIGHT;
929     else if (start.startsWith("0%/100%"))
930         info.start = DOWN;
931     else if (start.startsWith("0%/-100%"))
932         info.start = UP;
933     else
934         info.start = CENTER;
935
936     if (start.count(':') == 2)
937         info.startTransparency = start.section(':', -1).toInt();
938     else
939         info.startTransparency = 100;
940
941     if (end.startsWith("-100%/0"))
942         info.end = LEFT;
943     else if (end.startsWith("100%/0"))
944         info.end = RIGHT;
945     else if (end.startsWith("0%/100%"))
946         info.end = DOWN;
947     else if (end.startsWith("0%/-100%"))
948         info.end = UP;
949     else
950         info.end = CENTER;
951
952     if (end.count(':') == 2)
953         info.endTransparency = end.section(':', -1).toInt();
954     else
955         info.endTransparency = 100;
956
957     return info;
958 }
959
960 void ParameterContainer::updateTimecodeFormat()
961 {
962     if (m_keyframeEditor)
963         m_keyframeEditor->updateTimecodeFormat();
964
965     QDomNodeList namenode = m_effect.elementsByTagName("parameter");
966     for (int i = 0; i < namenode.count() ; i++) {
967         QDomNode pa = namenode.item(i);
968         QDomElement na = pa.firstChildElement("name");
969         QString type = pa.attributes().namedItem("type").nodeValue();
970         QString paramName = na.isNull() ? pa.attributes().namedItem("name").nodeValue() : i18n(na.text().toUtf8().data());
971
972         if (type == "geometry") {
973             if (KdenliveSettings::on_monitor_effects()) {
974                 if (m_geometryWidget) m_geometryWidget->updateTimecodeFormat();
975             } else {
976                 Geometryval *geom = ((Geometryval*)m_valueItems[paramName+"geometry"]);
977                 geom->updateTimecodeFormat();
978             }
979             break;
980         } else if (type == "position") {
981             PositionEdit *posi = ((PositionEdit*)m_valueItems[paramName+"position"]);
982             posi->updateTimecodeFormat();
983             break;
984 #ifdef USE_QJSON
985         } else if (type == "roto-spline") {
986             RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems[paramName]);
987             widget->updateTimecodeFormat();
988 #endif
989         }
990     }
991 }
992
993 void ParameterContainer::slotCollectAllParameters()
994 {
995     if (m_valueItems.isEmpty() || m_effect.isNull()) return;
996     QLocale locale;
997     locale.setNumberOptions(QLocale::OmitGroupSeparator);
998     const QDomElement oldparam = m_effect.cloneNode().toElement();
999     //QDomElement newparam = oldparam.cloneNode().toElement();
1000     QDomNodeList namenode = m_effect.elementsByTagName("parameter");
1001
1002     for (int i = 0; i < namenode.count() ; i++) {
1003         QDomNode pa = namenode.item(i);
1004         QDomElement na = pa.firstChildElement("name");
1005         QString type = pa.attributes().namedItem("type").nodeValue();
1006         QString paramName = na.isNull() ? pa.attributes().namedItem("name").nodeValue() : i18n(na.text().toUtf8().data());
1007         if (type == "complex")
1008             paramName.append("complex");
1009         else if (type == "position")
1010             paramName.append("position");
1011         else if (type == "geometry")
1012             paramName.append("geometry");
1013         else if (type == "keyframe")
1014             paramName.append("keyframe");
1015         if (type != "simplekeyframe" && type != "fixed" && type != "addedgeometry" && !m_valueItems.contains(paramName)) {
1016             kDebug() << "// Param: " << paramName << " NOT FOUND";
1017             continue;
1018         }
1019
1020         QString setValue;
1021         if (type == "double" || type == "constant") {
1022             DoubleParameterWidget *doubleparam = (DoubleParameterWidget*)m_valueItems.value(paramName);
1023             setValue = locale.toString(doubleparam->getValue());
1024         } else if (type == "list") {
1025             KComboBox *box = ((Listval*)m_valueItems.value(paramName))->list;
1026             setValue = box->itemData(box->currentIndex()).toString();
1027         } else if (type == "bool") {
1028             QCheckBox *box = ((Boolval*)m_valueItems.value(paramName))->checkBox;
1029             setValue = box->checkState() == Qt::Checked ? "1" : "0" ;
1030         } else if (type == "color") {
1031             ChooseColorWidget *choosecolor = ((ChooseColorWidget*)m_valueItems.value(paramName));
1032             setValue = choosecolor->getColor();
1033         } else if (type == "complex") {
1034             ComplexParameter *complex = ((ComplexParameter*)m_valueItems.value(paramName));
1035             namenode.item(i) = complex->getParamDesc();
1036         } else if (type == "geometry") {
1037             if (KdenliveSettings::on_monitor_effects()) {
1038                 if (m_geometryWidget) namenode.item(i).toElement().setAttribute("value", m_geometryWidget->getValue());
1039             } else {
1040                 Geometryval *geom = ((Geometryval*)m_valueItems.value(paramName));
1041                 namenode.item(i).toElement().setAttribute("value", geom->getValue());
1042             }
1043         } else if (type == "addedgeometry") {
1044             namenode.item(i).toElement().setAttribute("value", m_geometryWidget->getExtraValue(namenode.item(i).toElement().attribute("name")));
1045         } else if (type == "position") {
1046             PositionEdit *pedit = ((PositionEdit*)m_valueItems.value(paramName));
1047             int pos = pedit->getPosition();
1048             setValue = QString::number(pos);
1049             if (m_effect.attribute("id") == "fadein" || m_effect.attribute("id") == "fade_from_black") {
1050                 // Make sure duration is not longer than clip
1051                 /*if (pos > m_out) {
1052                     pos = m_out;
1053                     pedit->setPosition(pos);
1054                 }*/
1055                 EffectsList::setParameter(m_effect, "in", QString::number(m_in));
1056                 EffectsList::setParameter(m_effect, "out", QString::number(m_in + pos));
1057                 setValue.clear();
1058             } else if (m_effect.attribute("id") == "fadeout" || m_effect.attribute("id") == "fade_to_black") {
1059                 // Make sure duration is not longer than clip
1060                 /*if (pos > m_out) {
1061                     pos = m_out;
1062                     pedit->setPosition(pos);
1063                 }*/
1064                 EffectsList::setParameter(m_effect, "in", QString::number(m_out - pos));
1065                 EffectsList::setParameter(m_effect, "out", QString::number(m_out));
1066                 setValue.clear();
1067             }
1068         } else if (type == "curve") {
1069             KisCurveWidget *curve = ((KisCurveWidget*)m_valueItems.value(paramName));
1070             QList<QPointF> points = curve->curve().points();
1071             QString number = pa.attributes().namedItem("number").nodeValue();
1072             QString inName = pa.attributes().namedItem("inpoints").nodeValue();
1073             QString outName = pa.attributes().namedItem("outpoints").nodeValue();
1074             int off = pa.attributes().namedItem("min").nodeValue().toInt();
1075             int end = pa.attributes().namedItem("max").nodeValue().toInt();
1076             EffectsList::setParameter(m_effect, number, QString::number(points.count()));
1077             for (int j = 0; (j < points.count() && j + off <= end); j++) {
1078                 QString in = inName;
1079                 in.replace("%i", QString::number(j + off));
1080                 QString out = outName;
1081                 out.replace("%i", QString::number(j + off));
1082                 EffectsList::setParameter(m_effect, in, locale.toString(points.at(j).x()));
1083                 EffectsList::setParameter(m_effect, out, locale.toString(points.at(j).y()));
1084             }
1085             QString depends = pa.attributes().namedItem("depends").nodeValue();
1086             if (!depends.isEmpty())
1087                 meetDependency(paramName, type, EffectsList::parameter(m_effect, depends));
1088         } else if (type == "bezier_spline") {
1089             BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems.value(paramName);
1090             setValue = widget->spline();
1091             QString depends = pa.attributes().namedItem("depends").nodeValue();
1092             if (!depends.isEmpty())
1093                 meetDependency(paramName, type, EffectsList::parameter(m_effect, depends));
1094 #ifdef USE_QJSON
1095         } else if (type == "roto-spline") {
1096             RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems.value(paramName));
1097             setValue = widget->getSpline();
1098 #endif
1099         } else if (type == "wipe") {
1100             Wipeval *wp = (Wipeval*)m_valueItems.value(paramName);
1101             wipeInfo info;
1102             if (wp->start_left->isChecked())
1103                 info.start = LEFT;
1104             else if (wp->start_right->isChecked())
1105                 info.start = RIGHT;
1106             else if (wp->start_up->isChecked())
1107                 info.start = UP;
1108             else if (wp->start_down->isChecked())
1109                 info.start = DOWN;
1110             else if (wp->start_center->isChecked())
1111                 info.start = CENTER;
1112             else
1113                 info.start = LEFT;
1114             info.startTransparency = wp->start_transp->value();
1115
1116             if (wp->end_left->isChecked())
1117                 info.end = LEFT;
1118             else if (wp->end_right->isChecked())
1119                 info.end = RIGHT;
1120             else if (wp->end_up->isChecked())
1121                 info.end = UP;
1122             else if (wp->end_down->isChecked())
1123                 info.end = DOWN;
1124             else if (wp->end_center->isChecked())
1125                 info.end = CENTER;
1126             else
1127                 info.end = RIGHT;
1128             info.endTransparency = wp->end_transp->value();
1129
1130             setValue = getWipeString(info);
1131         } else if ((type == "simplekeyframe" || type == "keyframe") && m_keyframeEditor) {
1132             QDomElement elem = pa.toElement();
1133             QString realName = i18n(na.toElement().text().toUtf8().data());
1134             QString val = m_keyframeEditor->getValue(realName);
1135             elem.setAttribute("keyframes", val);
1136
1137             if (m_keyframeEditor->isVisibleParam(realName))
1138                 elem.setAttribute("intimeline", "1");
1139             else if (elem.hasAttribute("intimeline"))
1140                 elem.removeAttribute("intimeline");
1141         } else if (type == "url") {
1142             KUrlRequester *req = ((Urlval*)m_valueItems.value(paramName))->urlwidget;
1143             setValue = req->url().path();
1144         } else if (type == "keywords"){
1145             QLineEdit *line = ((Keywordval*)m_valueItems.value(paramName))->lineeditwidget;
1146             QComboBox *combo = ((Keywordval*)m_valueItems.value(paramName))->comboboxwidget;
1147             if(combo->currentIndex())
1148             {
1149                 QString comboval = combo->itemData(combo->currentIndex()).toString();
1150                 line->insert(comboval);
1151                 combo->setCurrentIndex(0);
1152             }
1153             setValue = line->text();
1154         } else if (type == "fontfamily") {
1155             QFontComboBox* fontfamily = ((Fontval*)m_valueItems.value(paramName))->fontfamilywidget;
1156             setValue = fontfamily->currentFont().family();
1157         }
1158         if (!setValue.isNull())
1159             pa.attributes().namedItem("value").setNodeValue(setValue);
1160
1161     }
1162     emit parameterChanged(oldparam, m_effect, m_index);
1163 }
1164
1165 QString ParameterContainer::getWipeString(wipeInfo info)
1166 {
1167
1168     QString start;
1169     QString end;
1170     switch (info.start) {
1171     case LEFT:
1172         start = "-100%/0%:100%x100%";
1173         break;
1174     case RIGHT:
1175         start = "100%/0%:100%x100%";
1176         break;
1177     case DOWN:
1178         start = "0%/100%:100%x100%";
1179         break;
1180     case UP:
1181         start = "0%/-100%:100%x100%";
1182         break;
1183     default:
1184         start = "0%/0%:100%x100%";
1185         break;
1186     }
1187     start.append(':' + QString::number(info.startTransparency));
1188
1189     switch (info.end) {
1190     case LEFT:
1191         end = "-100%/0%:100%x100%";
1192         break;
1193     case RIGHT:
1194         end = "100%/0%:100%x100%";
1195         break;
1196     case DOWN:
1197         end = "0%/100%:100%x100%";
1198         break;
1199     case UP:
1200         end = "0%/-100%:100%x100%";
1201         break;
1202     default:
1203         end = "0%/0%:100%x100%";
1204         break;
1205     }
1206     end.append(':' + QString::number(info.endTransparency));
1207     return QString(start + ";-1=" + end);
1208 }
1209
1210 int ParameterContainer::index() const
1211 {
1212     return m_index;
1213 }
1214
1215 void ParameterContainer::slotStartFilterJobAction()
1216 {
1217     QDomNodeList namenode = m_effect.elementsByTagName("parameter");
1218     for (int i = 0; i < namenode.count() ; i++) {
1219         QDomElement pa = namenode.item(i).toElement();
1220         QString type = pa.attribute("type");
1221         if (type == "filterjob") {
1222             emit startFilterJob(pa.attribute("filtertag"), pa.attribute("filterparams"), pa.attribute("finalfilter"), pa.attribute("consumer"), pa.attribute("consumerparams"), pa.attribute("wantedproperties"));
1223             kDebug()<<" - - -PROPS:\n"<<pa.attribute("filtertag")<<"-"<< pa.attribute("filterparams")<<"-"<< pa.attribute("consumer")<<"-"<< pa.attribute("consumerparams")<<"-"<< pa.attribute("wantedproperties");
1224             break;
1225         }
1226     }
1227 }
1228