1 /***************************************************************************
2 * Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
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. *
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. *
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 ***************************************************************************/
21 #include "collapsibleeffect.h"
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 "complexparameter.h"
28 #include "geometryval.h"
29 #include "positionedit.h"
30 #include "projectlist.h"
31 #include "effectslist.h"
32 #include "kdenlivesettings.h"
33 #include "profilesdialog.h"
34 #include "kis_curve_widget.h"
35 #include "kis_cubic_curve.h"
36 #include "choosecolorwidget.h"
37 #include "geometrywidget.h"
38 #include "colortools.h"
39 #include "doubleparameterwidget.h"
40 #include "cornerswidget.h"
41 #include "dragvalue.h"
42 #include "beziercurve/beziersplinewidget.h"
44 #include "rotoscoping/rotowidget.h"
49 #include <QVBoxLayout>
51 #include <KGlobalSettings>
53 #include <KFileDialog>
54 #include <KUrlRequester>
56 class Boolval: public QWidget, public Ui::Boolval_UI
60 class Listval: public QWidget, public Ui::Listval_UI
64 class Wipeval: public QWidget, public Ui::Wipeval_UI
68 class Urlval: public QWidget, public Ui::Urlval_UI
72 QMap<QString, QImage> CollapsibleEffect::iconCache;
74 void clearLayout(QLayout *layout)
77 while((item = layout->takeAt(0))) {
79 clearLayout(item->layout());
80 delete item->layout();
83 delete item->widget();
89 MySpinBox::MySpinBox(QWidget * parent):
92 setFocusPolicy(Qt::StrongFocus);
95 void MySpinBox::focusInEvent(QFocusEvent*)
97 setFocusPolicy(Qt::WheelFocus);
100 void MySpinBox::focusOutEvent(QFocusEvent*)
102 setFocusPolicy(Qt::StrongFocus);
105 CollapsibleEffect::CollapsibleEffect(QDomElement effect, QDomElement original_effect, ItemInfo info, int ix, EffectMetaInfo *metaInfo, bool lastEffect, QWidget * parent) :
109 m_original_effect(original_effect),
110 m_lastEffect(lastEffect),
113 //setMouseTracking(true);
115 frame->setBackgroundRole(QPalette::Midlight);
116 frame->setAutoFillBackground(true);
117 setFont(KGlobalSettings::smallestReadableFont());
118 QDomElement namenode = m_effect.firstChildElement("name");
119 if (namenode.isNull()) return;
120 QString type = m_effect.attribute("type", QString());
122 if (type == "audio") icon = KIcon("kdenlive-show-audio");
123 else if (m_effect.attribute("tag") == "region") icon = KIcon("kdenlive-mask-effect");
124 else if (type == "custom") icon = KIcon("kdenlive-custom-effect");
125 else icon = KIcon("kdenlive-show-video");
127 buttonUp->setIcon(KIcon("go-up"));
128 buttonUp->setToolTip(i18n("Move effect up"));
130 buttonDown->setIcon(KIcon("go-down"));
131 buttonDown->setToolTip(i18n("Move effect down"));
133 buttonDel->setIcon(KIcon("edit-delete"));
134 buttonDel->setToolTip(i18n("Delete effect"));
135 buttonSave->setIcon(KIcon("document-save"));
136 buttonSave->setToolTip(i18n("Save effect"));
138 buttonUp->setVisible(false);
139 buttonDown->setVisible(false);
140 buttonSave->setVisible(false);
141 buttonDel->setVisible(false);
143 /*buttonReset->setIcon(KIcon("view-refresh"));
144 buttonReset->setToolTip(i18n("Reset effect"));*/
145 //checkAll->setToolTip(i18n("Enable/Disable all effects"));
146 //buttonShowComments->setIcon(KIcon("help-about"));
147 //buttonShowComments->setToolTip(i18n("Show additional information for the parameters"));
149 title->setText(i18n(namenode.text().toUtf8().data()));
150 effectIcon->setPixmap(icon.pixmap(QSize(16,16)));
152 //QLabel *lab = new QLabel("HEllo", widgetFrame);
153 //m_vbox->addWidget(lab);
154 if (m_effect.attribute("disable") == "1") {
155 enabledBox->setCheckState(Qt::Unchecked);
156 title->setEnabled(false);
159 enabledBox->setCheckState(Qt::Checked);
162 connect(collapseButton, SIGNAL(clicked()), this, SLOT(slotSwitch()));
163 connect(enabledBox, SIGNAL(toggled(bool)), this, SLOT(slotEnable(bool)));
164 connect(buttonUp, SIGNAL(clicked()), this, SLOT(slotEffectUp()));
165 connect(buttonDown, SIGNAL(clicked()), this, SLOT(slotEffectDown()));
166 connect(buttonDel, SIGNAL(clicked()), this, SLOT(slotDeleteEffect()));
167 setupWidget(info, ix, metaInfo);
168 Q_FOREACH( QSpinBox * sp, findChildren<QSpinBox*>() ) {
169 sp->installEventFilter( this );
170 sp->setFocusPolicy( Qt::StrongFocus );
172 Q_FOREACH( KComboBox * cb, findChildren<KComboBox*>() ) {
173 cb->installEventFilter( this );
174 cb->setFocusPolicy( Qt::StrongFocus );
176 Q_FOREACH( QProgressBar * cb, findChildren<QProgressBar*>() ) {
177 cb->installEventFilter( this );
178 cb->setFocusPolicy( Qt::StrongFocus );
183 CollapsibleEffect::~CollapsibleEffect()
185 if (m_paramWidget) delete m_paramWidget;
188 bool CollapsibleEffect::eventFilter( QObject * o, QEvent * e )
190 if(e->type() == QEvent::Wheel) {
191 if (qobject_cast<QAbstractSpinBox*>(o)) {
192 if(qobject_cast<QAbstractSpinBox*>(o)->focusPolicy() == Qt::WheelFocus)
203 if (qobject_cast<KComboBox*>(o)) {
204 if(qobject_cast<KComboBox*>(o)->focusPolicy() == Qt::WheelFocus)
215 if (qobject_cast<QProgressBar*>(o)) {
216 if(qobject_cast<QProgressBar*>(o)->focusPolicy() == Qt::WheelFocus)
228 return QWidget::eventFilter(o, e);
232 void CollapsibleEffect::setActive(bool activate)
235 frame->setBackgroundRole(m_active ? QPalette::Mid : QPalette::Midlight);
236 frame->setAutoFillBackground(activate);
239 void CollapsibleEffect::mouseDoubleClickEvent ( QMouseEvent * event )
241 if (frame->underMouse() && collapseButton->isEnabled()) slotSwitch();
242 QWidget::mouseDoubleClickEvent(event);
245 void CollapsibleEffect::mousePressEvent ( QMouseEvent *event )
247 if (!m_active) emit activateEffect(m_paramWidget->index());
248 QWidget::mousePressEvent(event);
251 void CollapsibleEffect::enterEvent ( QEvent * event )
253 if (m_paramWidget->index() > 0) buttonUp->setVisible(true);
254 if (!m_lastEffect) buttonDown->setVisible(true);
255 buttonSave->setVisible(true);
256 buttonDel->setVisible(true);
257 if (!m_active) frame->setBackgroundRole(QPalette::Midlight);
258 frame->setAutoFillBackground(true);
259 QWidget::enterEvent(event);
262 void CollapsibleEffect::leaveEvent ( QEvent * event )
264 buttonUp->setVisible(false);
265 buttonDown->setVisible(false);
266 buttonSave->setVisible(false);
267 buttonDel->setVisible(false);
268 if (!m_active) frame->setAutoFillBackground(false);
269 QWidget::leaveEvent(event);
272 void CollapsibleEffect::slotEnable(bool enable)
274 title->setEnabled(enable);
275 m_effect.setAttribute("disable", enable ? 0 : 1);
276 if (enable || KdenliveSettings::disable_effect_parameters()) {
277 widgetFrame->setEnabled(enable);
279 emit effectStateChanged(!enable, m_paramWidget->index());
282 void CollapsibleEffect::slotDeleteEffect()
284 emit deleteEffect(m_effect, m_paramWidget->index());
287 void CollapsibleEffect::slotEffectUp()
289 emit changeEffectPosition(m_paramWidget->index(), true);
292 void CollapsibleEffect::slotEffectDown()
294 emit changeEffectPosition(m_paramWidget->index(), false);
297 void CollapsibleEffect::slotSwitch()
299 bool enable = !widgetFrame->isVisible();
303 void CollapsibleEffect::slotShow(bool show)
305 widgetFrame->setVisible(show);
307 collapseButton->setArrowType(Qt::DownArrow);
308 m_original_effect.removeAttribute("k_collapsed");
311 collapseButton->setArrowType(Qt::RightArrow);
312 m_original_effect.setAttribute("k_collapsed", 1);
317 void CollapsibleEffect::setupWidget(ItemInfo info, int index, EffectMetaInfo *metaInfo)
319 if (m_effect.isNull()) {
320 // kDebug() << "// EMPTY EFFECT STACK";
323 if (m_effect.attribute("tag") == "region") {
324 QVBoxLayout *vbox = new QVBoxLayout(widgetFrame);
325 vbox->setContentsMargins(0, 0, 0, 0);
327 QDomNodeList effects = m_effect.elementsByTagName("effect");
328 QDomNodeList origin_effects = m_original_effect.elementsByTagName("effect");
329 QWidget *container = new QWidget(widgetFrame);
330 vbox->addWidget(container);
331 m_paramWidget = new ParameterContainer(m_effect.toElement(), info, metaInfo, index, container);
332 for (int i = 0; i < effects.count(); i++) {
333 CollapsibleEffect *coll = new CollapsibleEffect(effects.at(i).toElement(), origin_effects.at(i).toElement(), info, i, metaInfo, container);
334 m_subParamWidgets.append(coll);
335 //container = new QWidget(widgetFrame);
336 vbox->addWidget(coll);
337 //p = new ParameterContainer(effects.at(i).toElement(), info, isEffect, container);
342 m_paramWidget = new ParameterContainer(m_effect, info, metaInfo, index, widgetFrame);
343 if (m_effect.firstChildElement("parameter").isNull()) {
344 // Effect has no parameter, don't allow expand
345 collapseButton->setEnabled(false);
346 widgetFrame->setVisible(false);
349 if (collapseButton->isEnabled()) slotShow(!m_effect.hasAttribute("k_collapsed"));
350 connect (m_paramWidget, SIGNAL(parameterChanged(const QDomElement, const QDomElement, int)), this, SIGNAL(parameterChanged(const QDomElement, const QDomElement, int)));
351 connect (this, SIGNAL(syncEffectsPos(int)), m_paramWidget, SIGNAL(syncEffectsPos(int)));
352 connect (this, SIGNAL(effectStateChanged(bool)), m_paramWidget, SIGNAL(effectStateChanged(bool)));
353 connect (m_paramWidget, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
354 connect (m_paramWidget, SIGNAL(seekTimeline(int)), this, SIGNAL(seekTimeline(int)));
359 void CollapsibleEffect::updateTimecodeFormat()
361 m_paramWidget->updateTimecodeFormat();
362 if (!m_subParamWidgets.isEmpty()) {
364 for (int i = 0; i < m_subParamWidgets.count(); i++)
365 m_subParamWidgets.at(i)->updateTimecodeFormat();
369 void CollapsibleEffect::slotSyncEffectsPos(int pos)
371 emit syncEffectsPos(pos);
376 ParameterContainer::ParameterContainer(QDomElement effect, ItemInfo info, EffectMetaInfo *metaInfo, int index, QWidget * parent) :
378 m_keyframeEditor(NULL),
379 m_geometryWidget(NULL),
380 m_metaInfo(metaInfo),
383 m_in = info.cropStart.frames(KdenliveSettings::project_fps());
384 m_out = (info.cropStart + info.cropDuration).frames(KdenliveSettings::project_fps()) - 1;
386 QDomNodeList namenode = effect.childNodes(); //elementsByTagName("parameter");
388 QDomElement e = effect.toElement();
389 int minFrame = e.attribute("start").toInt();
390 int maxFrame = e.attribute("end").toInt();
391 // In transitions, maxFrame is in fact one frame after the end of transition
392 if (maxFrame > 0) maxFrame --;
394 bool disable = effect.attribute("disable") == "1" && KdenliveSettings::disable_effect_parameters();
395 parent->setEnabled(!disable);
398 m_vbox = new QVBoxLayout(parent);
399 m_vbox->setContentsMargins(0, 0, 0, 0);
400 m_vbox->setSpacing(2);
402 for (int i = 0; i < namenode.count() ; i++) {
403 QDomElement pa = namenode.item(i).toElement();
404 if (pa.tagName() != "parameter") continue;
405 QDomElement na = pa.firstChildElement("name");
406 QDomElement commentElem = pa.firstChildElement("comment");
407 QString type = pa.attribute("type");
408 QString paramName = na.isNull() ? pa.attribute("name") : i18n(na.text().toUtf8().data());
410 if (!commentElem.isNull())
411 comment = i18n(commentElem.text().toUtf8().data());
412 QWidget * toFillin = new QWidget(parent);
413 QString value = pa.attribute("value").isNull() ?
414 pa.attribute("default") : pa.attribute("value");
417 /** See effects/README for info on the different types */
419 if (type == "double" || type == "constant") {
422 if (pa.attribute("min").contains('%'))
423 min = ProfilesDialog::getStringEval(m_metaInfo->profile, pa.attribute("min"), m_metaInfo->frameSize);
425 min = pa.attribute("min").toDouble();
426 if (pa.attribute("max").contains('%'))
427 max = ProfilesDialog::getStringEval(m_metaInfo->profile, pa.attribute("max"), m_metaInfo->frameSize);
429 max = pa.attribute("max").toDouble();
431 DoubleParameterWidget *doubleparam = new DoubleParameterWidget(paramName, value.toDouble(), min, max,
432 pa.attribute("default").toDouble(), comment, -1, pa.attribute("suffix"), pa.attribute("decimals").toInt(), parent);
433 doubleparam->setFocusPolicy(Qt::StrongFocus);
434 m_vbox->addWidget(doubleparam);
435 m_valueItems[paramName] = doubleparam;
436 connect(doubleparam, SIGNAL(valueChanged(double)), this, SLOT(slotCollectAllParameters()));
437 connect(this, SIGNAL(showComments(bool)), doubleparam, SLOT(slotShowComment(bool)));
438 } else if (type == "list") {
439 Listval *lsval = new Listval;
440 lsval->setupUi(toFillin);
441 lsval->list->setFocusPolicy(Qt::StrongFocus);
442 QStringList listitems = pa.attribute("paramlist").split(';');
443 if (listitems.count() == 1) {
444 // probably custom effect created before change to ';' as separator
445 listitems = pa.attribute("paramlist").split(",");
447 QDomElement list = pa.firstChildElement("paramlistdisplay");
448 QStringList listitemsdisplay;
449 if (!list.isNull()) {
450 listitemsdisplay = i18n(list.text().toUtf8().data()).split(',');
452 listitemsdisplay = i18n(pa.attribute("paramlistdisplay").toUtf8().data()).split(',');
454 if (listitemsdisplay.count() != listitems.count())
455 listitemsdisplay = listitems;
456 lsval->list->setIconSize(QSize(30, 30));
457 for (int i = 0; i < listitems.count(); i++) {
458 lsval->list->addItem(listitemsdisplay.at(i), listitems.at(i));
459 QString entry = listitems.at(i);
460 if (!entry.isEmpty() && (entry.endsWith(".png") || entry.endsWith(".pgm"))) {
461 if (!CollapsibleEffect::iconCache.contains(entry)) {
463 CollapsibleEffect::iconCache[entry] = pix.scaled(30, 30);
465 lsval->list->setItemIcon(i, QPixmap::fromImage(CollapsibleEffect::iconCache[entry]));
468 if (!value.isEmpty()) lsval->list->setCurrentIndex(listitems.indexOf(value));
469 lsval->name->setText(paramName);
470 lsval->labelComment->setText(comment);
471 lsval->widgetComment->setHidden(true);
472 m_valueItems[paramName] = lsval;
473 connect(lsval->list, SIGNAL(currentIndexChanged(int)) , this, SLOT(slotCollectAllParameters()));
474 if (!comment.isEmpty())
475 connect(this, SIGNAL(showComments(bool)), lsval->widgetComment, SLOT(setVisible(bool)));
476 m_uiItems.append(lsval);
477 } else if (type == "bool") {
478 Boolval *bval = new Boolval;
479 bval->setupUi(toFillin);
480 bval->checkBox->setCheckState(value == "0" ? Qt::Unchecked : Qt::Checked);
481 bval->name->setText(paramName);
482 bval->labelComment->setText(comment);
483 bval->widgetComment->setHidden(true);
484 m_valueItems[paramName] = bval;
485 connect(bval->checkBox, SIGNAL(stateChanged(int)) , this, SLOT(slotCollectAllParameters()));
486 if (!comment.isEmpty())
487 connect(this, SIGNAL(showComments(bool)), bval->widgetComment, SLOT(setVisible(bool)));
488 m_uiItems.append(bval);
489 } else if (type == "complex") {
490 ComplexParameter *pl = new ComplexParameter;
491 pl->setupParam(effect, pa.attribute("name"), 0, 100);
492 m_vbox->addWidget(pl);
493 m_valueItems[paramName+"complex"] = pl;
494 connect(pl, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
495 } else if (type == "geometry") {
496 if (KdenliveSettings::on_monitor_effects()) {
497 m_geometryWidget = new GeometryWidget(m_metaInfo->monitor, m_metaInfo->timecode, 0, true, effect.hasAttribute("showrotation"), parent);
498 m_geometryWidget->setFrameSize(m_metaInfo->frameSize);
499 m_geometryWidget->slotShowScene(!disable);
500 // connect this before setupParam to make sure the monitor scene shows up at startup
501 connect(m_geometryWidget, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
502 connect(m_geometryWidget, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
503 if (minFrame == maxFrame)
504 m_geometryWidget->setupParam(pa, m_in, m_out);
506 m_geometryWidget->setupParam(pa, minFrame, maxFrame);
507 m_vbox->addWidget(m_geometryWidget);
508 m_valueItems[paramName+"geometry"] = m_geometryWidget;
509 connect(m_geometryWidget, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
510 connect(this, SIGNAL(syncEffectsPos(int)), m_geometryWidget, SLOT(slotSyncPosition(int)));
511 connect(this, SIGNAL(effectStateChanged(bool)), m_geometryWidget, SLOT(slotShowScene(bool)));
513 Geometryval *geo = new Geometryval(m_metaInfo->profile, m_metaInfo->timecode, m_metaInfo->frameSize, 0);
514 if (minFrame == maxFrame)
515 geo->setupParam(pa, m_in, m_out);
517 geo->setupParam(pa, minFrame, maxFrame);
518 m_vbox->addWidget(geo);
519 m_valueItems[paramName+"geometry"] = geo;
520 connect(geo, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
521 connect(geo, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
522 connect(this, SIGNAL(syncEffectsPos(int)), geo, SLOT(slotSyncPosition(int)));
524 } else if (type == "addedgeometry") {
525 // this is a parameter that should be linked to the geometry widget, for example rotation, shear, ...
526 if (m_geometryWidget) m_geometryWidget->addParameter(pa);
527 } else if (type == "keyframe" || type == "simplekeyframe") {
528 // keyframe editor widget
529 if (m_keyframeEditor == NULL) {
531 if (pa.attribute("widget") == "corners") {
532 // we want a corners-keyframe-widget
533 CornersWidget *corners = new CornersWidget(m_metaInfo->monitor, pa, m_in, m_out, m_metaInfo->timecode, e.attribute("active_keyframe", "-1").toInt(), parent);
534 corners->slotShowScene(!disable);
535 connect(corners, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
536 connect(this, SIGNAL(effectStateChanged(bool)), corners, SLOT(slotShowScene(bool)));
537 connect(this, SIGNAL(syncEffectsPos(int)), corners, SLOT(slotSyncPosition(int)));
538 geo = static_cast<KeyframeEdit *>(corners);
540 geo = new KeyframeEdit(pa, m_in, m_out, m_metaInfo->timecode, e.attribute("active_keyframe", "-1").toInt());
542 m_vbox->addWidget(geo);
543 m_valueItems[paramName+"keyframe"] = geo;
544 m_keyframeEditor = geo;
545 connect(geo, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
546 connect(geo, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
547 connect(this, SIGNAL(showComments(bool)), geo, SIGNAL(showComments(bool)));
549 // we already have a keyframe editor, so just add another column for the new param
550 m_keyframeEditor->addParameter(pa);
552 } else if (type == "color") {
553 if (value.startsWith('#'))
554 value = value.replace('#', "0x");
555 ChooseColorWidget *choosecolor = new ChooseColorWidget(paramName, value, parent);
556 m_vbox->addWidget(choosecolor);
557 m_valueItems[paramName] = choosecolor;
558 connect(choosecolor, SIGNAL(displayMessage(const QString&, int)), this, SIGNAL(displayMessage(const QString&, int)));
559 connect(choosecolor, SIGNAL(modified()) , this, SLOT(slotCollectAllParameters()));
560 } else if (type == "position") {
561 int pos = value.toInt();
562 if (effect.attribute("id") == "fadein" || effect.attribute("id") == "fade_from_black") {
564 } else if (effect.attribute("id") == "fadeout" || effect.attribute("id") == "fade_to_black") {
565 // fadeout position starts from clip end
568 PositionEdit *posedit = new PositionEdit(paramName, pos, 0, m_out - m_in, m_metaInfo->timecode);
569 m_vbox->addWidget(posedit);
570 m_valueItems[paramName+"position"] = posedit;
571 connect(posedit, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
572 } else if (type == "curve") {
573 KisCurveWidget *curve = new KisCurveWidget(parent);
574 curve->setMaxPoints(pa.attribute("max").toInt());
575 QList<QPointF> points;
576 int number = EffectsList::parameter(e, pa.attribute("number")).toInt();
577 QString inName = pa.attribute("inpoints");
578 QString outName = pa.attribute("outpoints");
579 int start = pa.attribute("min").toInt();
580 for (int j = start; j <= number; j++) {
582 in.replace("%i", QString::number(j));
583 QString out = outName;
584 out.replace("%i", QString::number(j));
585 points << QPointF(EffectsList::parameter(e, in).toDouble(), EffectsList::parameter(e, out).toDouble());
587 if (!points.isEmpty())
588 curve->setCurve(KisCubicCurve(points));
589 MySpinBox *spinin = new MySpinBox();
590 spinin->setRange(0, 1000);
591 MySpinBox *spinout = new MySpinBox();
592 spinout->setRange(0, 1000);
593 curve->setupInOutControls(spinin, spinout, 0, 1000);
594 m_vbox->addWidget(curve);
595 m_vbox->addWidget(spinin);
596 m_vbox->addWidget(spinout);
598 connect(curve, SIGNAL(modified()), this, SLOT(slotCollectAllParameters()));
599 m_valueItems[paramName] = curve;
601 QString depends = pa.attribute("depends");
602 if (!depends.isEmpty())
603 meetDependency(paramName, type, EffectsList::parameter(e, depends));
604 } else if (type == "bezier_spline") {
605 BezierSplineWidget *widget = new BezierSplineWidget(value, parent);
607 m_vbox->addWidget(widget);
608 m_valueItems[paramName] = widget;
609 connect(widget, SIGNAL(modified()), this, SLOT(slotCollectAllParameters()));
610 QString depends = pa.attribute("depends");
611 if (!depends.isEmpty())
612 meetDependency(paramName, type, EffectsList::parameter(e, depends));
614 } else if (type == "roto-spline") {
615 RotoWidget *roto = new RotoWidget(value, m_metaInfo->monitor, info, m_metaInfo->timecode, parent);
616 roto->slotShowScene(!disable);
617 connect(roto, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
618 connect(roto, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
619 connect(roto, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
620 connect(this, SIGNAL(syncEffectsPos(int)), roto, SLOT(slotSyncPosition(int)));
621 connect(this, SIGNAL(effectStateChanged(bool)), roto, SLOT(slotShowScene(bool)));
622 m_vbox->addWidget(roto);
623 m_valueItems[paramName] = roto;
625 } else if (type == "wipe") {
626 Wipeval *wpval = new Wipeval;
627 wpval->setupUi(toFillin);
628 wipeInfo w = getWipeInfo(value);
631 wpval->start_up->setChecked(true);
634 wpval->start_down->setChecked(true);
637 wpval->start_right->setChecked(true);
640 wpval->start_left->setChecked(true);
643 wpval->start_center->setChecked(true);
648 wpval->end_up->setChecked(true);
651 wpval->end_down->setChecked(true);
654 wpval->end_right->setChecked(true);
657 wpval->end_left->setChecked(true);
660 wpval->end_center->setChecked(true);
663 wpval->start_transp->setValue(w.startTransparency);
664 wpval->end_transp->setValue(w.endTransparency);
665 m_valueItems[paramName] = wpval;
666 connect(wpval->end_up, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
667 connect(wpval->end_down, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
668 connect(wpval->end_left, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
669 connect(wpval->end_right, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
670 connect(wpval->end_center, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
671 connect(wpval->start_up, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
672 connect(wpval->start_down, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
673 connect(wpval->start_left, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
674 connect(wpval->start_right, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
675 connect(wpval->start_center, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
676 connect(wpval->start_transp, SIGNAL(valueChanged(int)), this, SLOT(slotCollectAllParameters()));
677 connect(wpval->end_transp, SIGNAL(valueChanged(int)), this, SLOT(slotCollectAllParameters()));
678 //wpval->title->setTitle(na.toElement().text());
679 m_uiItems.append(wpval);
680 } else if (type == "url") {
681 Urlval *cval = new Urlval;
682 cval->setupUi(toFillin);
683 cval->label->setText(paramName);
684 cval->urlwidget->fileDialog()->setFilter(ProjectList::getExtensions());
685 m_valueItems[paramName] = cval;
686 cval->urlwidget->setUrl(KUrl(value));
687 connect(cval->urlwidget, SIGNAL(returnPressed()) , this, SLOT(slotCollectAllParameters()));
688 connect(cval->urlwidget, SIGNAL(urlSelected(const KUrl&)) , this, SLOT(slotCollectAllParameters()));
689 m_uiItems.append(cval);
696 m_vbox->addWidget(toFillin);
700 m_vbox->addStretch();
702 if (m_keyframeEditor)
703 m_keyframeEditor->checkVisibleParam();
705 // Make sure all doubleparam spinboxes have the same width, looks much better
706 QList<DoubleParameterWidget *> allWidgets = findChildren<DoubleParameterWidget *>();
708 for (int i = 0; i < allWidgets.count(); i++) {
709 if (minSize < allWidgets.at(i)->spinSize()) minSize = allWidgets.at(i)->spinSize();
711 for (int i = 0; i < allWidgets.count(); i++) {
712 allWidgets.at(i)->setSpinSize(minSize);
716 ParameterContainer::~ParameterContainer()
722 void ParameterContainer::meetDependency(const QString& name, QString type, QString value)
724 if (type == "curve") {
725 KisCurveWidget *curve = (KisCurveWidget*)m_valueItems[name];
727 int color = value.toInt();
728 curve->setPixmap(QPixmap::fromImage(ColorTools::rgbCurvePlane(curve->size(), (ColorTools::ColorsRGB)(color == 3 ? 4 : color), 0.8)));
730 } else if (type == "bezier_spline") {
731 BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems[name];
733 widget->setMode((BezierSplineWidget::CurveModes)((int)(value.toDouble() * 10)));
738 wipeInfo ParameterContainer::getWipeInfo(QString value)
741 // Convert old geometry values that used a comma as separator
742 if (value.contains(',')) value.replace(',','/');
743 QString start = value.section(';', 0, 0);
744 QString end = value.section(';', 1, 1).section('=', 1, 1);
745 if (start.startsWith("-100%/0"))
747 else if (start.startsWith("100%/0"))
749 else if (start.startsWith("0%/100%"))
751 else if (start.startsWith("0%/-100%"))
756 if (start.count(':') == 2)
757 info.startTransparency = start.section(':', -1).toInt();
759 info.startTransparency = 100;
761 if (end.startsWith("-100%/0"))
763 else if (end.startsWith("100%/0"))
765 else if (end.startsWith("0%/100%"))
767 else if (end.startsWith("0%/-100%"))
772 if (end.count(':') == 2)
773 info.endTransparency = end.section(':', -1).toInt();
775 info.endTransparency = 100;
780 void ParameterContainer::updateTimecodeFormat()
782 if (m_keyframeEditor)
783 m_keyframeEditor->updateTimecodeFormat();
785 QDomNodeList namenode = m_effect.elementsByTagName("parameter");
786 for (int i = 0; i < namenode.count() ; i++) {
787 QDomNode pa = namenode.item(i);
788 QDomElement na = pa.firstChildElement("name");
789 QString type = pa.attributes().namedItem("type").nodeValue();
790 QString paramName = na.isNull() ? pa.attributes().namedItem("name").nodeValue() : i18n(na.text().toUtf8().data());
792 if (type == "geometry") {
793 if (KdenliveSettings::on_monitor_effects()) {
794 if (m_geometryWidget) m_geometryWidget->updateTimecodeFormat();
796 Geometryval *geom = ((Geometryval*)m_valueItems[paramName+"geometry"]);
797 geom->updateTimecodeFormat();
800 } else if (type == "position") {
801 PositionEdit *posi = ((PositionEdit*)m_valueItems[paramName+"position"]);
802 posi->updateTimecodeFormat();
805 } else if (type == "roto-spline") {
806 RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems[paramName]);
807 widget->updateTimecodeFormat();
813 void ParameterContainer::slotCollectAllParameters()
815 if (m_valueItems.isEmpty() || m_effect.isNull()) return;
817 locale.setNumberOptions(QLocale::OmitGroupSeparator);
818 const QDomElement oldparam = m_effect.cloneNode().toElement();
819 QDomElement newparam = oldparam.cloneNode().toElement();
820 QDomNodeList namenode = newparam.elementsByTagName("parameter");
822 for (int i = 0; i < namenode.count() ; i++) {
823 QDomNode pa = namenode.item(i);
824 QDomElement na = pa.firstChildElement("name");
825 QString type = pa.attributes().namedItem("type").nodeValue();
826 QString paramName = na.isNull() ? pa.attributes().namedItem("name").nodeValue() : i18n(na.text().toUtf8().data());
827 if (type == "complex")
828 paramName.append("complex");
829 else if (type == "position")
830 paramName.append("position");
831 else if (type == "geometry")
832 paramName.append("geometry");
833 else if (type == "keyframe")
834 paramName.append("keyframe");
835 if (type != "simplekeyframe" && type != "fixed" && type != "addedgeometry" && !m_valueItems.contains(paramName)) {
836 kDebug() << "// Param: " << paramName << " NOT FOUND";
841 if (type == "double" || type == "constant") {
842 DoubleParameterWidget *doubleparam = (DoubleParameterWidget*)m_valueItems.value(paramName);
843 setValue = locale.toString(doubleparam->getValue());
844 } else if (type == "list") {
845 KComboBox *box = ((Listval*)m_valueItems.value(paramName))->list;
846 setValue = box->itemData(box->currentIndex()).toString();
847 } else if (type == "bool") {
848 QCheckBox *box = ((Boolval*)m_valueItems.value(paramName))->checkBox;
849 setValue = box->checkState() == Qt::Checked ? "1" : "0" ;
850 } else if (type == "color") {
851 ChooseColorWidget *choosecolor = ((ChooseColorWidget*)m_valueItems.value(paramName));
852 setValue = choosecolor->getColor();
853 } else if (type == "complex") {
854 ComplexParameter *complex = ((ComplexParameter*)m_valueItems.value(paramName));
855 namenode.item(i) = complex->getParamDesc();
856 } else if (type == "geometry") {
857 if (KdenliveSettings::on_monitor_effects()) {
858 if (m_geometryWidget) namenode.item(i).toElement().setAttribute("value", m_geometryWidget->getValue());
860 Geometryval *geom = ((Geometryval*)m_valueItems.value(paramName));
861 namenode.item(i).toElement().setAttribute("value", geom->getValue());
863 } else if (type == "addedgeometry") {
864 namenode.item(i).toElement().setAttribute("value", m_geometryWidget->getExtraValue(namenode.item(i).toElement().attribute("name")));
865 } else if (type == "position") {
866 PositionEdit *pedit = ((PositionEdit*)m_valueItems.value(paramName));
867 int pos = pedit->getPosition();
868 setValue = QString::number(pos);
869 if (newparam.attribute("id") == "fadein" || newparam.attribute("id") == "fade_from_black") {
870 // Make sure duration is not longer than clip
873 pedit->setPosition(pos);
875 EffectsList::setParameter(newparam, "in", QString::number(m_in));
876 EffectsList::setParameter(newparam, "out", QString::number(m_in + pos));
878 } else if (newparam.attribute("id") == "fadeout" || newparam.attribute("id") == "fade_to_black") {
879 // Make sure duration is not longer than clip
882 pedit->setPosition(pos);
884 EffectsList::setParameter(newparam, "in", QString::number(m_out - pos));
885 EffectsList::setParameter(newparam, "out", QString::number(m_out));
888 } else if (type == "curve") {
889 KisCurveWidget *curve = ((KisCurveWidget*)m_valueItems.value(paramName));
890 QList<QPointF> points = curve->curve().points();
891 QString number = pa.attributes().namedItem("number").nodeValue();
892 QString inName = pa.attributes().namedItem("inpoints").nodeValue();
893 QString outName = pa.attributes().namedItem("outpoints").nodeValue();
894 int off = pa.attributes().namedItem("min").nodeValue().toInt();
895 int end = pa.attributes().namedItem("max").nodeValue().toInt();
896 EffectsList::setParameter(newparam, number, QString::number(points.count()));
897 for (int j = 0; (j < points.count() && j + off <= end); j++) {
899 in.replace("%i", QString::number(j + off));
900 QString out = outName;
901 out.replace("%i", QString::number(j + off));
902 EffectsList::setParameter(newparam, in, locale.toString(points.at(j).x()));
903 EffectsList::setParameter(newparam, out, locale.toString(points.at(j).y()));
905 QString depends = pa.attributes().namedItem("depends").nodeValue();
906 if (!depends.isEmpty())
907 meetDependency(paramName, type, EffectsList::parameter(newparam, depends));
908 } else if (type == "bezier_spline") {
909 BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems.value(paramName);
910 setValue = widget->spline();
911 QString depends = pa.attributes().namedItem("depends").nodeValue();
912 if (!depends.isEmpty())
913 meetDependency(paramName, type, EffectsList::parameter(newparam, depends));
915 } else if (type == "roto-spline") {
916 RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems.value(paramName));
917 setValue = widget->getSpline();
919 } else if (type == "wipe") {
920 Wipeval *wp = (Wipeval*)m_valueItems.value(paramName);
922 if (wp->start_left->isChecked())
924 else if (wp->start_right->isChecked())
926 else if (wp->start_up->isChecked())
928 else if (wp->start_down->isChecked())
930 else if (wp->start_center->isChecked())
934 info.startTransparency = wp->start_transp->value();
936 if (wp->end_left->isChecked())
938 else if (wp->end_right->isChecked())
940 else if (wp->end_up->isChecked())
942 else if (wp->end_down->isChecked())
944 else if (wp->end_center->isChecked())
948 info.endTransparency = wp->end_transp->value();
950 setValue = getWipeString(info);
951 } else if ((type == "simplekeyframe" || type == "keyframe") && m_keyframeEditor) {
952 QDomElement elem = pa.toElement();
953 QString realName = i18n(na.toElement().text().toUtf8().data());
954 QString val = m_keyframeEditor->getValue(realName);
955 elem.setAttribute("keyframes", val);
957 if (m_keyframeEditor->isVisibleParam(realName))
958 elem.setAttribute("intimeline", "1");
959 else if (elem.hasAttribute("intimeline"))
960 elem.removeAttribute("intimeline");
961 } else if (type == "url") {
962 KUrlRequester *req = ((Urlval*)m_valueItems.value(paramName))->urlwidget;
963 setValue = req->url().path();
966 if (!setValue.isNull())
967 pa.attributes().namedItem("value").setNodeValue(setValue);
970 emit parameterChanged(oldparam, newparam, m_index);
973 QString ParameterContainer::getWipeString(wipeInfo info)
978 switch (info.start) {
980 start = "-100%/0%:100%x100%";
983 start = "100%/0%:100%x100%";
986 start = "0%/100%:100%x100%";
989 start = "0%/-100%:100%x100%";
992 start = "0%/0%:100%x100%";
995 start.append(':' + QString::number(info.startTransparency));
999 end = "-100%/0%:100%x100%";
1002 end = "100%/0%:100%x100%";
1005 end = "0%/100%:100%x100%";
1008 end = "0%/-100%:100%x100%";
1011 end = "0%/0%:100%x100%";
1014 end.append(':' + QString::number(info.endTransparency));
1015 return QString(start + ";-1=" + end);
1018 int ParameterContainer::index()