]> git.sesse.net Git - kdenlive/blob - src/effectstack/collapsibleeffect.cpp
Fix stupid shortcut that accidentally stole keypresses
[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(QPalette p)
209 {
210     KColorScheme scheme(p.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(p.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 = EffectsList::parameter(e, pa.attribute("number")).toInt();
788             QString inName = pa.attribute("inpoints");
789             QString outName = pa.attribute("outpoints");
790             int start = pa.attribute("min").toInt();
791             for (int j = start; j <= number; j++) {
792                 QString in = inName;
793                 in.replace("%i", QString::number(j));
794                 QString out = outName;
795                 out.replace("%i", QString::number(j));
796                 points << QPointF(EffectsList::parameter(e, in).toDouble(), EffectsList::parameter(e, out).toDouble());
797             }
798             if (!points.isEmpty())
799                 curve->setCurve(KisCubicCurve(points));
800             MySpinBox *spinin = new MySpinBox();
801             spinin->setRange(0, 1000);
802             MySpinBox *spinout = new MySpinBox();
803             spinout->setRange(0, 1000);
804             curve->setupInOutControls(spinin, spinout, 0, 1000);
805             m_vbox->addWidget(curve);
806             m_vbox->addWidget(spinin);
807             m_vbox->addWidget(spinout);
808
809             connect(curve, SIGNAL(modified()), this, SLOT(slotCollectAllParameters()));
810             m_valueItems[paramName] = curve;
811
812             QString depends = pa.attribute("depends");
813             if (!depends.isEmpty())
814                 meetDependency(paramName, type, EffectsList::parameter(e, depends));
815         } else if (type == "bezier_spline") {
816             BezierSplineWidget *widget = new BezierSplineWidget(value, parent);
817             stretch = false;
818             m_vbox->addWidget(widget);
819             m_valueItems[paramName] = widget;
820             connect(widget, SIGNAL(modified()), this, SLOT(slotCollectAllParameters()));
821             QString depends = pa.attribute("depends");
822             if (!depends.isEmpty())
823                 meetDependency(paramName, type, EffectsList::parameter(e, depends));
824 #ifdef USE_QJSON
825         } else if (type == "roto-spline") {
826             RotoWidget *roto = new RotoWidget(value, m_metaInfo->monitor, info, m_metaInfo->timecode, parent);
827             roto->slotShowScene(!disable);
828             connect(roto, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
829             connect(roto, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
830             connect(roto, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
831             connect(this, SIGNAL(syncEffectsPos(int)), roto, SLOT(slotSyncPosition(int)));
832             connect(this, SIGNAL(effectStateChanged(bool)), roto, SLOT(slotShowScene(bool)));
833             m_vbox->addWidget(roto);
834             m_valueItems[paramName] = roto;
835 #endif
836         } else if (type == "wipe") {
837             Wipeval *wpval = new Wipeval;
838             wpval->setupUi(toFillin);
839             wipeInfo w = getWipeInfo(value);
840             switch (w.start) {
841             case UP:
842                 wpval->start_up->setChecked(true);
843                 break;
844             case DOWN:
845                 wpval->start_down->setChecked(true);
846                 break;
847             case RIGHT:
848                 wpval->start_right->setChecked(true);
849                 break;
850             case LEFT:
851                 wpval->start_left->setChecked(true);
852                 break;
853             default:
854                 wpval->start_center->setChecked(true);
855                 break;
856             }
857             switch (w.end) {
858             case UP:
859                 wpval->end_up->setChecked(true);
860                 break;
861             case DOWN:
862                 wpval->end_down->setChecked(true);
863                 break;
864             case RIGHT:
865                 wpval->end_right->setChecked(true);
866                 break;
867             case LEFT:
868                 wpval->end_left->setChecked(true);
869                 break;
870             default:
871                 wpval->end_center->setChecked(true);
872                 break;
873             }
874             wpval->start_transp->setValue(w.startTransparency);
875             wpval->end_transp->setValue(w.endTransparency);
876             m_valueItems[paramName] = wpval;
877             connect(wpval->end_up, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
878             connect(wpval->end_down, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
879             connect(wpval->end_left, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
880             connect(wpval->end_right, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
881             connect(wpval->end_center, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
882             connect(wpval->start_up, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
883             connect(wpval->start_down, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
884             connect(wpval->start_left, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
885             connect(wpval->start_right, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
886             connect(wpval->start_center, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
887             connect(wpval->start_transp, SIGNAL(valueChanged(int)), this, SLOT(slotCollectAllParameters()));
888             connect(wpval->end_transp, SIGNAL(valueChanged(int)), this, SLOT(slotCollectAllParameters()));
889             //wpval->title->setTitle(na.toElement().text());
890             m_uiItems.append(wpval);
891         } else if (type == "url") {
892             Urlval *cval = new Urlval;
893             cval->setupUi(toFillin);
894             cval->label->setText(paramName);
895             cval->urlwidget->fileDialog()->setFilter(ProjectList::getExtensions());
896             m_valueItems[paramName] = cval;
897             cval->urlwidget->setUrl(KUrl(value));
898             connect(cval->urlwidget, SIGNAL(returnPressed()) , this, SLOT(slotCollectAllParameters()));
899             connect(cval->urlwidget, SIGNAL(urlSelected(const KUrl&)) , this, SLOT(slotCollectAllParameters()));
900             m_uiItems.append(cval);
901         } else if (type == "keywords") {
902             Keywordval* kval = new Keywordval;
903             kval->setupUi(toFillin);
904             kval->label->setText(paramName);
905             kval->lineeditwidget->setText(value);
906             QDomElement klistelem = pa.firstChildElement("keywords");
907             QDomElement kdisplaylistelem = pa.firstChildElement("keywordsdisplay");
908             QStringList keywordlist;
909             QStringList keyworddisplaylist;
910             if (!klistelem.isNull()) {
911                 keywordlist = klistelem.text().split(';');
912                 keyworddisplaylist = i18n(kdisplaylistelem.text().toUtf8().data()).split(';');
913             }
914             if (keyworddisplaylist.count() != keywordlist.count()) {
915                 keyworddisplaylist = keywordlist;
916             }
917             for (int i = 0; i < keywordlist.count(); i++) {
918                 kval->comboboxwidget->addItem(keyworddisplaylist.at(i), keywordlist.at(i));
919             }
920             // Add disabled user prompt at index 0
921             kval->comboboxwidget->insertItem(0, i18n("<select a keyword>"), "");
922             kval->comboboxwidget->model()->setData( kval->comboboxwidget->model()->index(0,0), QVariant(Qt::NoItemFlags), Qt::UserRole -1);
923             kval->comboboxwidget->setCurrentIndex(0);
924             m_valueItems[paramName] = kval;
925             connect(kval->lineeditwidget, SIGNAL(editingFinished()) , this, SLOT(collectAllParameters()));
926             connect(kval->comboboxwidget, SIGNAL(activated (const QString&)), this, SLOT(collectAllParameters()));
927             m_uiItems.append(kval);
928         } else if (type == "fontfamily") {
929             Fontval* fval = new Fontval;
930             fval->setupUi(toFillin);
931             fval->name->setText(paramName);
932             fval->fontfamilywidget->setCurrentFont(QFont(value));
933             m_valueItems[paramName] = fval;
934             connect(fval->fontfamilywidget, SIGNAL(currentFontChanged(const QFont &)), this, SLOT(collectAllParameters())) ;
935             m_uiItems.append(fval);
936         } else if (type == "filterjob") {
937             QVBoxLayout *l= new QVBoxLayout(toFillin);
938             QPushButton *button = new QPushButton(paramName, toFillin);
939             l->addWidget(button);
940             m_valueItems[paramName] = button;
941             connect(button, SIGNAL(pressed()), this, SLOT(slotStartFilterJobAction()));   
942         } else {
943             delete toFillin;
944             toFillin = NULL;
945         }
946
947         if (toFillin)
948             m_vbox->addWidget(toFillin);
949     }
950
951     if (stretch)
952         m_vbox->addStretch();
953
954     if (m_keyframeEditor)
955         m_keyframeEditor->checkVisibleParam();
956
957     // Make sure all doubleparam spinboxes have the same width, looks much better
958     QList<DoubleParameterWidget *> allWidgets = findChildren<DoubleParameterWidget *>();
959     int minSize = 0;
960     for (int i = 0; i < allWidgets.count(); i++) {
961         if (minSize < allWidgets.at(i)->spinSize()) minSize = allWidgets.at(i)->spinSize();
962     }
963     for (int i = 0; i < allWidgets.count(); i++) {
964         allWidgets.at(i)->setSpinSize(minSize);
965     }
966 }
967
968 ParameterContainer::~ParameterContainer()
969 {
970     clearLayout(m_vbox);
971     delete m_vbox;
972 }
973
974 void ParameterContainer::meetDependency(const QString& name, QString type, QString value)
975 {
976     if (type == "curve") {
977         KisCurveWidget *curve = (KisCurveWidget*)m_valueItems[name];
978         if (curve) {
979             int color = value.toInt();
980             curve->setPixmap(QPixmap::fromImage(ColorTools::rgbCurvePlane(curve->size(), (ColorTools::ColorsRGB)(color == 3 ? 4 : color), 0.8)));
981         }
982     } else if (type == "bezier_spline") {
983         BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems[name];
984         if (widget) {
985             widget->setMode((BezierSplineWidget::CurveModes)((int)(value.toDouble() * 10)));
986         }
987     }
988 }
989
990 wipeInfo ParameterContainer::getWipeInfo(QString value)
991 {
992     wipeInfo info;
993     // Convert old geometry values that used a comma as separator
994     if (value.contains(',')) value.replace(',','/');
995     QString start = value.section(';', 0, 0);
996     QString end = value.section(';', 1, 1).section('=', 1, 1);
997     if (start.startsWith("-100%/0"))
998         info.start = LEFT;
999     else if (start.startsWith("100%/0"))
1000         info.start = RIGHT;
1001     else if (start.startsWith("0%/100%"))
1002         info.start = DOWN;
1003     else if (start.startsWith("0%/-100%"))
1004         info.start = UP;
1005     else
1006         info.start = CENTER;
1007
1008     if (start.count(':') == 2)
1009         info.startTransparency = start.section(':', -1).toInt();
1010     else
1011         info.startTransparency = 100;
1012
1013     if (end.startsWith("-100%/0"))
1014         info.end = LEFT;
1015     else if (end.startsWith("100%/0"))
1016         info.end = RIGHT;
1017     else if (end.startsWith("0%/100%"))
1018         info.end = DOWN;
1019     else if (end.startsWith("0%/-100%"))
1020         info.end = UP;
1021     else
1022         info.end = CENTER;
1023
1024     if (end.count(':') == 2)
1025         info.endTransparency = end.section(':', -1).toInt();
1026     else
1027         info.endTransparency = 100;
1028
1029     return info;
1030 }
1031
1032 void ParameterContainer::updateTimecodeFormat()
1033 {
1034     if (m_keyframeEditor)
1035         m_keyframeEditor->updateTimecodeFormat();
1036
1037     QDomNodeList namenode = m_effect.elementsByTagName("parameter");
1038     for (int i = 0; i < namenode.count() ; i++) {
1039         QDomNode pa = namenode.item(i);
1040         QDomElement na = pa.firstChildElement("name");
1041         QString type = pa.attributes().namedItem("type").nodeValue();
1042         QString paramName = na.isNull() ? pa.attributes().namedItem("name").nodeValue() : i18n(na.text().toUtf8().data());
1043
1044         if (type == "geometry") {
1045             if (KdenliveSettings::on_monitor_effects()) {
1046                 if (m_geometryWidget) m_geometryWidget->updateTimecodeFormat();
1047             } else {
1048                 Geometryval *geom = ((Geometryval*)m_valueItems[paramName+"geometry"]);
1049                 geom->updateTimecodeFormat();
1050             }
1051             break;
1052         } else if (type == "position") {
1053             PositionEdit *posi = ((PositionEdit*)m_valueItems[paramName+"position"]);
1054             posi->updateTimecodeFormat();
1055             break;
1056 #ifdef USE_QJSON
1057         } else if (type == "roto-spline") {
1058             RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems[paramName]);
1059             widget->updateTimecodeFormat();
1060 #endif
1061         }
1062     }
1063 }
1064
1065 void ParameterContainer::slotCollectAllParameters()
1066 {
1067     if (m_valueItems.isEmpty() || m_effect.isNull()) return;
1068     QLocale locale;
1069     locale.setNumberOptions(QLocale::OmitGroupSeparator);
1070     const QDomElement oldparam = m_effect.cloneNode().toElement();
1071     //QDomElement newparam = oldparam.cloneNode().toElement();
1072     QDomNodeList namenode = m_effect.elementsByTagName("parameter");
1073
1074     for (int i = 0; i < namenode.count() ; i++) {
1075         QDomNode pa = namenode.item(i);
1076         QDomElement na = pa.firstChildElement("name");
1077         QString type = pa.attributes().namedItem("type").nodeValue();
1078         QString paramName = na.isNull() ? pa.attributes().namedItem("name").nodeValue() : i18n(na.text().toUtf8().data());
1079         if (type == "complex")
1080             paramName.append("complex");
1081         else if (type == "position")
1082             paramName.append("position");
1083         else if (type == "geometry")
1084             paramName.append("geometry");
1085         else if (type == "keyframe")
1086             paramName.append("keyframe");
1087         if (type != "simplekeyframe" && type != "fixed" && type != "addedgeometry" && !m_valueItems.contains(paramName)) {
1088             kDebug() << "// Param: " << paramName << " NOT FOUND";
1089             continue;
1090         }
1091
1092         QString setValue;
1093         if (type == "double" || type == "constant") {
1094             DoubleParameterWidget *doubleparam = (DoubleParameterWidget*)m_valueItems.value(paramName);
1095             setValue = locale.toString(doubleparam->getValue());
1096         } else if (type == "list") {
1097             KComboBox *box = ((Listval*)m_valueItems.value(paramName))->list;
1098             setValue = box->itemData(box->currentIndex()).toString();
1099         } else if (type == "bool") {
1100             QCheckBox *box = ((Boolval*)m_valueItems.value(paramName))->checkBox;
1101             setValue = box->checkState() == Qt::Checked ? "1" : "0" ;
1102         } else if (type == "color") {
1103             ChooseColorWidget *choosecolor = ((ChooseColorWidget*)m_valueItems.value(paramName));
1104             setValue = choosecolor->getColor();
1105         } else if (type == "complex") {
1106             ComplexParameter *complex = ((ComplexParameter*)m_valueItems.value(paramName));
1107             namenode.item(i) = complex->getParamDesc();
1108         } else if (type == "geometry") {
1109             if (KdenliveSettings::on_monitor_effects()) {
1110                 if (m_geometryWidget) namenode.item(i).toElement().setAttribute("value", m_geometryWidget->getValue());
1111             } else {
1112                 Geometryval *geom = ((Geometryval*)m_valueItems.value(paramName));
1113                 namenode.item(i).toElement().setAttribute("value", geom->getValue());
1114             }
1115         } else if (type == "addedgeometry") {
1116             namenode.item(i).toElement().setAttribute("value", m_geometryWidget->getExtraValue(namenode.item(i).toElement().attribute("name")));
1117         } else if (type == "position") {
1118             PositionEdit *pedit = ((PositionEdit*)m_valueItems.value(paramName));
1119             int pos = pedit->getPosition();
1120             setValue = QString::number(pos);
1121             if (m_effect.attribute("id") == "fadein" || m_effect.attribute("id") == "fade_from_black") {
1122                 // Make sure duration is not longer than clip
1123                 /*if (pos > m_out) {
1124                     pos = m_out;
1125                     pedit->setPosition(pos);
1126                 }*/
1127                 EffectsList::setParameter(m_effect, "in", QString::number(m_in));
1128                 EffectsList::setParameter(m_effect, "out", QString::number(m_in + pos));
1129                 setValue.clear();
1130             } else if (m_effect.attribute("id") == "fadeout" || m_effect.attribute("id") == "fade_to_black") {
1131                 // Make sure duration is not longer than clip
1132                 /*if (pos > m_out) {
1133                     pos = m_out;
1134                     pedit->setPosition(pos);
1135                 }*/
1136                 EffectsList::setParameter(m_effect, "in", QString::number(m_out - pos));
1137                 EffectsList::setParameter(m_effect, "out", QString::number(m_out));
1138                 setValue.clear();
1139             }
1140         } else if (type == "curve") {
1141             KisCurveWidget *curve = ((KisCurveWidget*)m_valueItems.value(paramName));
1142             QList<QPointF> points = curve->curve().points();
1143             QString number = pa.attributes().namedItem("number").nodeValue();
1144             QString inName = pa.attributes().namedItem("inpoints").nodeValue();
1145             QString outName = pa.attributes().namedItem("outpoints").nodeValue();
1146             int off = pa.attributes().namedItem("min").nodeValue().toInt();
1147             int end = pa.attributes().namedItem("max").nodeValue().toInt();
1148             EffectsList::setParameter(m_effect, number, QString::number(points.count()));
1149             for (int j = 0; (j < points.count() && j + off <= end); j++) {
1150                 QString in = inName;
1151                 in.replace("%i", QString::number(j + off));
1152                 QString out = outName;
1153                 out.replace("%i", QString::number(j + off));
1154                 EffectsList::setParameter(m_effect, in, locale.toString(points.at(j).x()));
1155                 EffectsList::setParameter(m_effect, out, locale.toString(points.at(j).y()));
1156             }
1157             QString depends = pa.attributes().namedItem("depends").nodeValue();
1158             if (!depends.isEmpty())
1159                 meetDependency(paramName, type, EffectsList::parameter(m_effect, depends));
1160         } else if (type == "bezier_spline") {
1161             BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems.value(paramName);
1162             setValue = widget->spline();
1163             QString depends = pa.attributes().namedItem("depends").nodeValue();
1164             if (!depends.isEmpty())
1165                 meetDependency(paramName, type, EffectsList::parameter(m_effect, depends));
1166 #ifdef USE_QJSON
1167         } else if (type == "roto-spline") {
1168             RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems.value(paramName));
1169             setValue = widget->getSpline();
1170 #endif
1171         } else if (type == "wipe") {
1172             Wipeval *wp = (Wipeval*)m_valueItems.value(paramName);
1173             wipeInfo info;
1174             if (wp->start_left->isChecked())
1175                 info.start = LEFT;
1176             else if (wp->start_right->isChecked())
1177                 info.start = RIGHT;
1178             else if (wp->start_up->isChecked())
1179                 info.start = UP;
1180             else if (wp->start_down->isChecked())
1181                 info.start = DOWN;
1182             else if (wp->start_center->isChecked())
1183                 info.start = CENTER;
1184             else
1185                 info.start = LEFT;
1186             info.startTransparency = wp->start_transp->value();
1187
1188             if (wp->end_left->isChecked())
1189                 info.end = LEFT;
1190             else if (wp->end_right->isChecked())
1191                 info.end = RIGHT;
1192             else if (wp->end_up->isChecked())
1193                 info.end = UP;
1194             else if (wp->end_down->isChecked())
1195                 info.end = DOWN;
1196             else if (wp->end_center->isChecked())
1197                 info.end = CENTER;
1198             else
1199                 info.end = RIGHT;
1200             info.endTransparency = wp->end_transp->value();
1201
1202             setValue = getWipeString(info);
1203         } else if ((type == "simplekeyframe" || type == "keyframe") && m_keyframeEditor) {
1204             QDomElement elem = pa.toElement();
1205             QString realName = i18n(na.toElement().text().toUtf8().data());
1206             QString val = m_keyframeEditor->getValue(realName);
1207             elem.setAttribute("keyframes", val);
1208
1209             if (m_keyframeEditor->isVisibleParam(realName))
1210                 elem.setAttribute("intimeline", "1");
1211             else if (elem.hasAttribute("intimeline"))
1212                 elem.removeAttribute("intimeline");
1213         } else if (type == "url") {
1214             KUrlRequester *req = ((Urlval*)m_valueItems.value(paramName))->urlwidget;
1215             setValue = req->url().path();
1216         } else if (type == "keywords"){
1217             QLineEdit *line = ((Keywordval*)m_valueItems.value(paramName))->lineeditwidget;
1218             QComboBox *combo = ((Keywordval*)m_valueItems.value(paramName))->comboboxwidget;
1219             if(combo->currentIndex())
1220             {
1221                 QString comboval = combo->itemData(combo->currentIndex()).toString();
1222                 line->insert(comboval);
1223                 combo->setCurrentIndex(0);
1224             }
1225             setValue = line->text();
1226         } else if (type == "fontfamily") {
1227             QFontComboBox* fontfamily = ((Fontval*)m_valueItems.value(paramName))->fontfamilywidget;
1228             setValue = fontfamily->currentFont().family();
1229         }
1230         if (!setValue.isNull())
1231             pa.attributes().namedItem("value").setNodeValue(setValue);
1232
1233     }
1234     emit parameterChanged(oldparam, m_effect, m_effect.attribute("kdenlive_ix").toInt());
1235 }
1236
1237 QString ParameterContainer::getWipeString(wipeInfo info)
1238 {
1239
1240     QString start;
1241     QString end;
1242     switch (info.start) {
1243     case LEFT:
1244         start = "-100%/0%:100%x100%";
1245         break;
1246     case RIGHT:
1247         start = "100%/0%:100%x100%";
1248         break;
1249     case DOWN:
1250         start = "0%/100%:100%x100%";
1251         break;
1252     case UP:
1253         start = "0%/-100%:100%x100%";
1254         break;
1255     default:
1256         start = "0%/0%:100%x100%";
1257         break;
1258     }
1259     start.append(':' + QString::number(info.startTransparency));
1260
1261     switch (info.end) {
1262     case LEFT:
1263         end = "-100%/0%:100%x100%";
1264         break;
1265     case RIGHT:
1266         end = "100%/0%:100%x100%";
1267         break;
1268     case DOWN:
1269         end = "0%/100%:100%x100%";
1270         break;
1271     case UP:
1272         end = "0%/-100%:100%x100%";
1273         break;
1274     default:
1275         end = "0%/0%:100%x100%";
1276         break;
1277     }
1278     end.append(':' + QString::number(info.endTransparency));
1279     return QString(start + ";-1=" + end);
1280 }
1281
1282 void ParameterContainer::slotStartFilterJobAction()
1283 {
1284     QDomNodeList namenode = m_effect.elementsByTagName("parameter");
1285     for (int i = 0; i < namenode.count() ; i++) {
1286         QDomElement pa = namenode.item(i).toElement();
1287         QString type = pa.attribute("type");
1288         if (type == "filterjob") {
1289             emit startFilterJob(pa.attribute("filtertag"), pa.attribute("filterparams"), pa.attribute("finalfilter"), pa.attribute("consumer"), pa.attribute("consumerparams"), pa.attribute("wantedproperties"));
1290             kDebug()<<" - - -PROPS:\n"<<pa.attribute("filtertag")<<"-"<< pa.attribute("filterparams")<<"-"<< pa.attribute("consumer")<<"-"<< pa.attribute("consumerparams")<<"-"<< pa.attribute("wantedproperties");
1291             break;
1292         }
1293     }
1294 }