+ m_baseColor = QColor(141, 215, 166);
+ connect(m_clip, SIGNAL(gotAudioData()), this, SLOT(slotGotAudioData()));
+ }
+}
+
+
+ClipItem::~ClipItem()
+{
+ blockSignals(true);
+ if (scene()) scene()->removeItem(this);
+ if (m_clipType == VIDEO || m_clipType == AV || m_clipType == SLIDESHOW || m_clipType == PLAYLIST) {
+ //disconnect(m_clip->thumbProducer(), SIGNAL(thumbReady(int, QImage)), this, SLOT(slotThumbReady(int, QImage)));
+ //disconnect(m_clip, SIGNAL(gotAudioData()), this, SLOT(slotGotAudioData()));
+ }
+ delete m_timeLine;
+}
+
+ClipItem *ClipItem::clone(ItemInfo info) const
+{
+ ClipItem *duplicate = new ClipItem(m_clip, info, m_fps, m_speed, m_strobe);
+ if (m_clipType == IMAGE || m_clipType == TEXT) duplicate->slotSetStartThumb(m_startPix);
+ else if (m_clipType != COLOR) {
+ if (info.cropStart == m_info.cropStart) duplicate->slotSetStartThumb(m_startPix);
+ if (info.cropStart + (info.endPos - info.startPos) == m_info.cropStart + (m_info.endPos - m_info.startPos)) duplicate->slotSetEndThumb(m_endPix);
+ }
+ //kDebug() << "// CLoning clip: " << (info.cropStart + (info.endPos - info.startPos)).frames(m_fps) << ", CURRENT end: " << (cropStart() + duration()).frames(m_fps);
+ duplicate->setEffectList(m_effectList);
+ duplicate->setVideoOnly(m_videoOnly);
+ duplicate->setAudioOnly(m_audioOnly);
+ duplicate->setFades(fadeIn(), fadeOut());
+ //duplicate->setSpeed(m_speed);
+ return duplicate;
+}
+
+void ClipItem::setEffectList(const EffectsList effectList)
+{
+ m_effectList.clone(effectList);
+ m_effectNames = m_effectList.effectNames().join(" / ");
+ if (!m_effectList.isEmpty()) {
+ for (int i = 0; i < m_effectList.count(); i++) {
+ QString effectId = m_effectList.item(i).attribute("id");
+ // check if it is a fade effect
+ QDomNodeList params = m_effectList.item(i).elementsByTagName("parameter");
+ int fade = 0;
+ for (int j = 0; j < params.count(); j++) {
+ QDomElement e = params.item(j).toElement();
+ if (!e.isNull()) {
+ if (effectId == "fadein") {
+ if (m_effectList.hasEffect(QString(), "fade_from_black") == -1) {
+ if (e.attribute("name") == "out") fade += e.attribute("value").toInt();
+ else if (e.attribute("name") == "in") fade -= e.attribute("value").toInt();
+ } else {
+ QDomElement fadein = m_effectList.getEffectByTag(QString(), "fade_from_black");
+ if (fadein.attribute("name") == "out") fade += fadein.attribute("value").toInt();
+ else if (fadein.attribute("name") == "in") fade -= fadein.attribute("value").toInt();
+ }
+ } else if (effectId == "fade_from_black") {
+ if (m_effectList.hasEffect(QString(), "fadein") == -1) {
+ if (e.attribute("name") == "out") fade += e.attribute("value").toInt();
+ else if (e.attribute("name") == "in") fade -= e.attribute("value").toInt();
+ } else {
+ QDomElement fadein = m_effectList.getEffectByTag(QString(), "fadein");
+ if (fadein.attribute("name") == "out") fade += fadein.attribute("value").toInt();
+ else if (fadein.attribute("name") == "in") fade -= fadein.attribute("value").toInt();
+ }
+ } else if (effectId == "fadeout") {
+ if (m_effectList.hasEffect(QString(), "fade_to_black") == -1) {
+ if (e.attribute("name") == "out") fade -= e.attribute("value").toInt();
+ else if (e.attribute("name") == "in") fade += e.attribute("value").toInt();
+ } else {
+ QDomElement fadeout = m_effectList.getEffectByTag(QString(), "fade_to_black");
+ if (fadeout.attribute("name") == "out") fade -= fadeout.attribute("value").toInt();
+ else if (fadeout.attribute("name") == "in") fade += fadeout.attribute("value").toInt();
+ }
+ } else if (effectId == "fade_to_black") {
+ if (m_effectList.hasEffect(QString(), "fadeout") == -1) {
+ if (e.attribute("name") == "out") fade -= e.attribute("value").toInt();
+ else if (e.attribute("name") == "in") fade += e.attribute("value").toInt();
+ } else {
+ QDomElement fadeout = m_effectList.getEffectByTag(QString(), "fadeout");
+ if (fadeout.attribute("name") == "out") fade -= fadeout.attribute("value").toInt();
+ else if (fadeout.attribute("name") == "in") fade += fadeout.attribute("value").toInt();
+ }
+ }
+ }
+ }
+ if (fade > 0) m_startFade = fade;
+ else if (fade < 0) m_endFade = -fade;
+ }
+ setSelectedEffect(0);
+ }
+}
+
+const EffectsList ClipItem::effectList() const
+{
+ return m_effectList;
+}
+
+int ClipItem::selectedEffectIndex() const
+{
+ return m_selectedEffect;
+}
+
+void ClipItem::initEffect(QDomElement effect, int diff)
+{
+ // the kdenlive_ix int is used to identify an effect in mlt's playlist, should
+ // not be changed
+ if (effect.attribute("kdenlive_ix").toInt() == 0)
+ effect.setAttribute("kdenlive_ix", QString::number(effectsCounter()));
+
+ if (effect.attribute("id") == "freeze" && diff > 0) {
+ EffectsList::setParameter(effect, "frame", QString::number(diff));
+ }
+
+ // Init parameter value & keyframes if required
+ QDomNodeList params = effect.elementsByTagName("parameter");
+ for (int i = 0; i < params.count(); i++) {
+ QDomElement e = params.item(i).toElement();
+ kDebug() << "// init eff: " << e.attribute("name");
+
+ // Check if this effect has a variable parameter
+ if (e.attribute("default").startsWith('%')) {
+ double evaluatedValue = ProfilesDialog::getStringEval(projectScene()->profile(), e.attribute("default"));
+ e.setAttribute("default", evaluatedValue);
+ if (e.hasAttribute("value") && e.attribute("value").startsWith('%')) {
+ e.setAttribute("value", evaluatedValue);
+ }
+ }
+
+ if (!e.isNull() && (e.attribute("type") == "keyframe" || e.attribute("type") == "simplekeyframe")) {
+ QString def = e.attribute("default");
+ // Effect has a keyframe type parameter, we need to set the values
+ if (e.attribute("keyframes").isEmpty()) {
+ e.setAttribute("keyframes", QString::number(cropStart().frames(m_fps)) + ':' + def + ';' + QString::number((cropStart() + cropDuration()).frames(m_fps) - 1) + ':' + def);
+ kDebug() << "///// EFFECT KEYFRAMES INITED: " << e.attribute("keyframes");
+ //break;
+ }
+ }
+ }
+ if (effect.attribute("tag") == "volume" || effect.attribute("tag") == "brightness") {
+ if (effect.attribute("id") == "fadeout" || effect.attribute("id") == "fade_to_black") {
+ int end = (cropDuration() + cropStart()).frames(m_fps);
+ int start = end;
+ if (effect.attribute("id") == "fadeout") {
+ if (m_effectList.hasEffect(QString(), "fade_to_black") == -1) {
+ int effectDuration = EffectsList::parameter(effect, "in").toInt();
+ if (effectDuration > cropDuration().frames(m_fps)) {
+ effectDuration = cropDuration().frames(m_fps) / 2;
+ }
+ start -= effectDuration;
+ } else {
+ QDomElement fadeout = m_effectList.getEffectByTag(QString(), "fade_to_black");
+ start -= EffectsList::parameter(fadeout, "out").toInt() - EffectsList::parameter(fadeout, "in").toInt();
+ }
+ } else if (effect.attribute("id") == "fade_to_black") {
+ if (m_effectList.hasEffect(QString(), "fadeout") == -1) {
+ int effectDuration = EffectsList::parameter(effect, "in").toInt();
+ if (effectDuration > cropDuration().frames(m_fps)) {
+ effectDuration = cropDuration().frames(m_fps) / 2;
+ }
+ start -= effectDuration;
+ } else {
+ QDomElement fadeout = m_effectList.getEffectByTag(QString(), "fadeout");
+ start -= EffectsList::parameter(fadeout, "out").toInt() - EffectsList::parameter(fadeout, "in").toInt();
+ }
+ }
+ EffectsList::setParameter(effect, "in", QString::number(start));
+ EffectsList::setParameter(effect, "out", QString::number(end));
+ } else if (effect.attribute("id") == "fadein" || effect.attribute("id") == "fade_from_black") {
+ int start = cropStart().frames(m_fps);
+ int end = start;
+ if (effect.attribute("id") == "fadein") {
+ if (m_effectList.hasEffect(QString(), "fade_from_black") == -1) {
+ int effectDuration = EffectsList::parameter(effect, "out").toInt();
+ if (effectDuration > cropDuration().frames(m_fps)) {
+ effectDuration = cropDuration().frames(m_fps) / 2;
+ }
+ end += effectDuration;
+ } else
+ end += EffectsList::parameter(m_effectList.getEffectByTag(QString(), "fade_from_black"), "out").toInt();
+ } else if (effect.attribute("id") == "fade_from_black") {
+ if (m_effectList.hasEffect(QString(), "fadein") == -1) {
+ int effectDuration = EffectsList::parameter(effect, "out").toInt();
+ if (effectDuration > cropDuration().frames(m_fps)) {
+ effectDuration = cropDuration().frames(m_fps) / 2;
+ }
+ end += effectDuration;
+ } else
+ end += EffectsList::parameter(m_effectList.getEffectByTag(QString(), "fadein"), "out").toInt();
+ }
+ EffectsList::setParameter(effect, "in", QString::number(start));
+ EffectsList::setParameter(effect, "out", QString::number(end));
+ }
+ }
+}
+
+bool ClipItem::checkKeyFrames()
+{
+ bool clipEffectsModified = false;
+ // go through all effects this clip has
+ for (int ix = 0; ix < m_effectList.count(); ++ix) {
+ QStringList keyframeParams = keyframes(ix);
+ QStringList newKeyFrameParams;
+ bool effModified = false;
+
+ // go through all params which have keyframes
+ foreach(const QString &kfr, keyframeParams) {
+ const QStringList keyframes = kfr.split(';', QString::SkipEmptyParts);
+ QStringList newKeyFrames;
+ bool cutKeyFrame = false;
+ bool modified = false;
+ int lastPos = -1;
+ double lastValue = -1;
+ int start = cropStart().frames(m_fps);
+ int end = (cropStart() + cropDuration()).frames(m_fps);
+
+ // go through all keyframes for one param
+ foreach(const QString &str, keyframes) {
+ int pos = str.section(':', 0, 0).toInt();
+ double val = str.section(':', 1, 1).toDouble();
+ if (pos - start < 0) {
+ // a keyframe is defined before the start of the clip
+ cutKeyFrame = true;
+ } else if (cutKeyFrame) {
+ // create new keyframe at clip start, calculate interpolated value
+ if (pos > start) {
+ int diff = pos - lastPos;
+ double ratio = (double)(start - lastPos) / diff;
+ double newValue = lastValue + (val - lastValue) * ratio;
+ newKeyFrames.append(QString::number(start) + ':' + QString::number(newValue));
+ modified = true;
+ }
+ cutKeyFrame = false;
+ }
+ if (!cutKeyFrame) {
+ if (pos > end) {
+ // create new keyframe at clip end, calculate interpolated value
+ int diff = pos - lastPos;
+ if (diff != 0) {
+ double ratio = (double)(end - lastPos) / diff;
+ double newValue = lastValue + (val - lastValue) * ratio;
+ newKeyFrames.append(QString::number(end) + ':' + QString::number(newValue));
+ modified = true;
+ }
+ break;
+ } else {
+ newKeyFrames.append(QString::number(pos) + ':' + QString::number(val));
+ }
+ }
+ lastPos = pos;
+ lastValue = val;
+ }
+
+ newKeyFrameParams.append(newKeyFrames.join(";"));
+ if (modified)
+ effModified = true;
+ }
+
+ if (effModified) {
+ // update KeyFrames
+ setKeyframes(ix, newKeyFrameParams);
+ clipEffectsModified = true;
+ }
+ }
+ return clipEffectsModified;
+}
+
+void ClipItem::setKeyframes(const int ix, const QStringList keyframes)
+{
+ QDomElement effect = getEffectAt(ix);
+ if (effect.attribute("disable") == "1") return;
+ QDomNodeList params = effect.elementsByTagName("parameter");
+ int keyframeParams = 0;
+ for (int i = 0; i < params.count(); i++) {
+ QDomElement e = params.item(i).toElement();
+ if (!e.isNull() && (e.attribute("type") == "keyframe" || e.attribute("type") == "simplekeyframe")) {
+ e.setAttribute("keyframes", keyframes.at(keyframeParams));
+ if (ix == m_selectedEffect && keyframeParams == 0) {
+ m_keyframes.clear();
+ double max = e.attribute("max").toDouble();
+ double min = e.attribute("min").toDouble();
+ m_keyframeFactor = 100.0 / (max - min);
+ m_keyframeDefault = e.attribute("default").toDouble();
+ m_selectedKeyframe = 0;
+ // parse keyframes
+ const QStringList keyframes = e.attribute("keyframes").split(';', QString::SkipEmptyParts);
+ foreach(const QString &str, keyframes) {
+ int pos = str.section(':', 0, 0).toInt();
+ double val = str.section(':', 1, 1).toDouble();
+ m_keyframes[pos] = val;
+ }
+ if (m_keyframes.find(m_editedKeyframe) == m_keyframes.end()) m_editedKeyframe = -1;
+ if (m_keyframes.find(m_editedKeyframe) == m_keyframes.end()) m_editedKeyframe = -1;
+ update();
+ }
+ ++keyframeParams;
+ }
+ }
+}
+
+
+void ClipItem::setSelectedEffect(const int ix)
+{
+ m_selectedEffect = ix;
+ QDomElement effect = effectAt(m_selectedEffect);
+ if (effect.isNull() == false) {
+ QDomNodeList params = effect.elementsByTagName("parameter");
+ if (effect.attribute("disable") != "1")
+ for (int i = 0; i < params.count(); i++) {
+ QDomElement e = params.item(i).toElement();
+ if (!e.isNull() && (e.attribute("type") == "keyframe" || e.attribute("type") == "simplekeyframe")) {
+ m_keyframes.clear();
+ double max = e.attribute("max").toDouble();
+ double min = e.attribute("min").toDouble();
+ m_keyframeFactor = 100.0 / (max - min);
+ m_keyframeDefault = e.attribute("default").toDouble();
+ m_selectedKeyframe = 0;
+
+ // parse keyframes
+ const QStringList keyframes = e.attribute("keyframes").split(';', QString::SkipEmptyParts);
+ foreach(const QString &str, keyframes) {
+ int pos = str.section(':', 0, 0).toInt();
+ double val = str.section(':', 1, 1).toDouble();
+ m_keyframes[pos] = val;
+ }
+ if (m_keyframes.find(m_editedKeyframe) == m_keyframes.end()) m_editedKeyframe = -1;
+ update();
+ return;
+ }
+ }
+ }
+ if (!m_keyframes.isEmpty()) {
+ m_keyframes.clear();
+ update();
+ }
+}
+
+QStringList ClipItem::keyframes(const int index)
+{
+ QStringList result;
+ QDomElement effect = effectAt(index);
+ QDomNodeList params = effect.elementsByTagName("parameter");
+
+ for (int i = 0; i < params.count(); i++) {
+ QDomElement e = params.item(i).toElement();
+ if (!e.isNull() && (e.attribute("type") == "keyframe" || e.attribute("type") == "simplekeyframe"))
+ result.append(e.attribute("keyframes"));