]> git.sesse.net Git - kdenlive/blob - src/effectstack/parametercontainer.cpp
Minor improvements to keyframe widget, allow resetting keyframes
[kdenlive] / src / effectstack / parametercontainer.cpp
1 /***************************************************************************
2  *   Copyright (C) 2012 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 #include "ui_listval_ui.h"
21 #include "ui_boolval_ui.h"
22 #include "ui_wipeval_ui.h"
23 #include "ui_urlval_ui.h"
24 #include "ui_keywordval_ui.h"
25 #include "ui_fontval_ui.h"
26 #include "complexparameter.h"
27 #include "geometryval.h"
28 #include "positionedit.h"
29 #include "kis_curve_widget.h"
30 #include "kis_cubic_curve.h"
31 #include "choosecolorwidget.h"
32 #include "geometrywidget.h"
33 #include "colortools.h"
34 #include "doubleparameterwidget.h"
35 #include "cornerswidget.h"
36 #include "dragvalue.h"
37 #include "beziercurve/beziersplinewidget.h"
38 #ifdef USE_QJSON
39 #include "rotoscoping/rotowidget.h"
40 #endif
41 #include "kdenlivesettings.h"
42 #include "profilesdialog.h"
43 #include "projectlist.h"
44 #include "mainwindow.h"
45 #include "parametercontainer.h"
46 #include "../customtrackview.h"
47
48 #include <KUrlRequester>
49 #include <KFileDialog>
50
51 #include <QMap>
52 #include <QString>
53 #include <QImage>
54
55 MySpinBox::MySpinBox(QWidget * parent):
56     QSpinBox(parent)
57 {
58     setFocusPolicy(Qt::StrongFocus);
59 }
60
61 void MySpinBox::focusInEvent(QFocusEvent *e)
62 {
63      setFocusPolicy(Qt::WheelFocus);
64      e->accept();
65 }
66
67 void MySpinBox::focusOutEvent(QFocusEvent *e)
68 {
69      setFocusPolicy(Qt::StrongFocus);
70      e->accept();
71 }
72
73 class Boolval: public QWidget, public Ui::Boolval_UI
74 {
75 };
76
77 class Listval: public QWidget, public Ui::Listval_UI
78 {
79 };
80
81 class Wipeval: public QWidget, public Ui::Wipeval_UI
82 {
83 };
84
85 class Urlval: public QWidget, public Ui::Urlval_UI
86 {
87 };
88
89 class Keywordval: public QWidget, public Ui::Keywordval_UI
90 {
91 };
92
93 class Fontval: public QWidget, public Ui::Fontval_UI
94 {
95 };
96
97
98 ParameterContainer::ParameterContainer(QDomElement effect, ItemInfo info, EffectMetaInfo *metaInfo, QWidget * parent) :
99         m_keyframeEditor(NULL),
100         m_geometryWidget(NULL),
101         m_metaInfo(metaInfo),
102         m_effect(effect),
103         m_needsMonitorEffectScene(false)
104 {
105     m_in = info.cropStart.frames(KdenliveSettings::project_fps());
106     m_out = (info.cropStart + info.cropDuration).frames(KdenliveSettings::project_fps()) - 1;
107
108     QDomNodeList namenode = effect.childNodes(); //elementsByTagName("parameter");
109     
110     QDomElement e = effect.toElement();
111     int minFrame = e.attribute("start").toInt();
112     int maxFrame = e.attribute("end").toInt();
113     // In transitions, maxFrame is in fact one frame after the end of transition
114     if (maxFrame > 0) maxFrame --;
115
116     bool disable = effect.attribute("disable") == "1" && KdenliveSettings::disable_effect_parameters();
117     parent->setEnabled(!disable);
118
119     bool stretch = true;
120     m_vbox = new QVBoxLayout(parent);
121     m_vbox->setContentsMargins(4, 0, 4, 0);
122     m_vbox->setSpacing(2);
123
124     for (int i = 0; i < namenode.count() ; i++) {
125         QDomElement pa = namenode.item(i).toElement();
126         if (pa.tagName() != "parameter") continue;
127         QDomElement na = pa.firstChildElement("name");
128         QDomElement commentElem = pa.firstChildElement("comment");
129         QString type = pa.attribute("type");
130         QString paramName = na.isNull() ? pa.attribute("name") : i18n(na.text().toUtf8().data());
131         QString comment;
132         if (!commentElem.isNull())
133             comment = i18n(commentElem.text().toUtf8().data());
134         QWidget * toFillin = new QWidget(parent);
135         QString value = pa.attribute("value").isNull() ?
136                         pa.attribute("default") : pa.attribute("value");
137
138
139         /** See effects/README for info on the different types */
140
141         if (type == "double" || type == "constant") {
142             double min;
143             double max;
144             if (pa.attribute("min").contains('%'))
145                 min = ProfilesDialog::getStringEval(m_metaInfo->profile, pa.attribute("min"), m_metaInfo->frameSize);
146             else
147                 min = pa.attribute("min").toDouble();
148             if (pa.attribute("max").contains('%'))
149                 max = ProfilesDialog::getStringEval(m_metaInfo->profile, pa.attribute("max"), m_metaInfo->frameSize);
150             else
151                 max = pa.attribute("max").toDouble();
152
153             DoubleParameterWidget *doubleparam = new DoubleParameterWidget(paramName, value.toDouble(), min, max,
154                     pa.attribute("default").toDouble(), comment, -1, pa.attribute("suffix"), pa.attribute("decimals").toInt(), parent);
155             doubleparam->setFocusPolicy(Qt::StrongFocus);
156             m_vbox->addWidget(doubleparam);
157             m_valueItems[paramName] = doubleparam;
158             connect(doubleparam, SIGNAL(valueChanged(double)), this, SLOT(slotCollectAllParameters()));
159             connect(this, SIGNAL(showComments(bool)), doubleparam, SLOT(slotShowComment(bool)));
160         } else if (type == "list") {
161             Listval *lsval = new Listval;
162             lsval->setupUi(toFillin);
163             lsval->list->setFocusPolicy(Qt::StrongFocus);
164             QStringList listitems = pa.attribute("paramlist").split(';');
165             if (listitems.count() == 1) {
166                 // probably custom effect created before change to ';' as separator
167                 listitems = pa.attribute("paramlist").split(',');
168             }
169             QDomElement list = pa.firstChildElement("paramlistdisplay");
170             QStringList listitemsdisplay;
171             if (!list.isNull()) {
172                 listitemsdisplay = i18n(list.text().toUtf8().data()).split(',');
173             } else {
174                 listitemsdisplay = i18n(pa.attribute("paramlistdisplay").toUtf8().data()).split(',');
175             }
176             if (listitemsdisplay.count() != listitems.count())
177                 listitemsdisplay = listitems;
178             lsval->list->setIconSize(QSize(30, 30));
179             for (int i = 0; i < listitems.count(); i++) {
180                 lsval->list->addItem(listitemsdisplay.at(i), listitems.at(i));
181                 QString entry = listitems.at(i);
182                 if (!entry.isEmpty() && (entry.endsWith(".png") || entry.endsWith(".pgm"))) {
183                     if (!MainWindow::m_lumacache.contains(entry)) {
184                         QImage pix(entry);
185                         MainWindow::m_lumacache.insert(entry, pix.scaled(30, 30, Qt::KeepAspectRatio, Qt::SmoothTransformation));
186                     }
187                     lsval->list->setItemIcon(i, QPixmap::fromImage(MainWindow::m_lumacache.value(entry)));
188                 }
189             }
190             if (!value.isEmpty()) lsval->list->setCurrentIndex(listitems.indexOf(value));
191             lsval->name->setText(paramName);
192             lsval->labelComment->setText(comment);
193             lsval->widgetComment->setHidden(true);
194             m_valueItems[paramName] = lsval;
195             connect(lsval->list, SIGNAL(currentIndexChanged(int)) , this, SLOT(slotCollectAllParameters()));
196             if (!comment.isEmpty())
197                 connect(this, SIGNAL(showComments(bool)), lsval->widgetComment, SLOT(setVisible(bool)));
198             m_uiItems.append(lsval);
199         } else if (type == "bool") {
200             Boolval *bval = new Boolval;
201             bval->setupUi(toFillin);
202             bval->checkBox->setCheckState(value == "0" ? Qt::Unchecked : Qt::Checked);
203             bval->name->setText(paramName);
204             bval->labelComment->setText(comment);
205             bval->widgetComment->setHidden(true);
206             m_valueItems[paramName] = bval;
207             connect(bval->checkBox, SIGNAL(stateChanged(int)) , this, SLOT(slotCollectAllParameters()));
208             if (!comment.isEmpty())
209                 connect(this, SIGNAL(showComments(bool)), bval->widgetComment, SLOT(setVisible(bool)));
210             m_uiItems.append(bval);
211         } else if (type == "complex") {
212             ComplexParameter *pl = new ComplexParameter;
213             pl->setupParam(effect, pa.attribute("name"), 0, 100);
214             m_vbox->addWidget(pl);
215             m_valueItems[paramName+"complex"] = pl;
216             connect(pl, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
217         } else if (type == "geometry") {
218             if (KdenliveSettings::on_monitor_effects()) {
219                 m_needsMonitorEffectScene = true;
220                 m_geometryWidget = new GeometryWidget(m_metaInfo->monitor, m_metaInfo->timecode, 0, true, effect.hasAttribute("showrotation"), parent);
221                 m_geometryWidget->setFrameSize(m_metaInfo->frameSize);
222                 connect(m_geometryWidget, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
223                 if (minFrame == maxFrame)
224                     m_geometryWidget->setupParam(pa, m_in, m_out);
225                 else
226                     m_geometryWidget->setupParam(pa, minFrame, maxFrame);
227                 m_vbox->addWidget(m_geometryWidget);
228                 m_valueItems[paramName+"geometry"] = m_geometryWidget;
229                 connect(m_geometryWidget, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
230                 connect(m_geometryWidget, SIGNAL(importClipKeyframes()), this, SIGNAL(importClipKeyframes()));
231                 connect(this, SIGNAL(syncEffectsPos(int)), m_geometryWidget, SLOT(slotSyncPosition(int)));
232             } else {
233                 Geometryval *geo = new Geometryval(m_metaInfo->profile, m_metaInfo->timecode, m_metaInfo->frameSize, 0);
234                 if (minFrame == maxFrame)
235                     geo->setupParam(pa, m_in, m_out);
236                 else
237                     geo->setupParam(pa, minFrame, maxFrame);
238                 m_vbox->addWidget(geo);
239                 m_valueItems[paramName+"geometry"] = geo;
240                 connect(geo, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
241                 connect(geo, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
242                 connect(this, SIGNAL(syncEffectsPos(int)), geo, SLOT(slotSyncPosition(int)));
243             }
244         } else if (type == "addedgeometry") {
245             // this is a parameter that should be linked to the geometry widget, for example rotation, shear, ...
246             if (m_geometryWidget) m_geometryWidget->addParameter(pa);
247         } else if (type == "keyframe" || type == "simplekeyframe") {
248             // keyframe editor widget
249             if (m_keyframeEditor == NULL) {
250                 KeyframeEdit *geo;
251                 if (pa.attribute("widget") == "corners") {
252                     // we want a corners-keyframe-widget
253                     CornersWidget *corners = new CornersWidget(m_metaInfo->monitor, pa, m_in, m_out, m_metaInfo->timecode, e.attribute("active_keyframe", "-1").toInt(), parent);
254                     m_needsMonitorEffectScene = true;
255                     connect(this, SIGNAL(syncEffectsPos(int)), corners, SLOT(slotSyncPosition(int)));
256                     geo = static_cast<KeyframeEdit *>(corners);
257                 } else {
258                     geo = new KeyframeEdit(pa, m_in, m_out, m_metaInfo->timecode, e.attribute("active_keyframe", "-1").toInt());
259                 }
260                 m_vbox->addWidget(geo);
261                 m_valueItems[paramName+"keyframe"] = geo;
262                 m_keyframeEditor = geo;
263                 connect(geo, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
264                 connect(geo, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
265                 connect(this, SIGNAL(showComments(bool)), geo, SIGNAL(showComments(bool)));
266             } else {
267                 // we already have a keyframe editor, so just add another column for the new param
268                 m_keyframeEditor->addParameter(pa);
269             }
270         } else if (type == "color") {
271             if (pa.hasAttribute("paramprefix")) value.remove(0, pa.attribute("paramprefix").size());
272             if (value.startsWith('#'))
273                 value = value.replace('#', "0x");
274             ChooseColorWidget *choosecolor = new ChooseColorWidget(paramName, value, pa.hasAttribute("alpha"), parent);
275             m_vbox->addWidget(choosecolor);
276             m_valueItems[paramName] = choosecolor;
277             connect(choosecolor, SIGNAL(displayMessage(const QString&, int)), this, SIGNAL(displayMessage(const QString&, int)));
278             connect(choosecolor, SIGNAL(modified()) , this, SLOT(slotCollectAllParameters()));
279             connect(choosecolor, SIGNAL(disableCurrentFilter(bool)) , this, SIGNAL(disableCurrentFilter(bool)));
280         } else if (type == "position") {
281             int pos = value.toInt();
282             if (effect.attribute("id") == "fadein" || effect.attribute("id") == "fade_from_black") {
283                 pos = pos - m_in;
284             } else if (effect.attribute("id") == "fadeout" || effect.attribute("id") == "fade_to_black") {
285                 // fadeout position starts from clip end
286                 pos = m_out - pos;
287             }
288             PositionEdit *posedit = new PositionEdit(paramName, pos, 0, m_out - m_in, m_metaInfo->timecode);
289             m_vbox->addWidget(posedit);
290             m_valueItems[paramName+"position"] = posedit;
291             connect(posedit, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
292         } else if (type == "curve") {
293             KisCurveWidget *curve = new KisCurveWidget(parent);
294             curve->setMaxPoints(pa.attribute("max").toInt());
295             QList<QPointF> points;
296             int number;
297             if (e.attribute("version").toDouble() > 0.2) {
298                 // Rounding gives really weird results. (int) (10 * 0.3) gives 2! So for now, add 0.5 to get correct result
299                 number = EffectsList::parameter(e, pa.attribute("number")).toDouble() * 10 + 0.5;
300             } else {
301                 number = EffectsList::parameter(e, pa.attribute("number")).toInt();
302             }
303             QString inName = pa.attribute("inpoints");
304             QString outName = pa.attribute("outpoints");
305             int start = pa.attribute("min").toInt();
306             for (int j = start; j <= number; j++) {
307                 QString in = inName;
308                 in.replace("%i", QString::number(j));
309                 QString out = outName;
310                 out.replace("%i", QString::number(j));
311                 points << QPointF(EffectsList::parameter(e, in).toDouble(), EffectsList::parameter(e, out).toDouble());
312             }
313             if (!points.isEmpty())
314                 curve->setCurve(KisCubicCurve(points));
315             MySpinBox *spinin = new MySpinBox();
316             spinin->setRange(0, 1000);
317             MySpinBox *spinout = new MySpinBox();
318             spinout->setRange(0, 1000);
319             curve->setupInOutControls(spinin, spinout, 0, 1000);
320             m_vbox->addWidget(curve);
321             m_vbox->addWidget(spinin);
322             m_vbox->addWidget(spinout);
323
324             connect(curve, SIGNAL(modified()), this, SLOT(slotCollectAllParameters()));
325             m_valueItems[paramName] = curve;
326
327             QString depends = pa.attribute("depends");
328             if (!depends.isEmpty())
329                 meetDependency(paramName, type, EffectsList::parameter(e, depends));
330         } else if (type == "bezier_spline") {
331             BezierSplineWidget *widget = new BezierSplineWidget(value, parent);
332             stretch = false;
333             m_vbox->addWidget(widget);
334             m_valueItems[paramName] = widget;
335             connect(widget, SIGNAL(modified()), this, SLOT(slotCollectAllParameters()));
336             QString depends = pa.attribute("depends");
337             if (!depends.isEmpty())
338                 meetDependency(paramName, type, EffectsList::parameter(e, depends));
339 #ifdef USE_QJSON
340         } else if (type == "roto-spline") {
341             m_needsMonitorEffectScene = true;
342             RotoWidget *roto = new RotoWidget(value, m_metaInfo->monitor, info, m_metaInfo->timecode, parent);
343             connect(roto, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
344             connect(roto, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
345             connect(this, SIGNAL(syncEffectsPos(int)), roto, SLOT(slotSyncPosition(int)));
346             m_vbox->addWidget(roto);
347             m_valueItems[paramName] = roto;
348 #endif
349         } else if (type == "wipe") {
350             Wipeval *wpval = new Wipeval;
351             wpval->setupUi(toFillin);
352             wipeInfo w = getWipeInfo(value);
353             switch (w.start) {
354             case UP:
355                 wpval->start_up->setChecked(true);
356                 break;
357             case DOWN:
358                 wpval->start_down->setChecked(true);
359                 break;
360             case RIGHT:
361                 wpval->start_right->setChecked(true);
362                 break;
363             case LEFT:
364                 wpval->start_left->setChecked(true);
365                 break;
366             default:
367                 wpval->start_center->setChecked(true);
368                 break;
369             }
370             switch (w.end) {
371             case UP:
372                 wpval->end_up->setChecked(true);
373                 break;
374             case DOWN:
375                 wpval->end_down->setChecked(true);
376                 break;
377             case RIGHT:
378                 wpval->end_right->setChecked(true);
379                 break;
380             case LEFT:
381                 wpval->end_left->setChecked(true);
382                 break;
383             default:
384                 wpval->end_center->setChecked(true);
385                 break;
386             }
387             wpval->start_transp->setValue(w.startTransparency);
388             wpval->end_transp->setValue(w.endTransparency);
389             m_valueItems[paramName] = wpval;
390             connect(wpval->end_up, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
391             connect(wpval->end_down, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
392             connect(wpval->end_left, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
393             connect(wpval->end_right, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
394             connect(wpval->end_center, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
395             connect(wpval->start_up, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
396             connect(wpval->start_down, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
397             connect(wpval->start_left, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
398             connect(wpval->start_right, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
399             connect(wpval->start_center, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
400             connect(wpval->start_transp, SIGNAL(valueChanged(int)), this, SLOT(slotCollectAllParameters()));
401             connect(wpval->end_transp, SIGNAL(valueChanged(int)), this, SLOT(slotCollectAllParameters()));
402             //wpval->title->setTitle(na.toElement().text());
403             m_uiItems.append(wpval);
404         } else if (type == "url") {
405             Urlval *cval = new Urlval;
406             cval->setupUi(toFillin);
407             cval->label->setText(paramName);
408             cval->urlwidget->fileDialog()->setFilter(ProjectList::getExtensions());
409             m_valueItems[paramName] = cval;
410             cval->urlwidget->setUrl(KUrl(value));
411             connect(cval->urlwidget, SIGNAL(returnPressed()) , this, SLOT(slotCollectAllParameters()));
412             connect(cval->urlwidget, SIGNAL(urlSelected(const KUrl&)) , this, SLOT(slotCollectAllParameters()));
413             m_uiItems.append(cval);
414         } else if (type == "keywords") {
415             Keywordval* kval = new Keywordval;
416             kval->setupUi(toFillin);
417             kval->label->setText(paramName);
418             kval->lineeditwidget->setText(value);
419             QDomElement klistelem = pa.firstChildElement("keywords");
420             QDomElement kdisplaylistelem = pa.firstChildElement("keywordsdisplay");
421             QStringList keywordlist;
422             QStringList keyworddisplaylist;
423             if (!klistelem.isNull()) {
424                 keywordlist = klistelem.text().split(';');
425                 keyworddisplaylist = i18n(kdisplaylistelem.text().toUtf8().data()).split(';');
426             }
427             if (keyworddisplaylist.count() != keywordlist.count()) {
428                 keyworddisplaylist = keywordlist;
429             }
430             for (int i = 0; i < keywordlist.count(); i++) {
431                 kval->comboboxwidget->addItem(keyworddisplaylist.at(i), keywordlist.at(i));
432             }
433             // Add disabled user prompt at index 0
434             kval->comboboxwidget->insertItem(0, i18n("<select a keyword>"), "");
435             kval->comboboxwidget->model()->setData( kval->comboboxwidget->model()->index(0,0), QVariant(Qt::NoItemFlags), Qt::UserRole -1);
436             kval->comboboxwidget->setCurrentIndex(0);
437             m_valueItems[paramName] = kval;
438             connect(kval->lineeditwidget, SIGNAL(editingFinished()) , this, SLOT(collectAllParameters()));
439             connect(kval->comboboxwidget, SIGNAL(activated (const QString&)), this, SLOT(collectAllParameters()));
440             m_uiItems.append(kval);
441         } else if (type == "fontfamily") {
442             Fontval* fval = new Fontval;
443             fval->setupUi(toFillin);
444             fval->name->setText(paramName);
445             fval->fontfamilywidget->setCurrentFont(QFont(value));
446             m_valueItems[paramName] = fval;
447             connect(fval->fontfamilywidget, SIGNAL(currentFontChanged(const QFont &)), this, SLOT(collectAllParameters())) ;
448             m_uiItems.append(fval);
449         } else if (type == "filterjob") {
450             QVBoxLayout *l= new QVBoxLayout(toFillin);
451             QPushButton *button = new QPushButton(paramName, toFillin);
452             l->addWidget(button);
453             m_valueItems[paramName] = button;
454             connect(button, SIGNAL(pressed()), this, SLOT(slotStartFilterJobAction()));   
455         } else {
456             delete toFillin;
457             toFillin = NULL;
458         }
459
460         if (toFillin)
461             m_vbox->addWidget(toFillin);
462     }
463
464     if (stretch)
465         m_vbox->addStretch();
466
467     if (m_keyframeEditor)
468         m_keyframeEditor->checkVisibleParam();
469
470     // Make sure all doubleparam spinboxes have the same width, looks much better
471     QList<DoubleParameterWidget *> allWidgets = findChildren<DoubleParameterWidget *>();
472     int minSize = 0;
473     for (int i = 0; i < allWidgets.count(); i++) {
474         if (minSize < allWidgets.at(i)->spinSize()) minSize = allWidgets.at(i)->spinSize();
475     }
476     for (int i = 0; i < allWidgets.count(); i++) {
477         allWidgets.at(i)->setSpinSize(minSize);
478     }
479 }
480
481 ParameterContainer::~ParameterContainer()
482 {
483     clearLayout(m_vbox);
484     delete m_vbox;
485 }
486
487 void ParameterContainer::meetDependency(const QString& name, QString type, QString value)
488 {
489     if (type == "curve") {
490         KisCurveWidget *curve = (KisCurveWidget*)m_valueItems[name];
491         if (curve) {
492             int color = value.toInt();
493             curve->setPixmap(QPixmap::fromImage(ColorTools::rgbCurvePlane(curve->size(), (ColorTools::ColorsRGB)(color == 3 ? 4 : color), 0.8)));
494         }
495     } else if (type == "bezier_spline") {
496         BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems[name];
497         if (widget) {
498             widget->setMode((BezierSplineWidget::CurveModes)((int)(value.toDouble() * 10 + 0.5)));
499         }
500     }
501 }
502
503 wipeInfo ParameterContainer::getWipeInfo(QString value)
504 {
505     wipeInfo info;
506     // Convert old geometry values that used a comma as separator
507     if (value.contains(',')) value.replace(',','/');
508     QString start = value.section(';', 0, 0);
509     QString end = value.section(';', 1, 1).section('=', 1, 1);
510     if (start.startsWith("-100%/0"))
511         info.start = LEFT;
512     else if (start.startsWith("100%/0"))
513         info.start = RIGHT;
514     else if (start.startsWith("0%/100%"))
515         info.start = DOWN;
516     else if (start.startsWith("0%/-100%"))
517         info.start = UP;
518     else
519         info.start = CENTER;
520
521     if (start.count(':') == 2)
522         info.startTransparency = start.section(':', -1).toInt();
523     else
524         info.startTransparency = 100;
525
526     if (end.startsWith("-100%/0"))
527         info.end = LEFT;
528     else if (end.startsWith("100%/0"))
529         info.end = RIGHT;
530     else if (end.startsWith("0%/100%"))
531         info.end = DOWN;
532     else if (end.startsWith("0%/-100%"))
533         info.end = UP;
534     else
535         info.end = CENTER;
536
537     if (end.count(':') == 2)
538         info.endTransparency = end.section(':', -1).toInt();
539     else
540         info.endTransparency = 100;
541
542     return info;
543 }
544
545 void ParameterContainer::updateTimecodeFormat()
546 {
547     if (m_keyframeEditor)
548         m_keyframeEditor->updateTimecodeFormat();
549
550     QDomNodeList namenode = m_effect.elementsByTagName("parameter");
551     for (int i = 0; i < namenode.count() ; i++) {
552         QDomNode pa = namenode.item(i);
553         QDomElement na = pa.firstChildElement("name");
554         QString type = pa.attributes().namedItem("type").nodeValue();
555         QString paramName = na.isNull() ? pa.attributes().namedItem("name").nodeValue() : i18n(na.text().toUtf8().data());
556
557         if (type == "geometry") {
558             if (KdenliveSettings::on_monitor_effects()) {
559                 if (m_geometryWidget) m_geometryWidget->updateTimecodeFormat();
560             } else {
561                 Geometryval *geom = ((Geometryval*)m_valueItems[paramName+"geometry"]);
562                 geom->updateTimecodeFormat();
563             }
564             break;
565         } else if (type == "position") {
566             PositionEdit *posi = ((PositionEdit*)m_valueItems[paramName+"position"]);
567             posi->updateTimecodeFormat();
568             break;
569 #ifdef USE_QJSON
570         } else if (type == "roto-spline") {
571             RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems[paramName]);
572             widget->updateTimecodeFormat();
573 #endif
574         }
575     }
576 }
577
578 void ParameterContainer::slotCollectAllParameters()
579 {
580     if (m_valueItems.isEmpty() || m_effect.isNull()) return;
581     QLocale locale;
582     locale.setNumberOptions(QLocale::OmitGroupSeparator);
583     const QDomElement oldparam = m_effect.cloneNode().toElement();
584     //QDomElement newparam = oldparam.cloneNode().toElement();
585     QDomNodeList namenode = m_effect.elementsByTagName("parameter");
586
587     for (int i = 0; i < namenode.count() ; i++) {
588         QDomElement pa = namenode.item(i).toElement();
589         QDomElement na = pa.firstChildElement("name");
590         QString type = pa.attribute("type");
591         QString paramName = na.isNull() ? pa.attribute("name") : i18n(na.text().toUtf8().data());
592         if (type == "complex")
593             paramName.append("complex");
594         else if (type == "position")
595             paramName.append("position");
596         else if (type == "geometry")
597             paramName.append("geometry");
598         else if (type == "keyframe")
599             paramName.append("keyframe");
600         if (type != "simplekeyframe" && type != "fixed" && type != "addedgeometry" && !m_valueItems.contains(paramName)) {
601             kDebug() << "// Param: " << paramName << " NOT FOUND";
602             continue;
603         }
604
605         QString setValue;
606         if (type == "double" || type == "constant") {
607             DoubleParameterWidget *doubleparam = (DoubleParameterWidget*)m_valueItems.value(paramName);
608             setValue = locale.toString(doubleparam->getValue());
609         } else if (type == "list") {
610             KComboBox *box = ((Listval*)m_valueItems.value(paramName))->list;
611             setValue = box->itemData(box->currentIndex()).toString();
612         } else if (type == "bool") {
613             QCheckBox *box = ((Boolval*)m_valueItems.value(paramName))->checkBox;
614             setValue = box->checkState() == Qt::Checked ? "1" : "0" ;
615         } else if (type == "color") {
616             ChooseColorWidget *choosecolor = ((ChooseColorWidget*)m_valueItems.value(paramName));
617             setValue = choosecolor->getColor();
618             if (pa.hasAttribute("paramprefix")) setValue.prepend(pa.attribute("paramprefix"));
619         } else if (type == "complex") {
620             ComplexParameter *complex = ((ComplexParameter*)m_valueItems.value(paramName));
621             namenode.item(i) = complex->getParamDesc();
622         } else if (type == "geometry") {
623             if (KdenliveSettings::on_monitor_effects()) {
624                 if (m_geometryWidget) namenode.item(i).toElement().setAttribute("value", m_geometryWidget->getValue());
625             } else {
626                 Geometryval *geom = ((Geometryval*)m_valueItems.value(paramName));
627                 namenode.item(i).toElement().setAttribute("value", geom->getValue());
628             }
629         } else if (type == "addedgeometry") {
630             namenode.item(i).toElement().setAttribute("value", m_geometryWidget->getExtraValue(namenode.item(i).toElement().attribute("name")));
631         } else if (type == "position") {
632             PositionEdit *pedit = ((PositionEdit*)m_valueItems.value(paramName));
633             int pos = pedit->getPosition();
634             setValue = QString::number(pos);
635             if (m_effect.attribute("id") == "fadein" || m_effect.attribute("id") == "fade_from_black") {
636                 // Make sure duration is not longer than clip
637                 /*if (pos > m_out) {
638                     pos = m_out;
639                     pedit->setPosition(pos);
640                 }*/
641                 EffectsList::setParameter(m_effect, "in", QString::number(m_in));
642                 EffectsList::setParameter(m_effect, "out", QString::number(m_in + pos));
643                 setValue.clear();
644             } else if (m_effect.attribute("id") == "fadeout" || m_effect.attribute("id") == "fade_to_black") {
645                 // Make sure duration is not longer than clip
646                 /*if (pos > m_out) {
647                     pos = m_out;
648                     pedit->setPosition(pos);
649                 }*/
650                 EffectsList::setParameter(m_effect, "in", QString::number(m_out - pos));
651                 EffectsList::setParameter(m_effect, "out", QString::number(m_out));
652                 setValue.clear();
653             }
654         } else if (type == "curve") {
655             KisCurveWidget *curve = ((KisCurveWidget*)m_valueItems.value(paramName));
656             QList<QPointF> points = curve->curve().points();
657             QString number = pa.attribute("number");
658             QString inName = pa.attribute("inpoints");
659             QString outName = pa.attribute("outpoints");
660             int off = pa.attribute("min").toInt();
661             int end = pa.attribute("max").toInt();
662             if (oldparam.attribute("version").toDouble() > 0.2) {
663                 EffectsList::setParameter(m_effect, number, locale.toString(points.count() / 10.));
664             } else {
665                 EffectsList::setParameter(m_effect, number, QString::number(points.count()));
666             }
667             for (int j = 0; (j < points.count() && j + off <= end); j++) {
668                 QString in = inName;
669                 in.replace("%i", QString::number(j + off));
670                 QString out = outName;
671                 out.replace("%i", QString::number(j + off));
672                 EffectsList::setParameter(m_effect, in, locale.toString(points.at(j).x()));
673                 EffectsList::setParameter(m_effect, out, locale.toString(points.at(j).y()));
674             }
675             QString depends = pa.attribute("depends");
676             if (!depends.isEmpty())
677                 meetDependency(paramName, type, EffectsList::parameter(m_effect, depends));
678         } else if (type == "bezier_spline") {
679             BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems.value(paramName);
680             setValue = widget->spline();
681             QString depends = pa.attribute("depends");
682             if (!depends.isEmpty())
683                 meetDependency(paramName, type, EffectsList::parameter(m_effect, depends));
684 #ifdef USE_QJSON
685         } else if (type == "roto-spline") {
686             RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems.value(paramName));
687             setValue = widget->getSpline();
688 #endif
689         } else if (type == "wipe") {
690             Wipeval *wp = (Wipeval*)m_valueItems.value(paramName);
691             wipeInfo info;
692             if (wp->start_left->isChecked())
693                 info.start = LEFT;
694             else if (wp->start_right->isChecked())
695                 info.start = RIGHT;
696             else if (wp->start_up->isChecked())
697                 info.start = UP;
698             else if (wp->start_down->isChecked())
699                 info.start = DOWN;
700             else if (wp->start_center->isChecked())
701                 info.start = CENTER;
702             else
703                 info.start = LEFT;
704             info.startTransparency = wp->start_transp->value();
705
706             if (wp->end_left->isChecked())
707                 info.end = LEFT;
708             else if (wp->end_right->isChecked())
709                 info.end = RIGHT;
710             else if (wp->end_up->isChecked())
711                 info.end = UP;
712             else if (wp->end_down->isChecked())
713                 info.end = DOWN;
714             else if (wp->end_center->isChecked())
715                 info.end = CENTER;
716             else
717                 info.end = RIGHT;
718             info.endTransparency = wp->end_transp->value();
719
720             setValue = getWipeString(info);
721         } else if ((type == "simplekeyframe" || type == "keyframe") && m_keyframeEditor) {
722             QString realName = i18n(na.toElement().text().toUtf8().data());
723             QString val = m_keyframeEditor->getValue(realName);
724             pa.setAttribute("keyframes", val);
725
726             if (m_keyframeEditor->isVisibleParam(realName)) {
727                 pa.setAttribute("intimeline", "1");
728             }
729             else if (pa.hasAttribute("intimeline"))
730                 pa.removeAttribute("intimeline");
731         } else if (type == "url") {
732             KUrlRequester *req = ((Urlval*)m_valueItems.value(paramName))->urlwidget;
733             setValue = req->url().path();
734         } else if (type == "keywords"){
735             QLineEdit *line = ((Keywordval*)m_valueItems.value(paramName))->lineeditwidget;
736             QComboBox *combo = ((Keywordval*)m_valueItems.value(paramName))->comboboxwidget;
737             if(combo->currentIndex())
738             {
739                 QString comboval = combo->itemData(combo->currentIndex()).toString();
740                 line->insert(comboval);
741                 combo->setCurrentIndex(0);
742             }
743             setValue = line->text();
744         } else if (type == "fontfamily") {
745             QFontComboBox* fontfamily = ((Fontval*)m_valueItems.value(paramName))->fontfamilywidget;
746             setValue = fontfamily->currentFont().family();
747         }
748         if (!setValue.isNull())
749             pa.setAttribute("value", setValue);
750
751     }
752     emit parameterChanged(oldparam, m_effect, m_effect.attribute("kdenlive_ix").toInt());
753 }
754
755 QString ParameterContainer::getWipeString(wipeInfo info)
756 {
757
758     QString start;
759     QString end;
760     switch (info.start) {
761     case LEFT:
762         start = "-100%/0%:100%x100%";
763         break;
764     case RIGHT:
765         start = "100%/0%:100%x100%";
766         break;
767     case DOWN:
768         start = "0%/100%:100%x100%";
769         break;
770     case UP:
771         start = "0%/-100%:100%x100%";
772         break;
773     default:
774         start = "0%/0%:100%x100%";
775         break;
776     }
777     start.append(':' + QString::number(info.startTransparency));
778
779     switch (info.end) {
780     case LEFT:
781         end = "-100%/0%:100%x100%";
782         break;
783     case RIGHT:
784         end = "100%/0%:100%x100%";
785         break;
786     case DOWN:
787         end = "0%/100%:100%x100%";
788         break;
789     case UP:
790         end = "0%/-100%:100%x100%";
791         break;
792     default:
793         end = "0%/0%:100%x100%";
794         break;
795     }
796     end.append(':' + QString::number(info.endTransparency));
797     return QString(start + ";-1=" + end);
798 }
799
800 void ParameterContainer::updateParameter(const QString &key, const QString &value)
801 {
802     m_effect.setAttribute(key, value);
803 }
804
805 void ParameterContainer::slotStartFilterJobAction()
806 {
807     QDomNodeList namenode = m_effect.elementsByTagName("parameter");
808     for (int i = 0; i < namenode.count() ; i++) {
809         QDomElement pa = namenode.item(i).toElement();
810         QString type = pa.attribute("type");
811         if (type == "filterjob") {
812             QString filterparams = pa.attribute("filterparams");
813             if (filterparams.contains("%params")) {
814                 // Replace with current geometry
815                 EffectsParameterList parameters;
816                 QDomNodeList params = m_effect.elementsByTagName("parameter");
817                 CustomTrackView::adjustEffectParameters(parameters, params, m_metaInfo->profile);
818                 QString paramData;
819                 for (int j = 0; j < parameters.count(); j++)
820                     paramData.append(parameters.at(j).name()+"="+parameters.at(j).value()+" ");
821                 filterparams.replace("%params", paramData);
822             }
823             QMap <QString, QString> extraParams;
824             QDomNodeList jobparams = pa.elementsByTagName("jobparam");
825             for (int j = 0; j < jobparams.count(); j++) {
826                 QDomElement e = jobparams.item(j).toElement();
827                 extraParams.insert(e.attribute("name"), e.text().toUtf8());
828             }
829             emit startFilterJob(pa.attribute("filtertag"), filterparams, pa.attribute("consumer"), pa.attribute("consumerparams"), extraParams);
830             kDebug()<<" - - -PROPS:\n"<<pa.attribute("filtertag")<<"-"<< filterparams<<"-"<< pa.attribute("consumer")<<"-"<< pa.attribute("consumerparams")<<"-"<< pa.attribute("extraparams");
831             break;
832         }
833     }
834 }
835
836
837 void ParameterContainer::clearLayout(QLayout *layout)
838 {
839     QLayoutItem *item;
840     while((item = layout->takeAt(0))) {
841         if (item->layout()) {
842             clearLayout(item->layout());
843             delete item->layout();
844         }
845         if (item->widget()) {
846             delete item->widget();
847         }
848         delete item;
849     }
850 }
851
852 bool ParameterContainer::needsMonitorEffectScene() const
853 {
854     return m_needsMonitorEffectScene;
855 }
856
857 void ParameterContainer::setKeyframes(const QString &data, int maximum)
858 {
859     if (!m_geometryWidget) {
860         kDebug()<<" / / NO GEOMETRY WIDGET FOUND FOR IMPORTING DATA";
861         return;
862     }
863     m_geometryWidget->importKeyframes(data, maximum);
864     
865 }
866