X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fclipitem.cpp;h=fadf4228fa1f80f7b02aab950de23895bdab487f;hb=d5e2d9e691b22dab741ed689df6d87478ba24c9f;hp=06049d54cf2c26323c3a5b7a767a51029c589877;hpb=1fe9da9516fdff64e756068fd8e96dd18b925997;p=kdenlive diff --git a/src/clipitem.cpp b/src/clipitem.cpp index 06049d54..fadf4228 100644 --- a/src/clipitem.cpp +++ b/src/clipitem.cpp @@ -32,26 +32,27 @@ #include "clipitem.h" #include "customtrackview.h" +#include "customtrackscene.h" #include "renderer.h" #include "docclipbase.h" #include "transition.h" -#include "events.h" #include "kdenlivesettings.h" #include "kthumb.h" -ClipItem::ClipItem(DocClipBase *clip, ItemInfo info, GenTime cropStart, double scale, 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_thumbsRequested(0), m_hover(false) { - QRectF rect((double) info.startPos.frames(fps) * scale, (double)(info.track * KdenliveSettings::trackheight() + 1), (double)(info.endPos - info.startPos).frames(fps) * scale, (double)(KdenliveSettings::trackheight() - 1)); - setRect(rect); + +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); m_clipName = clip->name(); m_producer = clip->getId(); m_clipType = clip->clipType(); - m_cropStart = cropStart; - + m_cropStart = info.cropStart; m_maxDuration = clip->maxDuration(); setAcceptDrops(true); audioThumbReady = clip->audioThumbCreated(); + /* m_cropStart = xml.attribute("in", 0).toInt(); m_maxDuration = xml.attribute("duration", 0).toInt(); @@ -63,28 +64,33 @@ ClipItem::ClipItem(DocClipBase *clip, ItemInfo info, GenTime cropStart, double s setFlags(QGraphicsItem::ItemClipsToShape | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); setAcceptsHoverEvents(true); - connect(this , SIGNAL(prepareAudioThumb(double, QPainterPath, int, int)) , this, SLOT(slotPrepareAudioThumb(double, QPainterPath, int, int))); + connect(this , SIGNAL(prepareAudioThumb(double, int, int, int)) , this, SLOT(slotPrepareAudioThumb(double, int, int, int))); setBrush(QColor(141, 166, 215)); if (m_clipType == VIDEO || m_clipType == AV || m_clipType == SLIDESHOW) { m_hasThumbs = true; - connect(this, SIGNAL(getThumb(int, int)), clip->thumbProducer(), SLOT(extractImage(int, int))); - connect(clip->thumbProducer(), SIGNAL(thumbReady(int, QPixmap)), this, SLOT(slotThumbReady(int, QPixmap))); - connect(clip, SIGNAL(gotAudioData()), this, SLOT(slotGotAudioData())); - QTimer::singleShot(300, this, SLOT(slotFetchThumbs())); - startThumbTimer = new QTimer(this); startThumbTimer->setSingleShot(true); connect(startThumbTimer, SIGNAL(timeout()), this, SLOT(slotGetStartThumb())); endThumbTimer = new QTimer(this); endThumbTimer->setSingleShot(true); connect(endThumbTimer, SIGNAL(timeout()), this, SLOT(slotGetEndThumb())); + + connect(this, SIGNAL(getThumb(int, int)), clip->thumbProducer(), SLOT(extractImage(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 (m_clip->producer()) { + videoThumbProducer.init(this, m_clip->producer(), KdenliveSettings::trackheight() * KdenliveSettings::project_display_ratio(), KdenliveSettings::trackheight()); + slotFetchThumbs(); + }*/ } else if (m_clipType == COLOR) { QString colour = clip->getProperty("colour"); colour = colour.replace(0, 2, "#"); setBrush(QColor(colour.left(7))); } else if (m_clipType == IMAGE || m_clipType == TEXT) { - m_startPix = KThumb::getImage(KUrl(clip->getProperty("resource")), (int)(50 * KdenliveSettings::project_display_ratio()), 50); + m_startPix = KThumb::getImage(KUrl(clip->getProperty("resource")), (int)(KdenliveSettings::trackheight() * KdenliveSettings::project_display_ratio()), KdenliveSettings::trackheight()); m_endPix = m_startPix; } else if (m_clipType == AUDIO) { connect(clip, SIGNAL(gotAudioData()), this, SLOT(slotGotAudioData())); @@ -95,9 +101,163 @@ ClipItem::ClipItem(DocClipBase *clip, ItemInfo info, GenTime cropStart, double s ClipItem::~ClipItem() { if (startThumbTimer) delete startThumbTimer; if (endThumbTimer) delete endThumbTimer; + if (m_timeLine) m_timeLine; +} + +ClipItem *ClipItem::clone(ItemInfo info) const { + ClipItem *duplicate = new ClipItem(m_clip, info, m_fps); + 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->setSpeed(m_speed); + return duplicate; +} + +void ClipItem::setEffectList(const EffectsList effectList) { + m_effectList = effectList; + m_effectNames = m_effectList.effectNames().join(" / "); +} + +const EffectsList ClipItem::effectList() { + return m_effectList; +} + +int ClipItem::selectedEffectIndex() const { + return m_selectedEffect; +} + +void ClipItem::initEffect(QDomElement effect) { + // 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())); + // init keyframes if required + 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") { + 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(m_cropStart.frames(m_fps)) + ":" + def + ";" + QString::number((m_cropStart + m_cropDuration).frames(m_fps)) + ":" + def); + //kDebug() << "///// EFFECT KEYFRAMES INITED: " << e.attribute("keyframes"); + break; + } + } + } +} + +void ClipItem::setKeyframes(const int ix, const QString keyframes) { + QDomElement effect = effectAt(ix); + if (effect.attribute("disabled") == "1") return; + 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.setAttribute("keyframes", keyframes); + if (ix == m_selectedEffect) { + 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(); + // 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; + } + update(); + return; + } + break; + } + } +} + + +void ClipItem::setSelectedEffect(const int ix) { + m_selectedEffect = ix; + QDomElement effect = effectAt(m_selectedEffect); + QDomNodeList params = effect.elementsByTagName("parameter"); + if (effect.attribute("disabled") != "1") + for (int i = 0; i < params.count(); i++) { + QDomElement e = params.item(i).toElement(); + if (!e.isNull() && e.attribute("type") == "keyframe") { + 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(); + // 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; + } + update(); + return; + } + } + if (!m_keyframes.isEmpty()) { + m_keyframes.clear(); + update(); + } +} + +QString ClipItem::keyframes(const int index) { + QString 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") { + result = e.attribute("keyframes"); + break; + } + } + return result; +} + +void ClipItem::updateKeyframeEffect() { + // regenerate xml parameter from the clip keyframes + QDomElement effect = effectAt(m_selectedEffect); + if (effect.attribute("disabled") == "1") return; + 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") { + QString keyframes; + if (m_keyframes.count() > 1) { + QMap::const_iterator i = m_keyframes.constBegin(); + double x1; + double y1; + while (i != m_keyframes.constEnd()) { + keyframes.append(QString::number(i.key()) + ":" + QString::number(i.value()) + ";"); + ++i; + } + } + // Effect has a keyframe type parameter, we need to set the values + //kDebug() << "::::::::::::::: SETTING EFFECT KEYFRAMES: " << keyframes; + e.setAttribute("keyframes", keyframes); + break; + } + } +} + +QDomElement ClipItem::selectedEffect() { + if (m_selectedEffect == -1 || m_effectList.isEmpty()) return QDomElement(); + return effectAt(m_selectedEffect); } void ClipItem::resetThumbs() { + m_startPix = QPixmap(); + m_endPix = QPixmap(); slotFetchThumbs(); audioThumbCachePic.clear(); } @@ -105,50 +265,114 @@ void ClipItem::resetThumbs() { void ClipItem::refreshClip() { m_maxDuration = m_clip->maxDuration(); - if (m_clipType == VIDEO || m_clipType == AV || m_clipType == SLIDESHOW) slotFetchThumbs(); - else if (m_clipType == COLOR) { + if (m_clipType == COLOR) { QString colour = m_clip->getProperty("colour"); colour = colour.replace(0, 2, "#"); setBrush(QColor(colour.left(7))); - } else if (m_clipType == IMAGE || m_clipType == TEXT) { - m_startPix = KThumb::getImage(KUrl(m_clip->getProperty("resource")), (int)(50 * KdenliveSettings::project_display_ratio()), 50); - m_endPix = m_startPix; - } + } else slotFetchThumbs(); } void ClipItem::slotFetchThumbs() { - m_thumbsRequested += 2; - emit getThumb((int)m_cropStart.frames(m_fps), (int)(m_cropStart + m_cropDuration).frames(m_fps)); + 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); + } else { + if (m_endPix.isNull()) { + slotGetEndThumb(); + } + if (m_startPix.isNull()) { + slotGetStartThumb(); + } + } + /* + if (m_hasThumbs) { + if (m_endPix.isNull() && m_startPix.isNull()) { + int frame1 = (int)m_cropStart.frames(m_fps); + int frame2 = (int)(m_cropStart + m_cropDuration).frames(m_fps) - 1; + //videoThumbProducer.setThumbFrames(m_clip->producer(), frame1, frame2); + //videoThumbProducer.start(QThread::LowestPriority); + } else { + if (m_endPix.isNull()) slotGetEndThumb(); + else slotGetStartThumb(); + } + + } else if (m_startPix.isNull()) slotGetStartThumb();*/ } void ClipItem::slotGetStartThumb() { - m_thumbsRequested++; + m_startThumbRequested = true; emit getThumb((int)m_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_thumbsRequested++; - emit getThumb(-1, (int)(m_cropStart + m_cropDuration).frames(m_fps)); + m_endThumbRequested = true; + emit getThumb(-1, (int)(m_cropStart + m_cropDuration).frames(m_fps) - 1); + //videoThumbProducer.setThumbFrames(m_clip->producer(), -1, (int)(m_cropStart + m_cropDuration).frames(m_fps) - 1); + //videoThumbProducer.start(QThread::LowestPriority); +} + + +void ClipItem::slotSetStartThumb(QImage img) { + if (!img.isNull() && img.format() == QImage::Format_ARGB32) { + QPixmap pix = QPixmap::fromImage(img); + m_startPix = pix; + QRectF r = sceneBoundingRect(); + r.setRight(pix.width() + 2); + update(r); + } +} + +void ClipItem::slotSetEndThumb(QImage img) { + if (!img.isNull() && img.format() == QImage::Format_ARGB32) { + QPixmap pix = QPixmap::fromImage(img); + m_endPix = pix; + QRectF r = sceneBoundingRect(); + r.setLeft(r.right() - pix.width() - 2); + update(r); + } } void ClipItem::slotThumbReady(int frame, QPixmap pix) { - if (m_thumbsRequested == 0) return; - if (frame == m_cropStart.frames(m_fps)) m_startPix = pix; - else m_endPix = pix; - update(); - m_thumbsRequested--; + QRectF r = sceneBoundingRect(); + double width = m_startPix.width() / projectScene()->scale(); + if (m_startThumbRequested && frame == m_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) { + m_endPix = pix; + m_endThumbRequested = false; + double height = r.height(); + update(r.right() - width, r.y(), width, height); + } +} + +void ClipItem::slotSetStartThumb(QPixmap pix) { + m_startPix = pix; +} + +void ClipItem::slotSetEndThumb(QPixmap pix) { + m_endPix = pix; } void ClipItem::slotGotAudioData() { audioThumbReady = true; - update(); + if (m_clipType == AV) { + QRectF r = boundingRect(); + r.setTop(r.top() + r.height() / 2 - 1); + update(r); + } else update(); } int ClipItem::type() const { return AVWIDGET; } -DocClipBase *ClipItem::baseClip() { +DocClipBase *ClipItem::baseClip() const { return m_clip; } @@ -156,100 +380,123 @@ QDomElement ClipItem::xml() const { return m_clip->toXML(); } -int ClipItem::clipType() { +int ClipItem::clipType() const { return m_clipType; } -QString ClipItem::clipName() { +QString ClipItem::clipName() const { return m_clipName; } -int ClipItem::clipProducer() { +int ClipItem::clipProducer() const { return m_producer; } void ClipItem::flashClip() { if (m_timeLine == 0) { m_timeLine = new QTimeLine(750, this); + m_timeLine->setCurveShape(QTimeLine::EaseInOutCurve); connect(m_timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(animate(qreal))); } m_timeLine->start(); } void ClipItem::animate(qreal value) { - m_opacity = value; - update(); + QRectF r = boundingRect(); + r.setHeight(20); + update(r); } // virtual void ClipItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, - QWidget *widget) { + QWidget *) { painter->setOpacity(m_opacity); QBrush paintColor = brush(); if (isSelected()) paintColor = QBrush(QColor(79, 93, 121)); QRectF br = rect(); - double scale = br.width() / m_cropDuration.frames(m_fps); - QRect rectInView = visibleRect();//this is the rect that is visible by the user + QRectF exposed = option->exposedRect; + const double itemWidth = br.width(); + const double itemHeight = br.height(); + //kDebug() << "/// ITEM RECT: " << br << ", EPXOSED: " << option->exposedRect; + const double scale = option->matrix.m11(); - if (rectInView.isNull()) - return; - QPainterPath clippath; - clippath.addRect(rectInView); - - int startpixel = (int)(rectInView.x() - rect().x()); //start and endpixel that is viewable from rect() - - if (startpixel < 0) - startpixel = 0; - int endpixel = rectInView.width() + rectInView.x(); - if (endpixel < 0) - endpixel = 0; + // kDebug()<<"/// EXPOSED RECT: "<exposedRect.x()<<" X "<exposedRect.right(); //painter->setRenderHints(QPainter::Antialiasing); - QPainterPath roundRectPathUpper = upperRectPart(br), roundRectPathLower = lowerRectPart(br); - painter->setClipRect(option->exposedRect); + //QPainterPath roundRectPathUpper = upperRectPart(br), roundRectPathLower = lowerRectPart(br); + painter->setClipRect(exposed); // build path around clip + //QPainterPath resultClipPath = roundRectPathUpper.united(roundRectPathLower); + //painter->fillPath(resultClipPath, paintColor); + painter->fillRect(br, paintColor); - QPainterPath resultClipPath = roundRectPathUpper.united(roundRectPathLower); - - painter->setClipPath(resultClipPath.intersected(clippath), Qt::IntersectClip); - //painter->fillPath(roundRectPath, brush()); //, QBrush(QColor(Qt::red))); - painter->fillRect(br.intersected(rectInView), paintColor); - //painter->fillRect(QRectF(br.x() + br.width() - m_endPix.width(), br.y(), m_endPix.width(), br.height()), QBrush(QColor(Qt::black))); + //painter->setClipPath(resultClipPath, Qt::IntersectClip); // draw thumbnails - if (!m_startPix.isNull() && KdenliveSettings::videothumbnails()) { - if (m_clipType == IMAGE) { - painter->drawPixmap(QPointF(br.x() + br.width() - m_startPix.width(), br.y()), m_startPix); - QLineF l(br.x() + br.width() - m_startPix.width(), br.y(), br.x() + br.width() - m_startPix.width(), br.y() + br.height()); + painter->setMatrixEnabled(false); + + if (KdenliveSettings::videothumbnails()) { + QPen pen = painter->pen(); + pen.setStyle(Qt::DotLine); + pen.setColor(Qt::white); + painter->setPen(pen); + if (m_clipType == IMAGE && !m_startPix.isNull()) { + QPointF p1 = painter->matrix().map(QPointF(itemWidth, 0)) - QPointF(m_startPix.width(), 0); + QPointF p2 = painter->matrix().map(QPointF(itemWidth, itemHeight)) - QPointF(m_startPix.width(), 0); + painter->drawPixmap(p1, m_startPix); + QLineF l(p1, p2); painter->drawLine(l); - } else { - painter->drawPixmap(QPointF(br.x() + br.width() - m_endPix.width(), br.y()), m_endPix); - QLineF l(br.x() + br.width() - m_endPix.width(), br.y(), br.x() + br.width() - m_endPix.width(), br.y() + br.height()); + } else if (!m_endPix.isNull()) { + QPointF p1 = painter->matrix().map(QPointF(itemWidth, 0)) - QPointF(m_endPix.width(), 0); + QPointF p2 = painter->matrix().map(QPointF(itemWidth, itemHeight)) - QPointF(m_endPix.width(), 0); + painter->drawPixmap(p1, m_endPix); + QLineF l(p1, p2); painter->drawLine(l); } - - painter->drawPixmap(QPointF(br.x(), br.y()), m_startPix); - QLineF l2(br.x() + m_startPix.width(), br.y(), br.x() + m_startPix.width(), br.y() + br.height()); - painter->drawLine(l2); + if (!m_startPix.isNull()) { + QPointF p1 = painter->matrix().map(QPointF(0, 0)); + QPointF p2 = painter->matrix().map(QPointF(0, itemHeight)); + painter->drawPixmap(p1, m_startPix); + QLineF l2(p1.x() + m_startPix.width(), p1.y(), p2.x() + m_startPix.width(), p2.y()); + painter->drawLine(l2); + } + painter->setPen(Qt::black); } // draw audio thumbnails - if ((m_clipType == AV || m_clipType == AUDIO) && audioThumbReady && KdenliveSettings::audiothumbnails()) { - - QPainterPath path = m_clipType == AV ? roundRectPathLower : roundRectPathUpper.united(roundRectPathLower); - if (m_clipType == AV) painter->fillPath(path, QBrush(QColor(200, 200, 200, 140))); - - int channels = 2; + if (KdenliveSettings::audiothumbnails() && ((m_clipType == AV && option->exposedRect.bottom() > (itemHeight / 2)) || m_clipType == AUDIO) && audioThumbReady) { + + double startpixel = option->exposedRect.left(); // - pos().x(); + if (startpixel < 0) + startpixel = 0; + double endpixel = option->exposedRect.right(); + if (endpixel < 0) + endpixel = 0; + //kDebug()<<"/// REPAINTING AUDIO THMBS ZONE: "<matrix().mapRect(re); + painter->fillRect(mappedRect, QBrush(QColor(200, 200, 200, 140))); + } else mappedRect = painter->matrix().mapRect(re); + + int channels = baseClip()->getProperty("channels").toInt(); if (scale != framePixelWidth) audioThumbCachePic.clear(); - emit prepareAudioThumb(scale, path, startpixel, endpixel + 200);//200 more for less missing parts before repaint after scrolling - int cropLeft = (int)((m_cropStart).frames(m_fps) * scale); - for (int startCache = startpixel - startpixel % 100; startCache < endpixel + 300;startCache += 100) { + double cropLeft = m_cropStart.frames(m_fps); + emit prepareAudioThumb(scale, startpixel + cropLeft, endpixel + cropLeft, channels);//200 more for less missing parts before repaint after scrolling + int newstart = startpixel + cropLeft; + const int clipStart = mappedRect.x(); + for (int startCache = newstart - (newstart) % 100; startCache < endpixel + cropLeft; startCache += 100) { if (audioThumbCachePic.contains(startCache) && !audioThumbCachePic[startCache].isNull()) - painter->drawPixmap((int)(roundRectPathUpper.united(roundRectPathLower).boundingRect().x() + startCache - cropLeft), (int)(path.boundingRect().y()), audioThumbCachePic[startCache]); + //painter->drawPixmap((int)(startCache - cropLeft), (int)(path.boundingRect().y()), audioThumbCachePic[startCache]); + painter->drawPixmap(clipStart + startCache, mappedRect.y(), audioThumbCachePic[startCache]); } } @@ -269,11 +516,17 @@ void ClipItem::paint(QPainter *painter, pos = (*it).time() - cropStart(); if (pos > GenTime()) { if (pos > duration()) break; - framepos = scale * pos.frames(m_fps); - QLineF l(br.x() + framepos, br.y() + 5, br.x() + framepos, br.y() + br.height() - 5); - painter->drawLine(l); + QLineF l(br.x() + pos.frames(m_fps), br.y() + 5, br.x() + pos.frames(m_fps), br.bottom() - 5); + QLineF l2 = painter->matrix().map(l); + //framepos = scale * pos.frames(m_fps); + //QLineF l(framepos, 5, framepos, itemHeight - 5); + painter->drawLine(l2); if (KdenliveSettings::showmarkers()) { - const QRectF txtBounding = painter->boundingRect(br.x() + framepos + 1, br.y() + 10, br.width() - framepos - 2, br.height() - 10, Qt::AlignLeft | Qt::AlignTop, " " + (*it).comment() + " "); + framepos = br.x() + pos.frames(m_fps); + const QRectF r1(framepos + 0.04, 10, itemWidth - framepos - 2, itemHeight - 10); + const QRectF r2 = painter->matrix().map(r1).boundingRect(); + const QRectF txtBounding = painter->boundingRect(r2, Qt::AlignLeft | Qt::AlignTop, " " + (*it).comment() + " "); + QPainterPath path; path.addRoundedRect(txtBounding, 3, 3); painter->fillPath(path, markerBrush); @@ -284,154 +537,149 @@ void ClipItem::paint(QPainter *painter, } pen.setColor(Qt::black); pen.setStyle(Qt::SolidLine); + painter->setPen(pen); - - /* - // draw start / end fades - QBrush fades; - if (isSelected()) { - fades = QBrush(QColor(200, 50, 50, 150)); - } else fades = QBrush(QColor(200, 200, 200, 200)); - - if (m_startFade != 0) { - QPainterPath fadeInPath; - fadeInPath.moveTo(br.x() , br.y()); - fadeInPath.lineTo(br.x() , br.y() + br.height()); - fadeInPath.lineTo(br.x() + m_startFade * scale, br.y()); - fadeInPath.closeSubpath(); - painter->fillPath(fadeInPath, fades); - if (isSelected()) { - QLineF l(br.x() + m_startFade * scale, br.y(), br.x(), br.y() + br.height()); - painter->drawLine(l); - } - } - if (m_endFade != 0) { - QPainterPath fadeOutPath; - fadeOutPath.moveTo(br.x() + br.width(), br.y()); - fadeOutPath.lineTo(br.x() + br.width(), br.y() + br.height()); - fadeOutPath.lineTo(br.x() + br.width() - m_endFade * scale, br.y()); - fadeOutPath.closeSubpath(); - painter->fillPath(fadeOutPath, fades); - if (isSelected()) { - QLineF l(br.x() + br.width() - m_endFade * scale, br.y(), br.x() + br.width(), br.y() + br.height()); - painter->drawLine(l); - } - } - */ - - //pen.setStyle(Qt::DashDotDotLine); //Qt::DotLine); + // draw start / end fades + QBrush fades; + if (isSelected()) { + fades = QBrush(QColor(200, 50, 50, 150)); + } else fades = QBrush(QColor(200, 200, 200, 200)); + + if (m_startFade != 0) { + QPainterPath fadeInPath; + fadeInPath.moveTo(0, 0); + fadeInPath.lineTo(0, itemHeight); + fadeInPath.lineTo(m_startFade, 0); + fadeInPath.closeSubpath(); + QPainterPath f1 = painter->matrix().map(fadeInPath); + painter->fillPath(f1/*.intersected(resultClipPath)*/, fades); + /*if (isSelected()) { + QLineF l(m_startFade * scale, 0, 0, itemHeight); + painter->drawLine(l); + }*/ + } + if (m_endFade != 0) { + QPainterPath fadeOutPath; + fadeOutPath.moveTo(itemWidth, 0); + fadeOutPath.lineTo(itemWidth, itemHeight); + fadeOutPath.lineTo(itemWidth - m_endFade, 0); + fadeOutPath.closeSubpath(); + QPainterPath f1 = painter->matrix().map(fadeOutPath); + painter->fillPath(f1/*.intersected(resultClipPath)*/, fades); + /*if (isSelected()) { + QLineF l(itemWidth - m_endFade * scale, 0, itemWidth, itemHeight); + painter->drawLine(l); + }*/ + } // Draw effects names - QString effects = effectNames().join(" / "); - if (!effects.isEmpty()) { - QFont font = painter->font(); - QFont smallFont = font; - smallFont.setPointSize(8); - painter->setFont(smallFont); - QRectF txtBounding = painter->boundingRect(br, Qt::AlignLeft | Qt::AlignTop, " " + effects + " "); + if (!m_effectNames.isEmpty() && itemWidth * scale > 40) { + QRectF txtBounding = painter->boundingRect(painter->matrix().map(br).boundingRect(), Qt::AlignLeft | Qt::AlignTop, m_effectNames); + txtBounding.setRight(txtBounding.right() + 15); painter->setPen(Qt::white); - painter->fillRect(txtBounding, QBrush(QColor(0, 0, 0, 150))); - painter->drawText(txtBounding, Qt::AlignCenter, effects); + QBrush markerBrush(Qt::SolidPattern); + if (m_timeLine && m_timeLine->state() == QTimeLine::Running) { + qreal value = m_timeLine->currentValue(); + txtBounding.setWidth(txtBounding.width() * value); + markerBrush.setColor(QColor(50 + 200 * (1.0 - value), 50, 50, 100 + 50 * value)); + } else markerBrush.setColor(QColor(50, 50, 50, 150)); + QPainterPath path; + path.addRoundedRect(txtBounding, 4, 4); + painter->fillPath(path/*.intersected(resultClipPath)*/, markerBrush); + painter->drawText(txtBounding, Qt::AlignCenter, m_effectNames); painter->setPen(Qt::black); - painter->setFont(font); } - /* - // For testing puspose only: draw transitions count - { - painter->setPen(pen); - QFont font = painter->font(); - QFont smallFont = font; - smallFont.setPointSize(8); - painter->setFont(smallFont); - QString txt = " Transitions: " + QString::number(m_transitionsList.count()) + " "; - QRectF txtBoundin = painter->boundingRect(br, Qt::AlignRight | Qt::AlignTop, txt); - painter->fillRect(txtBoundin, QBrush(QColor(0, 0, 0, 150))); - painter->drawText(txtBoundin, Qt::AlignCenter, txt); - pen.setColor(Qt::black); - painter->setPen(pen); - painter->setFont(font); - } - */ // Draw clip name - QRectF txtBounding = painter->boundingRect(br, Qt::AlignHCenter | Qt::AlignTop, " " + m_clipName + " "); + QRectF txtBounding = painter->boundingRect(painter->matrix().map(br).boundingRect(), 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); txtBounding.translate(QPointF(1, 1)); painter->setPen(QColor(255, 255, 255, 255)); painter->drawText(txtBounding, Qt::AlignCenter, m_clipName); + + + // 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); + painter->drawPixmap(p1, projectScene()->m_transitionPixmap); + } + + // draw frame around clip if (isSelected()) { pen.setColor(Qt::red); - pen.setWidth(2); + //pen.setWidth(2); } else { pen.setColor(Qt::black); - pen.setWidth(1); + //pen.setWidth(1); } + + // draw effect or transition keyframes + if (itemWidth > 20) drawKeyFrames(painter, option->exposedRect); + + painter->setMatrixEnabled(true); + + // draw clip border + + //kDebug()<<"/// ITEM PAINTING:: exposed="<drawText(rect(), Qt::AlignCenter, m_name); - // painter->drawRect(boundingRect()); - //painter->drawRoundRect(-10, -10, 20, 20); - if (m_hover) { - int pointy = (int)(br.y() + br.height() / 2 - 5); - int pointx1 = (int)(br.x() + 10); - int pointx2 = (int)(br.x() + br.width() - 20); -#if 0 - painter->setPen(QPen(Qt::black)); - painter->setBrush(QBrush(QColor(50, 50, 0))); -#else - QRadialGradient gradient(pointx1 + 5, pointy + 5 , 5, 2, 2); - gradient.setColorAt(0.2, Qt::white); - gradient.setColorAt(0.8, Qt::yellow); - gradient.setColorAt(1, Qt::black); - painter->setBrush(gradient); -#endif - painter->drawEllipse(pointx1, pointy , 10, 10); - - QRadialGradient gradient1(pointx2 + 5, pointy + 5 , 5, 2, 2); - gradient1.setColorAt(0.2, Qt::white); - gradient1.setColorAt(0.8, Qt::yellow); - gradient1.setColorAt(1, Qt::black); - painter->setBrush(gradient1); - painter->drawEllipse(pointx2, pointy, 10, 10); - - } -} - - -OPERATIONTYPE ClipItem::operationMode(QPointF pos, double scale) { - if (qAbs((int)(pos.x() - (rect().x() + scale * m_startFade))) < 6 && qAbs((int)(pos.y() - rect().y())) < 6) return FADEIN; - else if (qAbs((int)(pos.x() - rect().x())) < 6) return RESIZESTART; - else if (qAbs((int)(pos.x() - (rect().x() + rect().width() - scale * m_endFade))) < 6 && qAbs((int)(pos.y() - rect().y())) < 6) return FADEOUT; - else if (qAbs((int)(pos.x() - (rect().x() + rect().width()))) < 6) return RESIZEEND; - else if (qAbs((int)(pos.x() - (rect().x() + 10))) < 6 && qAbs((int)(pos.y() - (rect().y() + rect().height() / 2 - 5))) < 6) return TRANSITIONSTART; - else if (qAbs((int)(pos.x() - (rect().x() + rect().width() - 20))) < 6 && qAbs((int)(pos.y() - (rect().y() + rect().height() / 2 - 5))) < 6) return TRANSITIONEND; + painter->drawRect(br); +} + +OPERATIONTYPE ClipItem::operationMode(QPointF pos) { + if (isSelected()) { + m_editedKeyframe = mouseOverKeyFrames(pos); + if (m_editedKeyframe != -1) return KEYFRAME; + } + QRectF rect = sceneBoundingRect(); + const double scale = projectScene()->scale(); + double maximumOffset; + if (scale > 3) maximumOffset = 25 / scale; + else maximumOffset = 6 / scale; + QMatrix matrix; + matrix.scale(scale, 0); + //kDebug()<<"// Item rect: "< ClipItem::snapMarkers() { +QList ClipItem::snapMarkers() const { QList < GenTime > snaps; QList < GenTime > markers = baseClip()->snapMarkers(); GenTime pos; @@ -447,14 +695,33 @@ QList ClipItem::snapMarkers() { return snaps; } -void ClipItem::slotPrepareAudioThumb(double pixelForOneFrame, QPainterPath path, int startpixel, int endpixel) { - int channels = 2; +QList ClipItem::commentedSnapMarkers() const { + QList < CommentedTime > snaps; + QList < CommentedTime > markers = baseClip()->commentedSnapMarkers(); + GenTime pos; + double framepos; + + for (int i = 0; i < markers.size(); i++) { + pos = markers.at(i).time() - cropStart(); + if (pos > GenTime()) { + if (pos > duration()) break; + else snaps.append(CommentedTime(pos + startPos(), markers.at(i).comment())); + } + } + return snaps; +} + +void ClipItem::slotPrepareAudioThumb(double pixelForOneFrame, int startpixel, int endpixel, int channels) { - QRectF re = path.boundingRect(); + //QRectF re = path.boundingRect(); + QRectF re = rect(); + if (m_clipType == AV) re.setTop(re.y() + re.height() / 2); + //QRectF re = rect(); //path.boundingRect(); + //kDebug() << "// PREP AUDIO THMB FRMO : " << startpixel << ", to: " << endpixel; //if ( (!audioThumbWasDrawn || framePixelWidth!=pixelForOneFrame ) && !baseClip()->audioFrameChache.isEmpty()){ - for (int startCache = startpixel - startpixel % 100;startCache + 100 < endpixel ;startCache += 100) { + for (int startCache = startpixel - startpixel % 100;startCache + 100 < endpixel + 100;startCache += 100) { //kDebug() << "creating " << startCache; //if (framePixelWidth!=pixelForOneFrame || if (framePixelWidth == pixelForOneFrame && audioThumbCachePic.contains(startCache)) @@ -527,24 +794,31 @@ void ClipItem::slotPrepareAudioThumb(double pixelForOneFrame, QPainterPath path, //} } +uint ClipItem::fadeIn() const { + return m_startFade; +} + +uint ClipItem::fadeOut() const { + return m_endFade; +} -void ClipItem::setFadeIn(int pos, double scale) { +void ClipItem::setFadeIn(int pos) { int oldIn = m_startFade; if (pos < 0) pos = 0; if (pos > m_cropDuration.frames(m_fps)) pos = (int)(m_cropDuration.frames(m_fps) / 2); m_startFade = pos; - if (oldIn > pos) update(rect().x(), rect().y(), oldIn * scale, rect().height()); - else update(rect().x(), rect().y(), pos * scale, rect().height()); + QRectF rect = boundingRect(); + update(rect.x(), rect.y(), qMax(oldIn, pos), rect.height()); } -void ClipItem::setFadeOut(int pos, double scale) { +void ClipItem::setFadeOut(int pos) { int oldOut = m_endFade; if (pos < 0) pos = 0; if (pos > m_cropDuration.frames(m_fps)) pos = (int)(m_cropDuration.frames(m_fps) / 2); m_endFade = pos; - if (oldOut > pos) update(rect().x() + rect().width() - pos * scale, rect().y(), pos * scale, rect().height()); - else update(rect().x() + rect().width() - oldOut * scale, rect().y(), oldOut * scale, rect().height()); + QRectF rect = boundingRect(); + update(rect.x() + rect.width() - qMax(oldOut, pos), rect.y(), pos, rect.height()); } @@ -565,31 +839,140 @@ void ClipItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) { } //virtual -void ClipItem::hoverEnterEvent(QGraphicsSceneHoverEvent *) { +void ClipItem::hoverEnterEvent(QGraphicsSceneHoverEvent *e) { + //if (e->pos().x() < 20) m_hover = true; m_hover = true; - update(); + QRectF r = boundingRect(); + double width = 35 / projectScene()->scale(); + double height = r.height() / 2; + update(r.x(), r.y() + height, width, height); + update(r.right() - width, r.y() + height, width, height); } //virtual void ClipItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { m_hover = false; - update(); + QRectF r = boundingRect(); + double width = 35 / projectScene()->scale(); + double height = r.height() / 2; + update(r.x(), r.y() + height, width, height); + update(r.right() - width, r.y() + height, width, height); } -void ClipItem::resizeStart(int posx, double scale) { - AbstractClipItem::resizeStart(posx, scale); - if (m_hasThumbs) startThumbTimer->start(100); +void ClipItem::resizeStart(int posx) { + 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); + 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)));*/ + startThumbTimer->start(100); + } } -void ClipItem::resizeEnd(int posx, double scale) { - AbstractClipItem::resizeEnd(posx, scale); - if (m_hasThumbs) endThumbTimer->start(100); +void ClipItem::resizeEnd(int posx) { + const int max = (startPos() - cropStart() + maxDuration()).frames(m_fps) + 1; + if (posx > max) posx = max; + if (posx == endPos().frames(m_fps)) return; + const int previous = (cropStart() + duration()).frames(m_fps); + AbstractClipItem::resizeEnd(posx); + 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); + } } -// virtual -void ClipItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event) { + +void ClipItem::checkEffectsKeyframesPos(const int previous, const int current, bool fromStart) { + for (int i = 0; i < m_effectList.size(); i++) { + QDomElement effect = m_effectList.at(i); + QDomNodeList params = effect.elementsByTagName("parameter"); + for (int j = 0; j < params.count(); j++) { + QDomElement e = params.item(i).toElement(); + if (e.attribute("type") == "keyframe") { + // parse keyframes and adjust values + const QStringList keyframes = e.attribute("keyframes").split(";", QString::SkipEmptyParts); + QMap kfr; + foreach(const QString str, keyframes) { + int pos = str.section(":", 0, 0).toInt(); + double val = str.section(":", 1, 1).toDouble(); + if (pos == previous) kfr[current] = val; + else { + if (fromStart && pos >= current) kfr[pos] = val; + else if (!fromStart && pos <= current) kfr[pos] = val; + } + } + QString newkfr; + QMap::const_iterator k = kfr.constBegin(); + while (k != kfr.constEnd()) { + newkfr.append(QString::number(k.key()) + ":" + QString::number(k.value()) + ";"); + ++k; + } + e.setAttribute("keyframes", newkfr); + break; + } + } + } + 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(); + QPointF newPos = value.toPointF(); + kDebug() << "/// MOVING CLIP ITEM.------------"; + 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(); + newTrack = qMin(newTrack, projectScene()->tracksCount() - 1); + newTrack = qMax(newTrack, 0); + newPos.setY((int)(newTrack * KdenliveSettings::trackheight() + 1)); + // Only one clip is moving + QRectF sceneShape = rect(); + sceneShape.translate(newPos); + QList items = scene()->items(sceneShape, Qt::IntersectsItemShape); + items.removeAll(this); + + 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); + newPos.setX(npos); + } else { + // get pos just after colliding clip + int npos = static_cast < AbstractClipItem* >(items.at(i))->endPos().frames(m_fps); + newPos.setX(npos); + } + m_track = newTrack; + m_startPos = GenTime((int) newPos.x(), m_fps); + return newPos; + } + } + } + m_track = newTrack; + m_startPos = GenTime((int) newPos.x(), m_fps); + //kDebug()<<"// ITEM NEW POS: "< m_effectList.count() - 1 || ix < 0) return QDomElement(); return m_effectList.at(ix); } @@ -610,44 +994,100 @@ void ClipItem::setEffectAt(int ix, QDomElement effect) { kDebug() << "CHange EFFECT AT: " << ix << ", CURR: " << m_effectList.at(ix).attribute("tag") << ", NEW: " << effect.attribute("tag"); m_effectList.insert(ix, effect); m_effectList.removeAt(ix + 1); - update(boundingRect()); + m_effectNames = m_effectList.effectNames().join(" / "); + if (effect.attribute("id") == "fadein" || effect.attribute("id") == "fadeout") update(boundingRect()); + else { + QRectF r = boundingRect(); + r.setHeight(20); + update(r); + } } -QMap ClipItem::addEffect(QDomElement effect, bool animate) { - QMap effectParams; +QHash ClipItem::addEffect(QDomElement effect, bool animate) { + QHash effectParams; + bool needRepaint = false; /*QDomDocument doc; doc.appendChild(doc.importNode(effect, true)); kDebug() << "/////// CLIP ADD EFFECT: "<< doc.toString();*/ m_effectList.append(effect); effectParams["tag"] = effect.attribute("tag"); + 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; 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()) { - effectParams[e.attribute("name")] = e.attribute("value"); - } - if (!e.attribute("factor").isEmpty()) { - effectParams[e.attribute("name")] = QString::number(effectParams[e.attribute("name")].toDouble() / e.attribute("factor").toDouble()); + 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"); + } + + double f = e.attribute("factor", "1").toDouble(); + + if (f == 1) { + effectParams[e.attribute("name")] = e.attribute("value"); + // check if it is a fade effect + if (effectId == "fadein") { + needRepaint = true; + if (e.attribute("name") == "out") fade += e.attribute("value").toInt(); + else if (e.attribute("name") == "in") fade -= e.attribute("value").toInt(); + } else if (effectId == "fadeout") { + needRepaint = true; + if (e.attribute("name") == "out") fade -= e.attribute("value").toInt(); + else if (e.attribute("name") == "in") fade += e.attribute("value").toInt(); + } + } else { + effectParams[e.attribute("name")] = QString::number(effectParams[e.attribute("name")].toDouble() / f); + } } } - if (animate) flashClip(); - update(boundingRect()); + m_effectNames = m_effectList.effectNames().join(" / "); + if (fade > 0) m_startFade = fade; + else if (fade < 0) m_endFade = -fade; + if (needRepaint) update(boundingRect()); + if (animate) { + flashClip(); + } else if (!needRepaint) { + QRectF r = boundingRect(); + r.setHeight(20); + update(r); + } + if (m_selectedEffect == -1) { + m_selectedEffect = 0; + setSelectedEffect(m_selectedEffect); + } return effectParams; } -QMap ClipItem::getEffectArgs(QDomElement effect) { - QMap effectParams; +QHash ClipItem::getEffectArgs(QDomElement effect) { + QHash effectParams; effectParams["tag"] = effect.attribute("tag"); effectParams["kdenlive_ix"] = effect.attribute("kdenlive_ix"); + effectParams["id"] = effect.attribute("id"); QString state = effect.attribute("disabled"); if (!state.isEmpty()) effectParams["disabled"] = state; QDomNodeList params = effect.elementsByTagName("parameter"); for (int i = 0; i < params.count(); i++) { QDomElement e = params.item(i).toElement(); - if (e.attribute("namedesc").contains(";")) { + //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"); + } else if (e.attribute("namedesc").contains(";")) { QString format = e.attribute("format"); QStringList separators = format.split("%d", QString::SkipEmptyParts); QStringList values = e.attribute("value").split(QRegExp("[,:;x]")); @@ -660,26 +1100,48 @@ QMap ClipItem::getEffectArgs(QDomElement effect) { txtNeu << (int)(values[i+1].toDouble()); } effectParams["start"] = neu; - } else - if (!e.isNull()) { - effectParams[e.attribute("name")] = e.attribute("value"); - } - if (!e.attribute("factor").isEmpty()) { - effectParams[e.attribute("name")] = QString::number(effectParams[e.attribute("name")].toDouble() / e.attribute("factor").toDouble()); + } 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"); } } return effectParams; } void ClipItem::deleteEffect(QString index) { + bool needRepaint = false; for (int i = 0; i < m_effectList.size(); ++i) { if (m_effectList.at(i).attribute("kdenlive_ix") == index) { + if (m_effectList.at(i).attribute("id") == "fadein") { + m_startFade = 0; + needRepaint = true; + } else if (m_effectList.at(i).attribute("id") == "fadeout") { + m_endFade = 0; + needRepaint = true; + } m_effectList.removeAt(i); break; } } + m_effectNames = m_effectList.effectNames().join(" / "); + if (needRepaint) update(boundingRect()); flashClip(); - update(boundingRect()); +} + +double ClipItem::speed() const { + return m_speed; +} + +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(); +} + +GenTime ClipItem::maxDuration() const { + return m_maxDuration / m_speed; } //virtual