Commit 3d50e4f7 authored by Jason Wood's avatar Jason Wood

Added in/outpoint support

Added ProjectClip support
Refactored scenelist generation to support ProjectClips
Added drag'n'drop support to KMMMonitor.

svn path=/trunk/kdenlive/; revision=248
parent 7d1f451e
......@@ -22,7 +22,8 @@
#include "avfile.h"
AVFile::AVFile(const QString &name, const KURL &url) :
m_duration(0.0)
m_duration(0.0),
m_framesPerSecond(0)
{
if(name.isNull()) {
setName(url.filename());
......@@ -53,17 +54,26 @@ void AVFile::calculateFileProperties(const QMap<QString, QString> &attributes)
if(attributes.contains("duration")) {
m_duration = GenTime(attributes["duration"].toDouble());
m_durationKnown = true;
m_durationKnown = true;
} else {
// No duration known, use an arbitrary one until it is.
m_duration = GenTime(0.0);
m_durationKnown = false;
m_duration = GenTime(0.0);
m_durationKnown = false;
}
if(attributes.contains("fps"))
{
m_framesPerSecond = attributes["fps"].toInt();
} else {
// No frame rate known.
m_framesPerSecond = 0;
}
} else {
/** If the file is not local, then no file properties are currently returned */
m_duration = GenTime(0.0);
m_durationKnown = false;
m_durationKnown = false;
m_framesPerSecond = 0;
m_filesize = -1;
}
}
......
......@@ -62,6 +62,8 @@ Returns the number of references to this AVFile. */
GenTime duration() const;
/** Returns true if this AVFile has a known duration, false if it is, as of yet, undetermined. */
bool durationKnown();
int framesPerSecond() const { return m_framesPerSecond; }
private: // Private attributes
/** Holds the url for this AV file. */
KURL m_url;;
......@@ -75,6 +77,8 @@ private: // Private attributes
bool m_durationKnown;
/** A list of all DocClipAVFiles which make use of this AVFile. This is used so that we can clean up if we decide to delete an AVFile. */
int m_refCount;
/** The number of frames per second that this AVFile runs at. */
int m_framesPerSecond;
};
#endif
......@@ -44,9 +44,9 @@ QDomDocument AVFileList::toXML() const
while(itt.current()) {
QDomElement elem = doc.createElement("AVFile");
elem.setAttribute("url", itt.current()->fileURL().path());
if(itt.current()->durationKnown()) {
elem.setAttribute("duration", QString(current()->duration().seconds()));
}
if(itt.current()->durationKnown()) {
elem.setAttribute("duration", QString::number(itt.current()->duration().seconds()));
}
doc.documentElement().appendChild(elem);
++itt;
}
......
......@@ -125,7 +125,7 @@ DocClipBaseList ClipDrag::decode(KdenliveDoc *doc, const QMimeSource *e)
node = node.nextSibling();
}
} else {
KURL::List list;
KURL::List list;
KURL::List::Iterator it;
KURLDrag::decode(e, list);
......
......@@ -80,7 +80,7 @@ QDomDocument DocClipAVFile::toXML() {
ASSERT(node.isNull());
/* This final return should never be reached, it is here to remove compiler warning. */
/* This final return should never be reached, it is here to remove compiler warning. */
return doc;
}
......@@ -107,3 +107,60 @@ bool DocClipAVFile::durationKnown()
{
return m_avFile->durationKnown();
}
// virtual
int DocClipAVFile::framesPerSecond() const
{
return m_avFile->framesPerSecond();
}
// virtual
QDomDocument DocClipAVFile::generateSceneList()
{
static QString str_inpoint="inpoint";
static QString str_outpoint="outpoint";
static QString str_file="file";
QDomDocument scenelist;
scenelist.appendChild(scenelist.createElement("scenelist"));
// generate the next scene.
QDomElement scene = scenelist.createElement("scene");
scene.setAttribute("duration", QString::number(duration().seconds()));
QDomElement sceneClip;
sceneClip = scenelist.createElement("input");
sceneClip.setAttribute(str_file, fileURL().path());
sceneClip.setAttribute(str_inpoint, "0.0");
sceneClip.setAttribute(str_outpoint, QString::number(duration().seconds()));
scene.appendChild(sceneClip);
scenelist.documentElement().appendChild(scene);
return scenelist;
}
// virtual
QDomDocument DocClipAVFile::sceneToXML(const GenTime &startTime, const GenTime &endTime)
{
static QString str_inpoint="inpoint";
static QString str_outpoint="outpoint";
static QString str_file="file";
QDomDocument sceneList;
QDomElement sceneClip = sceneList.createElement("input");
sceneClip.setAttribute(str_file, fileURL().path());
sceneClip.setAttribute(str_inpoint, QString::number((startTime - trackStart() + cropStartTime()).seconds()));
sceneClip.setAttribute(str_outpoint, QString::number((endTime - trackStart() + cropStartTime()).seconds()));
sceneList.appendChild(sceneClip);
return sceneList;
}
QValueVector<GenTime> DocClipAVFile::sceneTimes()
{
QValueVector<GenTime> list;
list.append(trackStart());
list.append(trackEnd());
return list;
}
......@@ -34,6 +34,7 @@ class KdenliveDoc;
*/
class DocClipAVFile : public DocClipBase {
Q_OBJECT
public:
DocClipAVFile(KdenliveDoc *doc, const QString &name, const KURL &url);
DocClipAVFile(KdenliveDoc *doc, AVFile *avFile);
......@@ -46,12 +47,19 @@ public:
DocClipBase::CLIPTYPE clipType();
QDomDocument toXML();
/** Returns the url of the AVFile this clip contains */
KURL fileURL();
/** Creates a clip from the passed QDomElement. This only pertains to those details specific to DocClipAVFile.*/
static DocClipAVFile * createClip(KdenliveDoc *doc, const QDomElement element);
/** Returns true if the clip duration is known, false otherwise. */
bool durationKnown();
/** Returns the url of the AVFile this clip contains */
KURL fileURL();
/** Creates a clip from the passed QDomElement. This only pertains to those details specific to DocClipAVFile.*/
static DocClipAVFile * createClip(KdenliveDoc *doc, const QDomElement element);
/** Returns true if the clip duration is known, false otherwise. */
virtual bool durationKnown();
virtual int framesPerSecond() const;
/** Returns a scene list generated from this clip. */
virtual QDomDocument generateSceneList();
// Returns a list of times that this clip must break upon.
virtual QValueVector<GenTime> sceneTimes();
// Returns an XML document that describes part of the current scene.
virtual QDomDocument sceneToXML(const GenTime &startTime, const GenTime &endTime);
private:
/** A play object factory, used for calculating information, and previewing files */
/** Determines whether this file contains audio, video or both. */
......
......@@ -26,11 +26,11 @@
DocClipBase::DocClipBase(KdenliveDoc *doc) :
m_trackStart(0.0),
m_cropStart(0.0),
m_trackEnd(0.0)
m_trackEnd(0.0),
m_parentTrack(0),
m_trackNum(-1),
m_document(doc)
{
m_doc = doc;
m_parentTrack=0;
m_trackNum = -1;
}
DocClipBase::~DocClipBase()
......@@ -44,9 +44,9 @@ const GenTime &DocClipBase::trackStart() const {
void DocClipBase::setTrackStart(const GenTime time)
{
m_trackStart = time;
if(m_doc->snapToFrame()) {
m_trackStart.roundNearestFrame(m_doc->framesPerSecond());
}
if(m_document->snapToFrame()) {
m_trackStart.roundNearestFrame(m_document->framesPerSecond());
}
if(m_parentTrack) {
m_parentTrack->clipMoved(this);
......@@ -83,9 +83,9 @@ void DocClipBase::setTrackEnd(const GenTime &time)
{
m_trackEnd = time;
if(m_doc->snapToFrame()) {
m_trackEnd.roundNearestFrame(m_doc->framesPerSecond());
}
if(m_document->snapToFrame()) {
m_trackEnd.roundNearestFrame(m_document->framesPerSecond());
}
if(m_parentTrack) {
m_parentTrack->clipMoved(this);
......@@ -100,7 +100,7 @@ GenTime DocClipBase::cropDuration()
QDomDocument DocClipBase::toXML() {
QDomDocument doc;
QDomElement clip = doc.createElement("clip");
QDomElement clip = doc.createElement("clip");
clip.setAttribute("name", name());
QDomElement position = doc.createElement("position");
......@@ -137,6 +137,8 @@ DocClipBase *DocClipBase::createClip(KdenliveDoc *doc, const QDomElement element
if(!e.isNull()) {
if(e.tagName() == "avfile") {
clip = DocClipAVFile::createClip(doc, e);
} else if(e.tagName() == "project") {
clip = DocClipProject::createClip(doc, e);
} else if(e.tagName() == "position") {
trackNum = e.attribute("track", "-1").toInt();
trackStart = GenTime(e.attribute("trackstart", "0").toDouble());
......@@ -181,7 +183,7 @@ int DocClipBase::trackNum()
/** Returns the end of the clip on the track. A convenience function, equivalent
to trackStart() + cropDuration() */
GenTime DocClipBase::trackEnd()
GenTime DocClipBase::trackEnd() const
{
return m_trackEnd;
}
......@@ -198,13 +200,13 @@ void DocClipBase::moveTrackStart(const GenTime &time)
m_trackEnd = m_trackEnd + time - m_trackStart;
m_trackStart = time;
if(m_doc->snapToFrame()) {
m_trackEnd.roundNearestFrame(m_doc->framesPerSecond());
m_trackStart.roundNearestFrame(m_doc->framesPerSecond());
if(m_document->snapToFrame()) {
m_trackEnd.roundNearestFrame(m_document->framesPerSecond());
m_trackStart.roundNearestFrame(m_document->framesPerSecond());
}
}
DocClipBase *DocClipBase::clone()
{
return createClip(m_doc, toXML().documentElement());
return createClip(m_document, toXML().documentElement());
}
......@@ -24,13 +24,16 @@
#include <qdom.h>
#include <kurl.h>
#include <qobject.h>
#include <qvaluevector.h>
#include "gentime.h"
class KdenliveDoc;
class DocTrackBase;
class DocClipBase {
class DocClipBase : public QObject {
Q_OBJECT
public:
/** this enum determines the types of "feed" available within this clip. types must be non-exlcusive
* - e.g. if you can have audio and video seperately, it should be possible to combin the two, as is
......@@ -80,21 +83,34 @@ public:
/** Reads in the element structure and creates a clip out of it.*/
static DocClipBase *createClip(KdenliveDoc *doc, const QDomElement element);
/** Sets the parent track for this clip. */
void setParentTrack(DocTrackBase *track, const int trackNum);
/** Returns the track number. This is a hint as to which track the clip is on, or should be placed on. */
int trackNum();
/** Returns the end of the clip on the track. A convenience function, equivalent
to trackStart() + cropDuration() */
GenTime trackEnd();
/** Returns the parentTrack of this clip. */
DocTrackBase * parentTrack();
/** Move the clips so that it's trackStart coincides with the time specified. */
void moveTrackStart(const GenTime &time);
/** Returns an identical but seperate (i.e. "deep") copy of this clip. */
DocClipBase * clone();
/** Returns true if the clip duration is known, false otherwise. */
virtual bool durationKnown() = 0;
/** Sets the parent track for this clip. */
void setParentTrack(DocTrackBase *track, const int trackNum);
/** Returns the track number. This is a hint as to which track the clip is on, or
* should be placed on. */
int trackNum();
/** Returns the end of the clip on the track. A convenience function, equivalent
to trackStart() + cropDuration() */
GenTime trackEnd() const;
/** Returns the parentTrack of this clip. */
DocTrackBase * parentTrack();
/** Move the clips so that it's trackStart coincides with the time specified. */
void moveTrackStart(const GenTime &time);
/** Returns an identical but seperate (i.e. "deep") copy of this clip. */
DocClipBase * clone();
/** Returns true if the clip duration is known, false otherwise. */
virtual bool durationKnown() = 0;
// Returns the number of frames per second that this clip should play at.
virtual int framesPerSecond() const = 0;
/** Returns a scene list generated from this clip. */
virtual QDomDocument generateSceneList() = 0;
/** Returns true if this clip is a project clip, false otherwise. Overridden in DocClipProject,
* where it returns true. */
virtual bool isProjectClip() { return false; }
// Returns a list of times that this clip must break upon.
virtual QValueVector<GenTime> sceneTimes() = 0;
// Returns an XML document that describes part of the current scene.
virtual QDomDocument sceneToXML(const GenTime &startTime, const GenTime &endTime) = 0;
private: // Private attributes
/** The name of this clip */
QString m_name;
......@@ -114,10 +130,10 @@ parented to any track. */
It is possible for this to be set and the parent track to be 0, in this situation
m_trackNum is a hint as to where the clip should be place when it get's parented
to a track. */
int m_trackNum;;
int m_trackNum;
protected: // Protected attributes
/** the document this clip belongs to */
KdenliveDoc * m_doc;
KdenliveDoc * m_document;
};
#endif
......@@ -17,9 +17,19 @@
#include "docclipproject.h"
#include <qptrvector.h>
#include <qvaluevector.h>
#include <kdebug.h>
#include "doctrackbase.h"
#include "doctrackclipiterator.h"
DocClipProject::DocClipProject(KdenliveDoc *doc) :
DocClipBase(doc)
{
m_framesPerSecond = 25; // Standard PAL.
m_tracks.setAutoDelete(true);
}
DocClipProject::~DocClipProject()
......@@ -28,7 +38,18 @@ DocClipProject::~DocClipProject()
GenTime DocClipProject::duration() const
{
return GenTime(0.0);
GenTime length(0);
QPtrListIterator<DocTrackBase> itt(m_tracks);
while(itt.current()) {
GenTime test = itt.current()->trackLength();
length = (test > length) ? test : length;
++itt;
}
return length;
}
/** No descriptions */
......@@ -42,11 +63,333 @@ KURL DocClipProject::fileURL()
QDomDocument DocClipProject::toXML()
{
QDomDocument doc = DocClipBase::toXML();
QDomNode node = doc.firstChild();
while( !node.isNull()) {
QDomElement element = node.toElement();
if(!element.isNull()) {
if(element.tagName() == "clip") {
QDomElement project = doc.createElement("project");
project.setAttribute("fps", QString::number(m_framesPerSecond));
element.appendChild(project);
project.appendChild(doc.importNode(m_tracks.toXML().documentElement(), true));
return doc;
}
}
node = node.nextSibling();
}
ASSERT(node.isNull());
return doc;
}
/** Returns true if the clip duration is known, false otherwise. */
bool DocClipProject::durationKnown()
{
return false;
return true;
}
//virtual
int DocClipProject::framesPerSecond() const
{
return m_framesPerSecond;
}
/** Adds a track to the project */
void DocClipProject::addTrack(DocTrackBase *track){
m_tracks.append(track);
track->trackIndexChanged(trackIndex(track));
connectTrack(track);
emit trackListChanged();
}
/** Returns the index value for this track, or -1 on failure.*/
int DocClipProject::trackIndex(DocTrackBase *track)
{
return m_tracks.find(track);
}
void DocClipProject::connectTrack(DocTrackBase *track)
{
connect(track, SIGNAL(clipLayoutChanged()), this, SIGNAL(clipLayoutChanged()));
connect(track, SIGNAL(signalClipSelected(DocClipBase *)), this, SIGNAL(signalClipSelected(DocClipBase *)));
}
uint DocClipProject::numTracks() const
{
return m_tracks.count();
}
DocTrackBase * DocClipProject::firstTrack()
{
return m_tracks.first();
}
DocTrackBase * DocClipProject::nextTrack()
{
return m_tracks.next();
}
DocTrackBase * DocClipProject::findTrack(DocClipBase *clip)
{
DocTrackBase *returnTrack = 0;
QPtrListIterator<DocTrackBase> itt(m_tracks);
for(DocTrackBase *track;(track = itt.current()); ++itt) {
if(track->clipExists(clip)) {
returnTrack = track;
break;
}
}
return returnTrack;
}
DocTrackBase * DocClipProject::track(int track)
{
return m_tracks.at(track);
}
bool DocClipProject::moveSelectedClips(GenTime startOffset, int trackOffset)
{
// For each track, check and make sure that the clips can be moved to their rightful place. If
// one cannot be moved to a particular location, then none of them can be moved.
// We check for the closest position the track could possibly be moved to, and move it there instead.
int destTrackNum;
DocTrackBase *srcTrack, *destTrack;
GenTime clipStartTime;
GenTime clipEndTime;
DocClipBase *srcClip, *destClip;
blockTrackSignals(true);
for(uint track=0; track<numTracks(); track++) {
srcTrack = m_tracks.at(track);
if(!srcTrack->hasSelectedClips()) continue;
destTrackNum = track + trackOffset;
if((destTrackNum < 0) || (destTrackNum >= numTracks())) return false; // This track will be moving it's clips out of the timeline, so fail automatically.
destTrack = m_tracks.at(destTrackNum);
QPtrListIterator<DocClipBase> srcClipItt = srcTrack->firstClip(true);
QPtrListIterator<DocClipBase> destClipItt = destTrack->firstClip(false);
destClip = destClipItt.current();
while( (srcClip = srcClipItt.current()) != 0) {
clipStartTime = srcClipItt.current()->trackStart() + startOffset;
clipEndTime = clipStartTime + srcClipItt.current()->cropDuration();
while((destClip) && (destClip->trackStart() + destClip->cropDuration() <= clipStartTime)) {
++destClipItt;
destClip = destClipItt.current();
}
if(destClip==0) break;
if(destClip->trackStart() < clipEndTime) {
blockTrackSignals(false);
return false;
}
++srcClipItt;
}
}
// we can now move all clips where they need to be.
// If the offset is negative, handle tracks from forwards, else handle tracks backwards. We
// do this so that there are no collisions between selected clips, which would be caught by DocTrackBase
// itself.
int startAtTrack, endAtTrack, direction;
if(trackOffset < 0) {
startAtTrack = 0;
endAtTrack = numTracks();
direction = 1;
} else {
startAtTrack = numTracks() - 1;
endAtTrack = -1;
direction = -1;
}
for(int track=startAtTrack; track!=endAtTrack; track += direction) {
srcTrack = m_tracks.at(track);
if(!srcTrack->hasSelectedClips()) continue;
srcTrack->moveClips(startOffset, true);
if(trackOffset) {
destTrackNum = track + trackOffset;
destTrack = m_tracks.at(destTrackNum);
destTrack->addClips(srcTrack->removeClips(true), true);
}
}
blockTrackSignals(false);
return true;
}
void DocClipProject::blockTrackSignals(bool block)
{
QPtrListIterator<DocTrackBase> itt(m_tracks);
while(itt.current() != 0)
{
itt.current()->blockSignals(block);
++itt;
}
}
QDomDocument DocClipProject::generateSceneList()
{
static QString str_inpoint="inpoint";
static QString str_outpoint="outpoint";
static QString str_file="file";
QDomDocument sceneList;
sceneList.appendChild(sceneList.createElement("scenelist"));
QValueVector<GenTime> unsortedTimes = sceneTimes();
// sort times and remove duplicates.
qHeapSort(unsortedTimes);
QValueVector<GenTime> times;
QValueVector<GenTime>::Iterator itt = unsortedTimes.begin();
while(itt != unsortedTimes.end()) {
if((times.isEmpty()) || ((*itt) != times.last())) {
times.append(*itt);
}
++itt;
}
QValueVector<GenTime>::Iterator sceneItt = times.begin();
while( (sceneItt != times.end()) && (sceneItt+1 != times.end()) ) {
QDomElement scene = sceneList.createElement("scene");
scene.setAttribute("duration", QString::number((*(sceneItt+1) - *sceneItt).seconds()));
QDomDocument clipDoc = sceneToXML(*sceneItt, *(sceneItt+1));
QDomElement sceneClip;