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