X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fclipitem.cpp;h=1b493385ae4661d36d449d56581480c230129950;hb=c5720a8894dd5a0dbd06f3330299dc669454fa06;hp=30eb0eb6127bdf498cfc10e6885241dbfb51853a;hpb=93e45d239c3f3a1a80b0a087100e9df0c76525ce;p=kdenlive diff --git a/src/clipitem.cpp b/src/clipitem.cpp index 30eb0eb6..1b493385 100644 --- a/src/clipitem.cpp +++ b/src/clipitem.cpp @@ -40,10 +40,11 @@ #include "kthumb.h" -ClipItem::ClipItem(DocClipBase *clip, ItemInfo info, double fps) - : AbstractClipItem(info, QRectF(), fps), m_clip(clip), m_resizeMode(NONE), m_grabPoint(0), m_maxTrack(0), m_hasThumbs(false), startThumbTimer(NULL), endThumbTimer(NULL), m_effectsCounter(1), audioThumbWasDrawn(false), m_opacity(1.0), m_timeLine(0), m_startThumbRequested(false), m_endThumbRequested(false), m_startFade(0), m_endFade(0), m_hover(false), m_selectedEffect(-1), m_speed(1.0), framePixelWidth(0) { - setRect(0, 0, (info.endPos - info.startPos).frames(fps) - 0.02, (qreal)(KdenliveSettings::trackheight() - 2)); - setPos((qreal) info.startPos.frames(fps), (qreal)(info.track * KdenliveSettings::trackheight()) + 1); +ClipItem::ClipItem(DocClipBase *clip, ItemInfo info, double fps, bool generateThumbs) + : AbstractClipItem(info, QRectF(), fps), m_clip(clip), m_resizeMode(NONE), m_grabPoint(0), m_maxTrack(0), m_hasThumbs(false), startThumbTimer(NULL), endThumbTimer(NULL), audioThumbWasDrawn(false), m_opacity(1.0), m_timeLine(0), m_startThumbRequested(false), m_endThumbRequested(false), m_startFade(0), m_endFade(0), m_hover(false), m_selectedEffect(-1), m_speed(1.0), framePixelWidth(0), m_startPix(QPixmap()), m_endPix(QPixmap()) { + setZValue(1); + setRect(0, 0, (info.endPos - info.startPos).frames(fps) - 0.02, (double)(KdenliveSettings::trackheight() - 2)); + setPos(info.startPos.frames(fps), (double)(info.track * KdenliveSettings::trackheight()) + 1); m_clipName = clip->name(); m_producer = clip->getId(); @@ -77,9 +78,11 @@ ClipItem::ClipItem(DocClipBase *clip, ItemInfo info, double fps) connect(endThumbTimer, SIGNAL(timeout()), this, SLOT(slotGetEndThumb())); connect(this, SIGNAL(getThumb(int, int)), clip->thumbProducer(), SLOT(extractImage(int, int))); + //connect(this, SIGNAL(getThumb(int, int)), clip->thumbProducer(), SLOT(getVideoThumbs(int, int))); + connect(clip->thumbProducer(), SIGNAL(thumbReady(int, QPixmap)), this, SLOT(slotThumbReady(int, QPixmap))); connect(clip, SIGNAL(gotAudioData()), this, SLOT(slotGotAudioData())); - QTimer::singleShot(200, this, SLOT(slotFetchThumbs())); + if (generateThumbs) QTimer::singleShot(200, this, SLOT(slotFetchThumbs())); /*if (m_clip->producer()) { videoThumbProducer.init(this, m_clip->producer(), KdenliveSettings::trackheight() * KdenliveSettings::project_display_ratio(), KdenliveSettings::trackheight()); @@ -109,7 +112,7 @@ ClipItem *ClipItem::clone(ItemInfo info) const { if (info.cropStart == cropStart()) duplicate->slotSetStartThumb(m_startPix); if (info.cropStart + (info.endPos - info.startPos) == m_cropStart + m_cropDuration) 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->setEffectList(m_effectList.clone()); duplicate->setSpeed(m_speed); return duplicate; } @@ -136,6 +139,7 @@ void ClipItem::initEffect(QDomElement effect) { QDomNodeList params = effect.elementsByTagName("parameter"); for (int i = 0; i < params.count(); i++) { QDomElement e = params.item(i).toElement(); + kDebug() << "// inint eff: " << e.attribute("name"); if (!e.isNull() && e.attribute("type") == "keyframe") { QString def = e.attribute("default"); // Effect has a keyframe type parameter, we need to set the values @@ -146,6 +150,78 @@ void ClipItem::initEffect(QDomElement effect) { } } } + + if (effect.attribute("tag") == "volume") { + if (effect.attribute("id") == "fadeout") { + int end = (duration() + cropStart()).frames(m_fps); + int start = end - EffectsList::parameter(effect, "in").toInt(); + EffectsList::setParameter(effect, "in", QString::number(start)); + EffectsList::setParameter(effect, "out", QString::number(end)); + } else if (effect.attribute("id") == "fadein") { + int start = cropStart().frames(m_fps); + int end = start + EffectsList::parameter(effect, "out").toInt(); + EffectsList::setParameter(effect, "in", QString::number(start)); + EffectsList::setParameter(effect, "out", QString::number(end)); + } + } +} + +bool ClipItem::checkKeyFrames() { + bool clipEffectsModified = false; + for (int ix = 0; ix < m_effectList.count(); ix ++) { + QString kfr = keyframes(ix); + if (!kfr.isEmpty()) { + const QStringList keyframes = kfr.split(";", QString::SkipEmptyParts); + QStringList newKeyFrames; + bool cutKeyFrame = false; + bool modified = false; + int lastPos = -1; + double lastValue = -1; + int start = m_cropStart.frames(m_fps); + int end = (m_cropStart + m_cropDuration).frames(m_fps); + 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; + } + if (modified) { + // update KeyFrames + setKeyframes(ix, newKeyFrames.join(";")); + clipEffectsModified = true; + } + } + } + return clipEffectsModified; } void ClipItem::setKeyframes(const int ix, const QString keyframes) { @@ -276,7 +352,7 @@ void ClipItem::slotFetchThumbs() { if (m_endPix.isNull() && m_startPix.isNull()) { m_startThumbRequested = true; m_endThumbRequested = true; - emit getThumb((int)m_cropStart.frames(m_fps), (int)(m_cropStart + m_cropDuration).frames(m_fps) - 1); + emit getThumb((int)cropStart().frames(m_fps), (int)(cropStart() + cropDuration()).frames(m_fps) - 1); } else { if (m_endPix.isNull()) { slotGetEndThumb(); @@ -302,14 +378,14 @@ void ClipItem::slotFetchThumbs() { void ClipItem::slotGetStartThumb() { m_startThumbRequested = true; - emit getThumb((int)m_cropStart.frames(m_fps), -1); + emit getThumb((int)cropStart().frames(m_fps), -1); //videoThumbProducer.setThumbFrames(m_clip->producer(), (int)m_cropStart.frames(m_fps), - 1); //videoThumbProducer.start(QThread::LowestPriority); } void ClipItem::slotGetEndThumb() { m_endThumbRequested = true; - emit getThumb(-1, (int)(m_cropStart + m_cropDuration).frames(m_fps) - 1); + emit getThumb(-1, (int)(cropStart() + cropDuration()).frames(m_fps) - 1); //videoThumbProducer.setThumbFrames(m_clip->producer(), -1, (int)(m_cropStart + m_cropDuration).frames(m_fps) - 1); //videoThumbProducer.start(QThread::LowestPriority); } @@ -336,14 +412,15 @@ void ClipItem::slotSetEndThumb(QImage img) { } void ClipItem::slotThumbReady(int frame, QPixmap pix) { + if (scene() == NULL) return; QRectF r = sceneBoundingRect(); double width = m_startPix.width() / projectScene()->scale(); - if (m_startThumbRequested && frame == m_cropStart.frames(m_fps)) { + if (m_startThumbRequested && frame == cropStart().frames(m_fps)) { m_startPix = pix; m_startThumbRequested = false; double height = r.height(); update(r.x(), r.y(), width, height); - } else if (m_endThumbRequested && frame == (m_cropStart + m_cropDuration).frames(m_fps) - 1) { + } else if (m_endThumbRequested && frame == (cropStart() + cropDuration()).frames(m_fps) - 1) { m_endPix = pix; m_endThumbRequested = false; double height = r.height(); @@ -351,14 +428,22 @@ void ClipItem::slotThumbReady(int frame, QPixmap pix) { } } -void ClipItem::slotSetStartThumb(QPixmap pix) { +void ClipItem::slotSetStartThumb(const QPixmap pix) { m_startPix = pix; } -void ClipItem::slotSetEndThumb(QPixmap pix) { +void ClipItem::slotSetEndThumb(const QPixmap pix) { m_endPix = pix; } +QPixmap ClipItem::startThumb() const { + return m_startPix; +} + +QPixmap ClipItem::endThumb() const { + return m_endPix; +} + void ClipItem::slotGotAudioData() { audioThumbReady = true; if (m_clipType == AV) { @@ -411,7 +496,9 @@ void ClipItem::animate(qreal value) { void ClipItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { - painter->setOpacity(m_opacity); + /*if (parentItem()) m_opacity = 0.5; + else m_opacity = 1.0; + painter->setOpacity(m_opacity);*/ QBrush paintColor = brush(); if (isSelected()) paintColor = QBrush(QColor(79, 93, 121)); QRectF br = rect(); @@ -423,16 +510,15 @@ void ClipItem::paint(QPainter *painter, const double scale = option->matrix.m11(); - //painter->setRenderHints(QPainter::Antialiasing); //QPainterPath roundRectPathUpper = upperRectPart(br), roundRectPathLower = lowerRectPart(br); painter->setClipRect(exposed); - // build path around clip + //build path around clip //QPainterPath resultClipPath = roundRectPathUpper.united(roundRectPathLower); //painter->fillPath(resultClipPath, paintColor); - painter->fillRect(br, paintColor); + painter->fillRect(exposed, paintColor); //painter->setClipPath(resultClipPath, Qt::IntersectClip); @@ -468,7 +554,7 @@ void ClipItem::paint(QPainter *painter, } // draw audio thumbnails - if (KdenliveSettings::audiothumbnails() && ((m_clipType == AV && exposed.bottom() > (itemHeight / 2)) || m_clipType == AUDIO) && audioThumbReady) { + if (KdenliveSettings::audiothumbnails() && m_speed == 1.0 && ((m_clipType == AV && exposed.bottom() > (itemHeight / 2)) || m_clipType == AUDIO) && audioThumbReady) { double startpixel = exposed.left(); if (startpixel < 0) @@ -496,7 +582,9 @@ void ClipItem::paint(QPainter *painter, const int mappedEndPixel = painter->matrix().map(QPointF(endpixel + cropLeft, 0)).x() - clipStart; cropLeft = cropLeft * scale; - emit prepareAudioThumb(scale, mappedStartPixel, mappedEndPixel, channels); + if (channels >= 1) { + emit prepareAudioThumb(scale, mappedStartPixel, mappedEndPixel, channels); + } for (int startCache = mappedStartPixel - (mappedStartPixel) % 100; startCache < mappedEndPixel; startCache += 100) { if (audioThumbCachePic.contains(startCache) && !audioThumbCachePic[startCache].isNull()) @@ -594,12 +682,11 @@ void ClipItem::paint(QPainter *painter, painter->setPen(Qt::black); } - // Draw clip name - QRectF txtBounding = painter->boundingRect(mapped, Qt::AlignHCenter | Qt::AlignTop, " " + m_clipName + " "); - //painter->fillRect(txtBounding, QBrush(QColor(255, 255, 255, 150))); - painter->setPen(QColor(0, 0, 0, 180)); - painter->drawText(txtBounding, Qt::AlignCenter, m_clipName); + QRectF txtBounding = painter->boundingRect(mapped, Qt::AlignHCenter | Qt::AlignVCenter, " " + m_clipName + " "); + painter->fillRect(txtBounding, QBrush(QColor(0, 0, 0, 150))); + //painter->setPen(QColor(0, 0, 0, 180)); + //painter->drawText(txtBounding, Qt::AlignCenter, m_clipName); txtBounding.translate(QPointF(1, 1)); painter->setPen(QColor(255, 255, 255, 255)); painter->drawText(txtBounding, Qt::AlignCenter, m_clipName); @@ -607,11 +694,9 @@ void ClipItem::paint(QPainter *painter, // draw transition handles on hover if (m_hover && itemWidth * scale > 40) { - QPainterPath transitionHandle; - const int handle_size = 4; QPointF p1 = painter->matrix().map(QPointF(0, itemHeight / 2)) + QPointF(10, 0); painter->drawPixmap(p1, projectScene()->m_transitionPixmap); - p1 = painter->matrix().map(QPointF(itemWidth, itemHeight / 2)) - QPointF(10 + handle_size * 3, 0); + p1 = painter->matrix().map(QPointF(itemWidth, itemHeight / 2)) - QPointF(22, 0); painter->drawPixmap(p1, projectScene()->m_transitionPixmap); } @@ -634,11 +719,10 @@ void ClipItem::paint(QPainter *painter, //kDebug()<<"/// ITEM PAINTING:: exposed="< ClipItem::commentedSnapMarkers() const { } void ClipItem::slotPrepareAudioThumb(double pixelForOneFrame, int startpixel, int endpixel, int channels) { - QRectF re = sceneBoundingRect(); if (m_clipType == AV) re.setTop(re.y() + re.height() / 2); @@ -741,6 +819,12 @@ void ClipItem::slotPrepareAudioThumb(double pixelForOneFrame, int startpixel, in pixpainter.setPen(audiopen); //pixpainter.setRenderHint(QPainter::Antialiasing,true); //pixpainter.drawLine(0,0,100,re.height()); + // Bail out, if caller provided invalid data + if (channels <= 0) { + kWarning() << "Unable to draw image with " << channels << "number of channels"; + return; + } + int channelHeight = audioThumbCachePic[startCache].height() / channels; for (int i = 0;i < channels;i++) { @@ -806,21 +890,23 @@ uint ClipItem::fadeOut() const { void ClipItem::setFadeIn(int pos) { + if (pos == m_startFade) return; int oldIn = m_startFade; if (pos < 0) pos = 0; - if (pos > m_cropDuration.frames(m_fps)) pos = (int)(m_cropDuration.frames(m_fps) / 2); + if (pos > m_cropDuration.frames(m_fps)) pos = (int)(m_cropDuration.frames(m_fps)); m_startFade = pos; QRectF rect = boundingRect(); update(rect.x(), rect.y(), qMax(oldIn, pos), rect.height()); } void ClipItem::setFadeOut(int pos) { + if (pos == m_endFade) return; int oldOut = m_endFade; if (pos < 0) pos = 0; - if (pos > m_cropDuration.frames(m_fps)) pos = (int)(m_cropDuration.frames(m_fps) / 2); + if (pos > m_cropDuration.frames(m_fps)) pos = (int)(m_cropDuration.frames(m_fps)); m_endFade = pos; QRectF rect = boundingRect(); - update(rect.x() + rect.width() - qMax(oldOut, pos), rect.y(), pos, rect.height()); + update(rect.x() + rect.width() - qMax(oldOut, pos), rect.y(), qMax(oldOut, pos), rect.height()); } @@ -863,12 +949,12 @@ void ClipItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { update(r.right() - width, r.y() + height, width, height); } -void ClipItem::resizeStart(int posx) { +void ClipItem::resizeStart(int posx, double speed) { const int min = (startPos() - cropStart()).frames(m_fps); if (posx < min) posx = min; if (posx == startPos().frames(m_fps)) return; const int previous = cropStart().frames(m_fps); - AbstractClipItem::resizeStart(posx); + AbstractClipItem::resizeStart(posx, m_speed); checkEffectsKeyframesPos(previous, cropStart().frames(m_fps), true); if (m_hasThumbs && KdenliveSettings::videothumbnails()) { /*connect(m_clip->thumbProducer(), SIGNAL(thumbReady(int, QPixmap)), this, SLOT(slotThumbReady(int, QPixmap)));*/ @@ -876,13 +962,14 @@ void ClipItem::resizeStart(int posx) { } } -void ClipItem::resizeEnd(int posx) { +void ClipItem::resizeEnd(int posx, double speed, bool updateKeyFrames) { const int max = (startPos() - cropStart() + maxDuration()).frames(m_fps) + 1; if (posx > max) posx = max; if (posx == endPos().frames(m_fps)) return; + //kDebug() << "// NEW POS: " << posx << ", OLD END: " << endPos().frames(m_fps); const int previous = (cropStart() + duration()).frames(m_fps); - AbstractClipItem::resizeEnd(posx); - checkEffectsKeyframesPos(previous, (cropStart() + duration()).frames(m_fps), false); + AbstractClipItem::resizeEnd(posx, m_speed); + if (updateKeyFrames) checkEffectsKeyframesPos(previous, (cropStart() + duration()).frames(m_fps), false); if (m_hasThumbs && KdenliveSettings::videothumbnails()) { /*connect(m_clip->thumbProducer(), SIGNAL(thumbReady(int, QPixmap)), this, SLOT(slotThumbReady(int, QPixmap)));*/ endThumbTimer->start(100); @@ -900,9 +987,11 @@ void ClipItem::checkEffectsKeyframesPos(const int previous, const int current, b // parse keyframes and adjust values const QStringList keyframes = e.attribute("keyframes").split(";", QString::SkipEmptyParts); QMap kfr; + int pos; + double val; foreach(const QString str, keyframes) { - int pos = str.section(":", 0, 0).toInt(); - double val = str.section(":", 1, 1).toDouble(); + pos = str.section(":", 0, 0).toInt(); + val = str.section(":", 1, 1).toDouble(); if (pos == previous) kfr[current] = val; else { if (fromStart && pos >= current) kfr[pos] = val; @@ -923,18 +1012,17 @@ void ClipItem::checkEffectsKeyframesPos(const int previous, const int current, b if (m_selectedEffect >= 0) setSelectedEffect(m_selectedEffect); } - //virtual QVariant ClipItem::itemChange(GraphicsItemChange change, const QVariant &value) { if (change == ItemPositionChange && scene()) { // calculate new position. - if (group() != 0) return pos(); + if (parentItem()) return pos(); QPointF newPos = value.toPointF(); - //kDebug() << "/// MOVING CLIP ITEM.------------"; + //kDebug() << "/// MOVING CLIP ITEM.------------\n++++++++++"; int xpos = projectScene()->getSnapPointForPos((int) newPos.x(), KdenliveSettings::snaptopoints()); xpos = qMax(xpos, 0); newPos.setX(xpos); - int newTrack = (newPos.y() + KdenliveSettings::trackheight() / 2) / KdenliveSettings::trackheight(); + int newTrack = newPos.y() / KdenliveSettings::trackheight(); newTrack = qMin(newTrack, projectScene()->tracksCount() - 1); newTrack = qMax(newTrack, 0); newPos.setY((int)(newTrack * KdenliveSettings::trackheight() + 1)); @@ -943,38 +1031,37 @@ QVariant ClipItem::itemChange(GraphicsItemChange change, const QVariant &value) sceneShape.translate(newPos); QList items = scene()->items(sceneShape, Qt::IntersectsItemShape); items.removeAll(this); - + bool forwardMove = newPos.x() > pos().x(); + int offset = 0; if (!items.isEmpty()) { for (int i = 0; i < items.count(); i++) { if (items.at(i)->type() == type()) { // Collision! QPointF otherPos = items.at(i)->pos(); - if ((int) otherPos.y() != (int) pos().y()) return pos(); - if (pos().x() < otherPos.x()) { - // move clip just before colliding clip - int npos = (static_cast < AbstractClipItem* >(items.at(i))->startPos() - m_cropDuration).frames(m_fps); - // check we don't run into another clip - newPos.setX(npos); - sceneShape = rect(); - sceneShape.translate(newPos); - QList subitems = scene()->items(sceneShape, Qt::IntersectsItemShape); - items.removeAll(this); - for (int j = 0; j < subitems.count(); j++) { - if (subitems.at(j)->type() == type()) return pos(); - } + if ((int) otherPos.y() != (int) pos().y()) { + return pos(); + } + if (forwardMove) { + offset = qMax(offset, (int)(newPos.x() - (static_cast < AbstractClipItem* >(items.at(i))->startPos() - m_cropDuration).frames(m_fps))); } else { - // get pos just after colliding clip - int npos = static_cast < AbstractClipItem* >(items.at(i))->endPos().frames(m_fps); - // check we don't run into another clip - newPos.setX(npos); - sceneShape = rect(); - sceneShape.translate(newPos); + offset = qMax(offset, (int)((static_cast < AbstractClipItem* >(items.at(i))->endPos().frames(m_fps)) - newPos.x())); + } + + if (offset > 0) { + if (forwardMove) { + sceneShape.translate(QPointF(-offset, 0)); + newPos.setX(newPos.x() - offset); + } else { + sceneShape.translate(QPointF(offset, 0)); + newPos.setX(newPos.x() + offset); + } QList subitems = scene()->items(sceneShape, Qt::IntersectsItemShape); - items.removeAll(this); + subitems.removeAll(this); for (int j = 0; j < subitems.count(); j++) { if (subitems.at(j)->type() == type()) return pos(); } } + m_track = newTrack; m_startPos = GenTime((int) newPos.x(), m_fps); return newPos; @@ -994,13 +1081,17 @@ QVariant ClipItem::itemChange(GraphicsItemChange change, const QVariant &value) }*/ int ClipItem::effectsCounter() { - return m_effectsCounter++; + return effectsCount() + 1; } int ClipItem::effectsCount() { return m_effectList.size(); } +int ClipItem::hasEffect(const QString &tag, const QString &id) const { + return m_effectList.hasEffect(tag, id); +} + QStringList ClipItem::effectNames() { return m_effectList.effectNames(); } @@ -1011,7 +1102,8 @@ QDomElement ClipItem::effectAt(int ix) { } void ClipItem::setEffectAt(int ix, QDomElement effect) { - kDebug() << "CHange EFFECT AT: " << ix << ", CURR: " << m_effectList.at(ix).attribute("tag") << ", NEW: " << effect.attribute("tag"); + kDebug() << "CHange EFFECT AT: " << ix << ", CURR: " << m_effectList.at(ix).attribute("tag") << ", NEW: " << effect.attribute("tag"); + effect.setAttribute("kdenlive_ix", ix + 1); m_effectList.insert(ix, effect); m_effectList.removeAt(ix + 1); m_effectNames = m_effectList.effectNames().join(" / "); @@ -1023,38 +1115,47 @@ void ClipItem::setEffectAt(int ix, QDomElement effect) { } } -QHash ClipItem::addEffect(QDomElement effect, bool animate) { - QHash effectParams; +EffectsParameterList ClipItem::addEffect(QDomElement effect, bool animate) { + bool needRepaint = false; /*QDomDocument doc; doc.appendChild(doc.importNode(effect, true)); - kDebug() << "/////// CLIP ADD EFFECT: "<< doc.toString();*/ + kDebug() << "/////// CLIP ADD EFFECT: " << doc.toString();*/ m_effectList.append(effect); - effectParams["tag"] = effect.attribute("tag"); + + EffectsParameterList parameters; + parameters.addParam("tag", effect.attribute("tag")); + parameters.addParam("kdenlive_ix", effect.attribute("kdenlive_ix")); + if (effect.hasAttribute("src")) parameters.addParam("src", effect.attribute("src")); + + QString state = effect.attribute("disabled"); + if (!state.isEmpty()) { + parameters.addParam("disabled", state); + } + QString effectId = effect.attribute("id"); if (effectId.isEmpty()) effectId = effect.attribute("tag"); - effectParams["id"] = effectId; - effectParams["kdenlive_ix"] = effect.attribute("kdenlive_ix"); - QString state = effect.attribute("disabled"); - if (!state.isEmpty()) effectParams["disabled"] = state; + parameters.addParam("id", effectId); + QDomNodeList params = effect.elementsByTagName("parameter"); int fade = 0; for (int i = 0; i < params.count(); i++) { QDomElement e = params.item(i).toElement(); if (!e.isNull()) { if (e.attribute("type") == "keyframe") { - effectParams["keyframes"] = e.attribute("keyframes"); - effectParams["min"] = e.attribute("min"); - effectParams["max"] = e.attribute("max"); - effectParams["factor"] = e.attribute("factor", "1"); - effectParams["starttag"] = e.attribute("starttag", "start"); - effectParams["endtag"] = e.attribute("endtag", "end"); + parameters.addParam("keyframes", e.attribute("keyframes")); + parameters.addParam("max", e.attribute("max")); + parameters.addParam("min", e.attribute("min")); + parameters.addParam("factor", e.attribute("factor", "1")); + parameters.addParam("starttag", e.attribute("starttag", "start")); + parameters.addParam("endtag", e.attribute("endtag", "end")); } double f = e.attribute("factor", "1").toDouble(); if (f == 1) { - effectParams[e.attribute("name")] = e.attribute("value"); + parameters.addParam(e.attribute("name"), e.attribute("value")); + // check if it is a fade effect if (effectId == "fadein") { needRepaint = true; @@ -1066,7 +1167,7 @@ QHash ClipItem::addEffect(QDomElement effect, bool animate) { else if (e.attribute("name") == "in") fade += e.attribute("value").toInt(); } } else { - effectParams[e.attribute("name")] = QString::number(effectParams[e.attribute("name")].toDouble() / f); + parameters.addParam(e.attribute("name"), QString::number(e.attribute("value").toDouble() / f)); } } } @@ -1085,28 +1186,32 @@ QHash ClipItem::addEffect(QDomElement effect, bool animate) { m_selectedEffect = 0; setSelectedEffect(m_selectedEffect); } - return effectParams; + return parameters; } -QHash ClipItem::getEffectArgs(QDomElement effect) { - QHash effectParams; - effectParams["tag"] = effect.attribute("tag"); - effectParams["kdenlive_ix"] = effect.attribute("kdenlive_ix"); - effectParams["id"] = effect.attribute("id"); +EffectsParameterList ClipItem::getEffectArgs(QDomElement effect) { + EffectsParameterList parameters; + parameters.addParam("tag", effect.attribute("tag")); + parameters.addParam("kdenlive_ix", effect.attribute("kdenlive_ix")); + parameters.addParam("id", effect.attribute("id")); + if (effect.hasAttribute("src")) parameters.addParam("src", effect.attribute("src")); QString state = effect.attribute("disabled"); - if (!state.isEmpty()) effectParams["disabled"] = state; + if (!state.isEmpty()) { + parameters.addParam("disabled", state); + } + QDomNodeList params = effect.elementsByTagName("parameter"); for (int i = 0; i < params.count(); i++) { QDomElement e = params.item(i).toElement(); //kDebug() << "/ / / /SENDING EFFECT PARAM: " << e.attribute("type") << ", NAME_ " << e.attribute("tag"); if (e.attribute("type") == "keyframe") { kDebug() << "/ / / /SENDING KEYFR EFFECT TYPE"; - effectParams["keyframes"] = e.attribute("keyframes"); - effectParams["max"] = e.attribute("max"); - effectParams["min"] = e.attribute("min"); - effectParams["factor"] = e.attribute("factor", "1"); - effectParams["starttag"] = e.attribute("starttag", "start"); - effectParams["endtag"] = e.attribute("endtag", "end"); + parameters.addParam("keyframes", e.attribute("keyframes")); + parameters.addParam("max", e.attribute("max")); + parameters.addParam("min", e.attribute("min")); + parameters.addParam("factor", e.attribute("factor", "1")); + parameters.addParam("starttag", e.attribute("starttag", "start")); + parameters.addParam("endtag", e.attribute("endtag", "end")); } else if (e.attribute("namedesc").contains(";")) { QString format = e.attribute("format"); QStringList separators = format.split("%d", QString::SkipEmptyParts); @@ -1119,20 +1224,24 @@ QHash ClipItem::getEffectArgs(QDomElement effect) { txtNeu << separators[i]; txtNeu << (int)(values[i+1].toDouble()); } - effectParams["start"] = neu; + parameters.addParam("start", neu); } else { - if (e.attribute("factor", "1") != "1") - effectParams[e.attribute("name")] = QString::number(e.attribute("value").toDouble() / e.attribute("factor").toDouble()); - else effectParams[e.attribute("name")] = e.attribute("value"); + if (e.attribute("factor", "1") != "1") { + parameters.addParam(e.attribute("name"), QString::number(e.attribute("value").toDouble() / e.attribute("factor").toDouble())); + } else { + parameters.addParam(e.attribute("name"), e.attribute("value")); + } } } - return effectParams; + return parameters; } void ClipItem::deleteEffect(QString index) { bool needRepaint = false; + QString ix; for (int i = 0; i < m_effectList.size(); ++i) { - if (m_effectList.at(i).attribute("kdenlive_ix") == index) { + ix = m_effectList.at(i).attribute("kdenlive_ix"); + if (ix == index) { if (m_effectList.at(i).attribute("id") == "fadein") { m_startFade = 0; needRepaint = true; @@ -1141,8 +1250,7 @@ void ClipItem::deleteEffect(QString index) { needRepaint = true; } m_effectList.removeAt(i); - break; - } + } else if (ix.toInt() > index.toInt()) m_effectList[i].setAttribute("kdenlive_ix", ix.toInt() - 1); } m_effectNames = m_effectList.effectNames().join(" / "); if (needRepaint) update(boundingRect()); @@ -1157,13 +1265,25 @@ void ClipItem::setSpeed(const double speed) { m_speed = speed; if (m_speed == 1.0) m_clipName = baseClip()->name(); else m_clipName = baseClip()->name() + " - " + QString::number(speed * 100, 'f', 0) + "%"; - update(); + //update(); } GenTime ClipItem::maxDuration() const { return m_maxDuration / m_speed; } +GenTime ClipItem::cropStart() const { + return m_cropStart / m_speed; +} + +GenTime ClipItem::cropDuration() const { + return m_cropDuration / m_speed; +} + +GenTime ClipItem::endPos() const { + return m_startPos + cropDuration(); +} + //virtual void ClipItem::dropEvent(QGraphicsSceneDragDropEvent * event) { QString effects = QString(event->mimeData()->data("kdenlive/effectslist"));