data_show
gtkrescale
watermark
-region
+#region
resize
resample
mono
oldfilm.xml
pan_zoom.xml
obscure.xml
+region.xml
rotation.xml
rotation_keyframable.xml
scratchlines.xml
- tag "name": visible name of the parameter (depending on the GUI this parameter uses)
- tag "comment": (optional) description of the parameter (support HTML formatting) (not yet supported by all widgets)
- attribute "name": MLT filter parameter name
+ - attribute "paramprefix": a string to be prepended to the parameter value before passing it to MLT
- attribute "default": initial value, format depends on parameter type
- attribute "type": widget (GUI) to use
- "fixed":
--- /dev/null
+<!DOCTYPE kpartgui>
+<effect tag="region" id="region">
+ <name>Regionalize</name>
+ <description>Apply sub-effects to a region defined by a clip's alpha channel</description>
+ <author>Charles Yates</author>
+ <parameter type="url" name="resource" default="">
+ <name>Url</name>
+ </parameter>
+ <parameter type="fixed" name="filter_only" min="1" max="1" default="1" />
+</effect>
EffectsParameterList parameters;
QLocale locale;
parameters.addParam("tag", effect.attribute("tag"));
- if (effect.hasAttribute("region")) parameters.addParam("region", effect.attribute("region"));
+ //if (effect.hasAttribute("region")) parameters.addParam("region", effect.attribute("region"));
parameters.addParam("kdenlive_ix", effect.attribute("kdenlive_ix"));
parameters.addParam("kdenlive_info", effect.attribute("kdenlive_info"));
parameters.addParam("id", effect.attribute("id"));
if (effect.hasAttribute("disable")) parameters.addParam("disable", effect.attribute("disable"));
if (effect.hasAttribute("in")) parameters.addParam("in", effect.attribute("in"));
if (effect.hasAttribute("out")) parameters.addParam("out", effect.attribute("out"));
+ if (effect.attribute("id") == "region") {
+ QDomNodeList subeffects = effect.elementsByTagName("effect");
+ for (int i = 0; i < subeffects.count(); i++) {
+ QDomElement subeffect = subeffects.at(i).toElement();
+ int subeffectix = subeffect.attribute("region_ix").toInt();
+ parameters.addParam(QString("filter%1").arg(subeffectix), subeffect.attribute("id"));
+ parameters.addParam(QString("filter%1.tag").arg(subeffectix), subeffect.attribute("tag"));
+ parameters.addParam(QString("filter%1.kdenlive_info").arg(subeffectix), subeffect.attribute("kdenlive_info"));
+ QDomNodeList subparams = subeffect.elementsByTagName("parameter");
+ adjustEffectParameters(parameters, subparams, QString("filter%1.").arg(subeffectix));
+ }
+ }
QDomNodeList params = effect.elementsByTagName("parameter");
- for (int i = 0; i < params.count(); i++) {
+ adjustEffectParameters(parameters, params);
+
+ return parameters;
+}
+
+
+void CustomTrackView::adjustEffectParameters(EffectsParameterList ¶meters, QDomNodeList params, const QString &prefix)
+{
+ QLocale locale;
+ for (int i = 0; i < params.count(); i++) {
QDomElement e = params.item(i).toElement();
+ QString paramname = prefix + e.attribute("name");
if (e.attribute("type") == "geometry" && !e.hasAttribute("fixed")) {
// effects with geometry param need in / out synced with the clip, request it...
parameters.addParam("_sync_in_out", "1");
}
if (e.attribute("type") == "simplekeyframe") {
-
QStringList values = e.attribute("keyframes").split(";", QString::SkipEmptyParts);
double factor = e.attribute("factor", "1").toDouble();
double offset = e.attribute("offset", "0").toDouble();
values[j] = pos + "=" + locale.toString(val);
}
// kDebug() << "/ / / /SENDING KEYFR:" << values;
- parameters.addParam(e.attribute("name"), values.join(";"));
+ parameters.addParam(paramname, values.join(";"));
/*parameters.addParam(e.attribute("name"), e.attribute("keyframes").replace(":", "="));
parameters.addParam("max", e.attribute("max"));
parameters.addParam("min", e.attribute("min"));
fact = e.attribute("factor", "1").toDouble();
}
double offset = e.attribute("offset", "0").toDouble();
- parameters.addParam(e.attribute("name"), locale.toString((e.attribute("value").toDouble() - offset) / fact));
+ parameters.addParam(paramname, locale.toString((e.attribute("value").toDouble() - offset) / fact));
} else {
- parameters.addParam(e.attribute("name"), e.attribute("value"));
+ parameters.addParam(paramname, e.attribute("value"));
}
}
}
- return parameters;
}
+
void CustomTrackView::updateTrackNames(int track, bool added)
{
QList <TrackInfo> tracks = m_document->tracksList();
/** @brief Prepare an add clip command for an effect */
void processEffect(ClipItem *item, QDomElement effect, QUndoCommand *effectCommand);
+
+ /** @brief Get effect parameters ready for MLT*/
+ void adjustEffectParameters(EffectsParameterList ¶meters, QDomNodeList params, const QString &prefix = QString());
private slots:
void slotRefreshGuides();
void EffectsList::setParameter(QDomElement effect, const QString &name, const QString &value)
{
QDomNodeList params = effect.elementsByTagName("parameter");
+ bool found = false;
for (int i = 0; i < params.count(); i++) {
QDomElement e = params.item(i).toElement();
if (e.attribute("name") == name) {
e.setAttribute("value", value);
+ found = true;
break;
}
}
+ if (!found) {
+ // create property
+ QDomDocument doc = effect.ownerDocument();
+ QDomElement e = doc.createElement("parameter");
+ e.setAttribute("name", name);
+ QDomText val = doc.createTextNode(value);
+ e.appendChild(val);
+ effect.appendChild(e);
+ }
}
// static
#include <KUrlRequester>
#include <KColorScheme>
#include <KColorUtils>
+#include <KApplication>
class Boolval: public QWidget, public Ui::Boolval_UI
{
m_paramWidget(NULL),
m_effect(effect),
m_original_effect(original_effect),
- m_lastEffect(lastEffect)
+ m_lastEffect(lastEffect),
+ m_regionEffect(false)
{
setupUi(this);
+ if (m_effect.attribute("tag") == "region") {
+ m_regionEffect = true;
+ decoframe->setObjectName("decoframegroup");
+ }
filterWheelEvent = true;
m_info.fromString(effect.attribute("kdenlive_info"));
setFont(KGlobalSettings::smallestReadableFont());
//buttonShowComments->setIcon(KIcon("help-about"));
//buttonShowComments->setToolTip(i18n("Show additional information for the parameters"));
m_menu = new QMenu;
+ if (m_regionEffect) m_menu->addAction(KIcon("document-new"), i18n("Change Region"), this, SLOT(slotResetEffect()));
m_menu->addAction(KIcon("view-refresh"), i18n("Reset Effect"), this, SLOT(slotResetEffect()));
m_menu->addAction(KIcon("document-save"), i18n("Save Effect"), this, SLOT(slotSaveEffect()));
QDomElement namenode = m_effect.firstChildElement("name");
if (namenode.isNull()) return;
- title->setText(i18n(namenode.text().toUtf8().data()));
+ QString effectname = i18n(namenode.text().toUtf8().data());
+ if (m_regionEffect) effectname.append(":" + KUrl(EffectsList::parameter(m_effect, "resource")).fileName());
+ title->setText(effectname);
/*
* Do not show icon, makes too much visual noise
QString type = m_effect.attribute("type", QString());
else icon = KIcon("kdenlive-show-video");
effecticon->setPixmap(icon.pixmap(16,16));*/
- m_menu->addAction(KIcon("folder-new"), i18n("Create Group"), this, SLOT(slotCreateGroup()));
+ if (!m_regionEffect) {
+ m_menu->addAction(KIcon("folder-new"), i18n("Create Group"), this, SLOT(slotCreateGroup()));
+ m_menu->addAction(KIcon("folder-new"), i18n("Create Region"), this, SLOT(slotCreateRegion()));
+ }
setupWidget(info, metaInfo);
setAcceptDrops(true);
menuButton->setIcon(KIcon("kdenlive-menu"));
emit createGroup(effectIndex());
}
+void CollapsibleEffect::slotCreateRegion()
+{
+ QString allExtensions = ProjectList::getExtensions();
+ const QString dialogFilter = allExtensions + ' ' + QLatin1Char('|') + i18n("All Supported Files") + "\n* " + QLatin1Char('|') + i18n("All Files");
+ KFileDialog *d = new KFileDialog(KUrl("kfiledialog:///clipfolder"), dialogFilter, kapp->activeWindow());
+ d->setOperationMode(KFileDialog::Opening);
+ d->setMode(KFile::File);
+ if (d->exec() == QDialog::Accepted) {
+ KUrl url = d->selectedUrl();
+ if (!url.isEmpty()) emit createRegion(effectIndex(), url);
+ }
+ delete d;
+}
+
void CollapsibleEffect::slotUnGroup()
{
emit unGroup(this);
}
if (m_effect.attribute("tag") == "region") {
+ m_regionEffect = true;
QVBoxLayout *vbox = new QVBoxLayout(widgetFrame);
vbox->setContentsMargins(0, 0, 0, 0);
vbox->setSpacing(2);
QDomNodeList origin_effects = m_original_effect.elementsByTagName("effect");
QWidget *container = new QWidget(widgetFrame);
vbox->addWidget(container);
- m_paramWidget = new ParameterContainer(m_effect.toElement(), info, metaInfo, container);
+ // m_paramWidget = new ParameterContainer(m_effect.toElement(), info, metaInfo, container);
for (int i = 0; i < effects.count(); i++) {
CollapsibleEffect *coll = new CollapsibleEffect(effects.at(i).toElement(), origin_effects.at(i).toElement(), info, metaInfo, container);
m_subParamWidgets.append(coll);
+ connect(coll, SIGNAL(parameterChanged(const QDomElement, const QDomElement, int)), this , SLOT(slotUpdateRegionEffectParams(const QDomElement, const QDomElement, int)));
//container = new QWidget(widgetFrame);
vbox->addWidget(coll);
//p = new ParameterContainer(effects.at(i).toElement(), info, isEffect, container);
}
}
+void CollapsibleEffect::slotUpdateRegionEffectParams(const QDomElement /*old*/, const QDomElement /*e*/, int /*ix*/)
+{
+ kDebug()<<"// EMIT CHANGE SUBEFFECT.....:";
+ emit parameterChanged(m_original_effect, m_effect, effectIndex());
+}
+
void CollapsibleEffect::slotSyncEffectsPos(int pos)
{
emit syncEffectsPos(pos);
void slotEffectDown();
void slotSaveEffect();
void slotCreateGroup();
+ void slotCreateRegion();
void slotUnGroup();
+ /** @brief A sub effect parameter was changed */
+ void slotUpdateRegionEffectParams(const QDomElement /*old*/, const QDomElement /*e*/, int /*ix*/);
private:
ParameterContainer *m_paramWidget;
QMenu *m_menu;
QPoint m_clickPoint;
EffectInfo m_info;
+ /** @brief True if this is a region effect, which behaves in a special way, like a group. */
+ bool m_regionEffect;
protected:
virtual void mouseDoubleClickEvent ( QMouseEvent * event );
void moveEffect(int current_pos, int new_pos, int groupIndex, QString groupName);
void unGroup(CollapsibleEffect *);
void addEffect(QDomElement e);
+ void createRegion(int, KUrl);
};
EffectInfo effectInfo;
effectInfo.fromString(d.attribute("kdenlive_info"));
if (effectInfo.groupIndex >= 0) {
- // effect is in a group
-
+ // effect is in a group
for (int j = 0; j < vbox1->count(); j++) {
CollapsibleGroup *eff = static_cast<CollapsibleGroup *>(vbox1->itemAt(j)->widget());
if (eff->isGroup() && eff->groupIndex() == effectInfo.groupIndex) {
connect(currentEffect, SIGNAL(createGroup(int)), this , SLOT(slotCreateGroup(int)));
connect(currentEffect, SIGNAL(moveEffect(int,int,int,QString)), this , SLOT(slotMoveEffect(int,int,int,QString)));
connect(currentEffect, SIGNAL(addEffect(QDomElement)), this , SLOT(slotAddEffect(QDomElement)));
+ connect(currentEffect, SIGNAL(createRegion(int,KUrl)), this, SLOT(slotCreateRegion(int,KUrl)));
//ui.title->setPixmap(icon.pixmap(QSize(12, 12)));
}
emit showComments(m_ui.buttonShowComments->isChecked());
}
+void EffectStackView2::slotCreateRegion(int ix, KUrl url)
+{
+ QDomElement oldeffect = m_currentEffectList.itemFromIndex(ix);
+ QDomElement neweffect = oldeffect.cloneNode().toElement();
+ QDomElement region = MainWindow::videoEffects.getEffectByTag("region", "region").cloneNode().toElement();
+ region.appendChild(region.ownerDocument().importNode(neweffect, true));
+ region.setAttribute("kdenlive_ix", ix);
+ EffectsList::setParameter(region, "resource", url.path());
+ if (m_effectMetaInfo.trackMode)
+ emit updateEffect(NULL, m_trackindex, oldeffect, region, ix,false);
+ else if (m_clipref) {
+ emit updateEffect(m_clipref, -1, oldeffect, region, ix, false);
+ // Make sure the changed effect is currently displayed
+ //slotSetCurrentEffect(ix);
+ }
+ // refresh effect stack
+ ItemInfo info;
+ bool isSelected = false;
+ if (m_effectMetaInfo.trackMode) {
+ info.track = m_trackInfo.type;
+ info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
+ info.cropStart = GenTime(0);
+ info.startPos = GenTime(-1);
+ info.track = 0;
+ }
+ else {
+ info = m_clipref->info();
+ }
+ CollapsibleEffect *current = getEffectByIndex(ix);
+ m_effects.removeAll(current);
+ current->setEnabled(false);
+ m_currentEffectList.removeAt(ix);
+ m_currentEffectList.insert(region);
+ current->deleteLater();
+ CollapsibleEffect *currentEffect = new CollapsibleEffect(region, m_currentEffectList.itemFromIndex(ix), info, &m_effectMetaInfo, ix == m_currentEffectList.count() - 1, m_ui.container->widget());
+ connect(currentEffect, SIGNAL(parameterChanged(const QDomElement, const QDomElement, int)), this , SLOT(slotUpdateEffectParams(const QDomElement, const QDomElement, int)));
+ if (m_effectMetaInfo.trackMode) {
+ isSelected = currentEffect->effectIndex() == 1;
+ }
+ else {
+ isSelected = currentEffect->effectIndex() == m_clipref->selectedEffectIndex();
+ }
+ if (isSelected) currentEffect->setActive(true);
+ m_effects.append(currentEffect);
+ // TODO: region in group?
+ //if (group) {
+ // group->addGroupEffect(currentEffect);
+ //} else {
+ QVBoxLayout *vbox = static_cast <QVBoxLayout *> (m_ui.container->widget()->layout());
+ vbox->insertWidget(ix, currentEffect);
+ //}
+
+ // Check drag & drop
+ currentEffect->installEventFilter( this );
+
+ QTimer::singleShot(200, this, SLOT(slotCheckWheelEventFilter()));
+
+}
+
void EffectStackView2::slotCreateGroup(int ix)
{
QDomElement oldeffect = m_currentEffectList.itemFromIndex(ix);
/** @brief Create a group containing effect with ix index. */
void slotCreateGroup(int ix);
+ /** @brief Create a region effect with ix index. */
+ void slotCreateRegion(int ix, KUrl url);
+
/** @brief Move an effect into a group.
** @param ix the index of effect to move in stack layout
** @param group the effect on which the effect was dropped
}
//kDebug() << "+ + CLIP EFF FND: " << effecttag << ", " << effectid << ", " << effectindex;
// get effect standard tags
- QDomElement clipeffect = MainWindow::customEffects.getEffectByTag(QString(), effectid);
- if (clipeffect.isNull()) {
- clipeffect = MainWindow::videoEffects.getEffectByTag(effecttag, effectid);
- }
- if (clipeffect.isNull()) {
- clipeffect = MainWindow::audioEffects.getEffectByTag(effecttag, effectid);
- }
+ QDomElement clipeffect = getEffectByTag(effecttag, effectid);
if (clipeffect.isNull()) {
kDebug() << "/// WARNING, EFFECT: " << effecttag << ": " << effectid << " not found, removing it from project";
m_documentErrors.append(i18n("Effect %1:%2 not found in MLT, it was removed from this project\n", effecttag, effectid));
EffectsList::setParameter(currenteffect, "out", effect.attribute("out"));
}
}
-
+
+ // Special case, region filter embeds other effects
+ bool regionFilter = effecttag == "region";
+ QMap <QString, QString> regionEffects;
+
// adjust effect parameters
for (QDomNode n3 = effect.firstChild(); !n3.isNull(); n3 = n3.nextSibling()) {
// parse effect parameters
QDomElement effectparam = n3.toElement();
QString paramname = effectparam.attribute("name");
QString paramvalue = effectparam.text();
-
- // try to find this parameter in the effect xml
- QDomElement e;
- for (int k = 0; k < clipeffectparams.count(); k++) {
- e = clipeffectparams.item(k).toElement();
- if (!e.isNull() && e.tagName() == "parameter" && e.attribute("name") == paramname) {
- QString type = e.attribute("type");
- QString factor = e.attribute("factor", "1");
- double fact;
- if (factor.contains('%')) {
- fact = ProfilesDialog::getStringEval(m_doc->mltProfile(), factor);
- } else {
- fact = factor.toDouble();
- }
- double offset = e.attribute("offset", "0").toDouble();
- if (type == "simplekeyframe") {
- QStringList kfrs = paramvalue.split(";");
- for (int l = 0; l < kfrs.count(); l++) {
- QString fr = kfrs.at(l).section('=', 0, 0);
- double val = locale.toDouble(kfrs.at(l).section('=', 1, 1));
- //kfrs[l] = fr + ":" + locale.toString((int)(val * fact));
- kfrs[l] = fr + ":" + QString::number((int) (offset + val * fact));
- }
- e.setAttribute("keyframes", kfrs.join(";"));
- } else if (type == "double" || type == "constant") {
- bool ok;
- e.setAttribute("value", offset + locale.toDouble(paramvalue, &ok) * fact);
- if (!ok)
- e.setAttribute("value", paramvalue);
- } else {
- e.setAttribute("value", paramvalue);
- }
- break;
- }
- }
+
+ if (regionFilter && paramname.startsWith("filter")) {
+ regionEffects.insert(paramname, paramvalue);
+ continue;
+ }
+
+ // try to find this parameter in the effect xml and set its value
+ adjustparameterValue(clipeffectparams, paramname, paramvalue);
+
}
+ if (regionFilter && !regionEffects.isEmpty()) {
+ // insert region sub-effects
+ int i = 0;
+ while (regionEffects.contains(QString("filter%1").arg(i))) {
+ QString filterid = regionEffects.value(QString("filter%1.kdenlive_id").arg(i));
+ QString filtertag = regionEffects.value(QString("filter%1.tag").arg(i));
+ QDomElement subclipeffect = getEffectByTag(filtertag, filterid).cloneNode().toElement();
+ QDomNodeList subclipeffectparams = subclipeffect.childNodes();
+ subclipeffect.setAttribute("region_ix", i);
+ QMap<QString, QString>::const_iterator j = regionEffects.constBegin();
+ while (j != regionEffects.constEnd()) {
+ if (j.key().startsWith(QString("filter%1.").arg(i))) {
+ QString pname = j.key().section('.', 1, -1);
+ adjustparameterValue(subclipeffectparams, pname, j.value());
+ }
+ ++j;
+ }
+ currenteffect.appendChild(currenteffect.ownerDocument().importNode(subclipeffect, true));
+ i++;
+ }
+ }
+
if (disableeffect) currenteffect.setAttribute("disable", "1");
if (clip)
clip->addEffect(currenteffect, false);
}
+void TrackView::adjustparameterValue(QDomNodeList clipeffectparams, const QString ¶mname, const QString ¶mvalue)
+{
+ QDomElement e;
+ QLocale locale;
+ for (int k = 0; k < clipeffectparams.count(); k++) {
+ e = clipeffectparams.item(k).toElement();
+ if (!e.isNull() && e.tagName() == "parameter" && e.attribute("name") == paramname) {
+ QString type = e.attribute("type");
+ QString factor = e.attribute("factor", "1");
+ double fact;
+ if (factor.contains('%')) {
+ fact = ProfilesDialog::getStringEval(m_doc->mltProfile(), factor);
+ } else {
+ fact = factor.toDouble();
+ }
+ double offset = e.attribute("offset", "0").toDouble();
+ if (type == "simplekeyframe") {
+ QStringList kfrs = paramvalue.split(";");
+ for (int l = 0; l < kfrs.count(); l++) {
+ QString fr = kfrs.at(l).section('=', 0, 0);
+ double val = locale.toDouble(kfrs.at(l).section('=', 1, 1));
+ //kfrs[l] = fr + ":" + locale.toString((int)(val * fact));
+ kfrs[l] = fr + ":" + QString::number((int) (offset + val * fact));
+ }
+ e.setAttribute("keyframes", kfrs.join(";"));
+ } else if (type == "double" || type == "constant") {
+ bool ok;
+ e.setAttribute("value", offset + locale.toDouble(paramvalue, &ok) * fact);
+ if (!ok)
+ e.setAttribute("value", paramvalue);
+ } else {
+ e.setAttribute("value", paramvalue);
+ }
+ break;
+ }
+ }
+}
+
+
+QDomElement TrackView::getEffectByTag(const QString &effecttag, const QString &effectid)
+{
+ QDomElement clipeffect = MainWindow::customEffects.getEffectByTag(QString(), effectid);
+ if (clipeffect.isNull()) {
+ clipeffect = MainWindow::videoEffects.getEffectByTag(effecttag, effectid);
+ }
+ if (clipeffect.isNull()) {
+ clipeffect = MainWindow::audioEffects.getEffectByTag(effecttag, effectid);
+ }
+ return clipeffect;
+}
+
+
DocClipBase *TrackView::getMissingProducer(const QString id) const
{
QDomElement missingXml;
void adjustTrackHeaders();
/** @brief Add effects from the xml. Returns true if some effect was upgraded, false if everything went fine.*/
void slotAddProjectEffects(QDomNodeList effects, QDomElement parentNode, ClipItem *clip, int trackIndex);
+
+ /** @brief Returns a kdenlive effect xml description from an effect tag / id */
+ QDomElement getEffectByTag(const QString &effecttag, const QString &effectid);
+
+ /** @brief Adjust kdenlive effect xml parameters to the MLT value*/
+ void adjustparameterValue(QDomNodeList clipeffectparams, const QString ¶mname, const QString ¶mvalue);
private slots:
void setCursorPos(int pos);
<rect>
<x>0</x>
<y>0</y>
- <width>194</width>
- <height>42</height>
+ <width>159</width>
+ <height>23</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>0</number>
</property>
- <item row="0" column="0" colspan="2">
+ <item row="1" column="1">
+ <widget class="KUrlRequester" name="urlwidget"/>
+ </item>
+ <item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Param</string>
</property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="KUrlRequester" name="urlwidget"/>
- </item>
</layout>
</widget>
<customwidgets>