+
+bool CustomTrackView::canBePastedTo(ItemInfo info, int type) const {
+ QRectF rect((double) info.startPos.frames(m_document->fps()), (double)(info.track * m_tracksHeight + 1), (double)(info.endPos - info.startPos).frames(m_document->fps()), (double)(m_tracksHeight - 1));
+ QList<QGraphicsItem *> collisions = scene()->items(rect, Qt::IntersectsItemBoundingRect);
+ for (int i = 0; i < collisions.count(); i++) {
+ if (collisions.at(i)->type() == type) return false;
+ }
+ return true;
+}
+
+bool CustomTrackView::canBePasted(QList<AbstractClipItem *> items, GenTime offset, int trackOffset) const {
+ for (int i = 0; i < items.count(); i++) {
+ ItemInfo info = items.at(i)->info();
+ info.startPos += offset;
+ info.endPos += offset;
+ info.track += trackOffset;
+ if (!canBePastedTo(info, items.at(i)->type())) return false;
+ }
+ return true;
+}
+
+bool CustomTrackView::canBeMoved(QList<AbstractClipItem *> items, GenTime offset, int trackOffset) const {
+ QPainterPath movePath;
+ movePath.moveTo(0, 0);
+
+ for (int i = 0; i < items.count(); i++) {
+ ItemInfo info = items.at(i)->info();
+ info.startPos = info.startPos + offset;
+ info.endPos = info.endPos + offset;
+ info.track = info.track + trackOffset;
+ if (info.startPos < GenTime()) {
+ // No clip should go below 0
+ return false;
+ }
+ QRectF rect((double) info.startPos.frames(m_document->fps()), (double)(info.track * m_tracksHeight + 1), (double)(info.endPos - info.startPos).frames(m_document->fps()), (double)(m_tracksHeight - 1));
+ movePath.addRect(rect);
+ }
+ QList<QGraphicsItem *> collisions = scene()->items(movePath, Qt::IntersectsItemBoundingRect);
+ for (int i = 0; i < collisions.count(); i++) {
+ if ((collisions.at(i)->type() == AVWIDGET || collisions.at(i)->type() == TRANSITIONWIDGET) && !items.contains(static_cast <AbstractClipItem *>(collisions.at(i)))) {
+ kDebug() << " //////////// CLIP COLLISION, MOVE NOT ALLOWED";
+ return false;
+ }
+ }
+ return true;
+}
+
+void CustomTrackView::pasteClip() {
+ if (m_copiedItems.count() == 0) {
+ emit displayMessage(i18n("No clip copied"), ErrorMessage);
+ return;
+ }
+ QPoint position;
+ if (m_menuPosition.isNull()) {
+ position = mapFromGlobal(QCursor::pos());
+ if (!underMouse() || position.y() > m_tracksHeight * m_document->tracksCount()) {
+ emit displayMessage(i18n("Cannot paste selected clips"), ErrorMessage);
+ return;
+ }
+ } else position = m_menuPosition;
+ GenTime pos = GenTime((int)(mapToScene(position).x()), m_document->fps());
+ int track = (int)(position.y() / m_tracksHeight);
+ ItemInfo first = m_copiedItems.at(0)->info();
+
+ GenTime offset = pos - first.startPos;
+ int trackOffset = track - first.track;
+
+ if (!canBePasted(m_copiedItems, offset, trackOffset)) {
+ emit displayMessage(i18n("Cannot paste selected clips"), ErrorMessage);
+ return;
+ }
+ QUndoCommand *pasteClips = new QUndoCommand();
+ pasteClips->setText("Paste clips");
+
+ for (int i = 0; i < m_copiedItems.count(); i++) {
+ // parse all clip names
+ if (m_copiedItems.at(i) && m_copiedItems.at(i)->type() == AVWIDGET) {
+ ClipItem *clip = static_cast <ClipItem *>(m_copiedItems.at(i));
+ ItemInfo info;
+ info.startPos = clip->startPos() + offset;
+ info.endPos = clip->endPos() + offset;
+ info.cropStart = clip->cropStart();
+ info.track = clip->track() + trackOffset;
+ if (canBePastedTo(info, AVWIDGET)) {
+ new AddTimelineClipCommand(this, clip->xml(), clip->clipProducer(), info, clip->effectList(), true, false, pasteClips);
+ } else emit displayMessage(i18n("Cannot paste clip to selected place"), ErrorMessage);
+ } else if (m_copiedItems.at(i) && m_copiedItems.at(i)->type() == TRANSITIONWIDGET) {
+ Transition *tr = static_cast <Transition *>(m_copiedItems.at(i));
+ ItemInfo info;
+ info.startPos = tr->startPos() + offset;
+ info.endPos = tr->endPos() + offset;
+ info.track = tr->track() + trackOffset;
+ if (canBePastedTo(info, TRANSITIONWIDGET)) {
+ if (info.startPos >= info.endPos) {
+ emit displayMessage(i18n("Invalid transition"), ErrorMessage);
+ } else new AddTransitionCommand(this, info, tr->transitionEndTrack() + trackOffset, tr->toXML(), false, true, pasteClips);
+ } else emit displayMessage(i18n("Cannot paste transition to selected place"), ErrorMessage);
+ }
+ }
+ m_commandStack->push(pasteClips);
+}
+
+void CustomTrackView::pasteClipEffects() {
+ if (m_copiedItems.count() != 1 || m_copiedItems.at(0)->type() != AVWIDGET) {
+ emit displayMessage(i18n("You must copy exactly one clip before pasting effects"), ErrorMessage);
+ return;
+ }
+ ClipItem *clip = static_cast < ClipItem *>(m_copiedItems.at(0));
+ EffectsList effects = clip->effectList();
+
+ QUndoCommand *paste = new QUndoCommand();
+ paste->setText("Paste effects");
+
+ QList<QGraphicsItem *> clips = scene()->selectedItems();
+ for (int i = 0; i < clips.count(); ++i) {
+ if (clips.at(i)->type() == AVWIDGET) {
+ ClipItem *item = static_cast < ClipItem *>(clips.at(i));
+ for (int i = 0; i < clip->effectsCount(); i++) {
+ new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), clip->effectAt(i), true, paste);
+ }
+ }
+ }
+ m_commandStack->push(paste);
+}
+
+
+ClipItem *CustomTrackView::getClipUnderCursor() const {
+ QRectF rect((double) m_cursorPos, 0.0, 1.0, (double)(m_tracksHeight * m_document->tracksCount()));
+ QList<QGraphicsItem *> collisions = scene()->items(rect, Qt::IntersectsItemBoundingRect);
+ for (int i = 0; i < collisions.count(); i++) {
+ if (collisions.at(i)->type() == AVWIDGET) {
+ return static_cast < ClipItem *>(collisions.at(i));
+ }
+ }
+ return NULL;
+}
+
+ClipItem *CustomTrackView::getMainActiveClip() const {
+ QList<QGraphicsItem *> clips = scene()->selectedItems();
+ if (clips.isEmpty()) {
+ return getClipUnderCursor();
+ } else {
+ ClipItem *item = NULL;
+ for (int i = 0; i < clips.count(); ++i) {
+ if (clips.at(i)->type() == AVWIDGET)
+ item = static_cast < ClipItem *>(clips.at(i));
+ if (item->startPos().frames(m_document->fps()) <= m_cursorPos && item->endPos().frames(m_document->fps()) >= m_cursorPos) break;
+ }
+ if (item) return item;
+ }
+ return NULL;
+}
+
+ClipItem *CustomTrackView::getActiveClipUnderCursor(bool allowOutsideCursor) const {
+ QList<QGraphicsItem *> clips = scene()->selectedItems();
+ if (clips.isEmpty()) {
+ return getClipUnderCursor();
+ } else {
+ ClipItem *item;
+ // remove all items in the list that are not clips
+ for (int i = 0; i < clips.count();) {
+ if (clips.at(i)->type() != AVWIDGET) clips.removeAt(i);
+ else i++;
+ }
+ if (clips.count() == 1 && allowOutsideCursor) return static_cast < ClipItem *>(clips.at(0));
+ for (int i = 0; i < clips.count(); ++i) {
+ if (clips.at(i)->type() == AVWIDGET)
+ item = static_cast < ClipItem *>(clips.at(i));
+ if (item->startPos().frames(m_document->fps()) <= m_cursorPos && item->endPos().frames(m_document->fps()) >= m_cursorPos) return item;
+ }
+ }
+ return NULL;
+}
+
+void CustomTrackView::setInPoint() {
+ ClipItem *clip = getActiveClipUnderCursor(true);
+ if (clip == NULL) {
+ emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
+ return;
+ }
+ ItemInfo startInfo = clip->info();
+ ItemInfo endInfo = startInfo;
+ endInfo.startPos = GenTime(m_cursorPos, m_document->fps());
+ if (endInfo.startPos >= startInfo.endPos) {
+ // Check for invalid resize
+ emit displayMessage(i18n("Invalid action"), ErrorMessage);
+ return;
+ } else if (endInfo.startPos < startInfo.startPos) {
+ int length = m_document->renderer()->mltGetSpaceLength(endInfo.startPos, m_document->tracksCount() - startInfo.track, false);
+ if (length < (startInfo.startPos - endInfo.startPos).frames(m_document->fps())) {
+ emit displayMessage(i18n("Invalid action"), ErrorMessage);
+ return;
+ }
+ }
+ ResizeClipCommand *command = new ResizeClipCommand(this, startInfo, endInfo, true);
+ m_commandStack->push(command);
+}
+
+void CustomTrackView::setOutPoint() {
+ ClipItem *clip = getActiveClipUnderCursor(true);
+ if (clip == NULL) {
+ emit displayMessage(i18n("You must select one clip for this action"), ErrorMessage);
+ return;
+ }
+ ItemInfo startInfo = clip->info();
+ ItemInfo endInfo = clip->info();
+ endInfo.endPos = GenTime(m_cursorPos, m_document->fps());
+ if (endInfo.endPos <= startInfo.startPos) {
+ // Check for invalid resize
+ emit displayMessage(i18n("Invalid action"), ErrorMessage);
+ return;
+ } else if (endInfo.endPos > startInfo.endPos) {
+ int length = m_document->renderer()->mltGetSpaceLength(endInfo.endPos, m_document->tracksCount() - startInfo.track, false);
+ if (length < (endInfo.endPos - startInfo.endPos).frames(m_document->fps())) {
+ emit displayMessage(i18n("Invalid action"), ErrorMessage);
+ return;
+ }
+ }
+
+
+
+ ResizeClipCommand *command = new ResizeClipCommand(this, startInfo, endInfo, true);
+ m_commandStack->push(command);
+}
+
+void CustomTrackView::slotUpdateAllThumbs() {
+ QList<QGraphicsItem *> itemList = items();
+ //if (itemList.isEmpty()) return;
+ ClipItem *item;
+ QString thumbBase = m_document->projectFolder().path() + "/thumbs/";
+ for (int i = 0; i < itemList.count(); i++) {
+ if (itemList.at(i)->type() == AVWIDGET) {
+ item = static_cast <ClipItem *>(itemList.at(i));
+ if (item->clipType() != COLOR) {
+ // Check if we have a cached thumbnail
+ if (item->clipType() == IMAGE || item->clipType() == TEXT || item->clipType() == AUDIO) {
+ QString thumb = thumbBase + item->baseClip()->getClipHash() + "_0.png";
+ if (QFile::exists(thumb)) {
+ QPixmap pix(thumb);
+ item->slotSetStartThumb(pix);
+ item->slotSetEndThumb(pix);
+ }
+ } else {
+ QString startThumb = thumbBase + item->baseClip()->getClipHash() + '_';
+ QString endThumb = startThumb;
+ startThumb.append(QString::number(item->cropStart().frames(m_document->fps())) + ".png");
+ endThumb.append(QString::number((item->cropStart() + item->cropDuration()).frames(m_document->fps()) - 1) + ".png");
+ if (QFile::exists(startThumb)) {
+ QPixmap pix(startThumb);
+ item->slotSetStartThumb(pix);
+ }
+ if (QFile::exists(endThumb)) {
+ QPixmap pix(endThumb);
+ item->slotSetEndThumb(pix);
+ }
+ }
+ }
+ item->refreshClip();
+ qApp->processEvents();
+ }
+ }
+ viewport()->update();
+}
+
+void CustomTrackView::saveThumbnails() {
+ QList<QGraphicsItem *> itemList = items();
+ ClipItem *item;
+ QString thumbBase = m_document->projectFolder().path() + "/thumbs/";
+ for (int i = 0; i < itemList.count(); i++) {
+ if (itemList.at(i)->type() == AVWIDGET) {
+ item = static_cast <ClipItem *>(itemList.at(i));
+ if (item->clipType() != COLOR) {
+ // Check if we have a cached thumbnail
+ if (item->clipType() == IMAGE || item->clipType() == TEXT || item->clipType() == AUDIO) {
+ QString thumb = thumbBase + item->baseClip()->getClipHash() + "_0.png";
+ if (!QFile::exists(thumb)) {
+ QPixmap pix(item->startThumb());
+ pix.save(thumb);
+ }
+ } else {
+ QString startThumb = thumbBase + item->baseClip()->getClipHash() + '_';
+ QString endThumb = startThumb;
+ startThumb.append(QString::number(item->cropStart().frames(m_document->fps())) + ".png");
+ endThumb.append(QString::number((item->cropStart() + item->cropDuration()).frames(m_document->fps()) - 1) + ".png");
+ if (!QFile::exists(startThumb)) {
+ QPixmap pix(item->startThumb());
+ pix.save(startThumb);
+ }
+ if (!QFile::exists(endThumb)) {
+ QPixmap pix(item->endThumb());
+ pix.save(endThumb);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void CustomTrackView::slotInsertTrack(int ix) {
+ kDebug() << "// INSERTING TRK: " << ix;
+ QDialog d(parentWidget());
+ Ui::AddTrack_UI view;
+ view.setupUi(&d);
+ view.track_nb->setMaximum(m_document->tracksCount() - 1);
+ view.track_nb->setValue(ix);
+ d.setWindowTitle(i18n("Insert Track"));
+
+ if (d.exec() == QDialog::Accepted) {
+ ix = view.track_nb->value();
+ if (view.before_select->currentIndex() == 1) {
+ ix++;
+ }
+ TrackInfo info;
+ if (view.video_track->isChecked()) {
+ info.type = VIDEOTRACK;
+ info.isMute = false;
+ info.isBlind = false;
+ info.isLocked = false;
+ } else {
+ info.type = AUDIOTRACK;
+ info.isMute = false;
+ info.isBlind = true;
+ info.isLocked = false;
+ }
+ AddTrackCommand *addTrack = new AddTrackCommand(this, ix, info, true, true);
+ m_commandStack->push(addTrack);
+ m_document->setModified(true);
+ }
+}
+
+void CustomTrackView::slotDeleteTrack(int ix) {
+ bool ok;
+ ix = QInputDialog::getInteger(this, i18n("Remove Track"), i18n("Track"), ix, 0, m_document->tracksCount() - 1, 1, &ok);
+ if (ok) {
+ TrackInfo info = m_document->trackInfoAt(m_document->tracksCount() - ix - 1);
+ deleteTimelineTrack(ix, info);
+ m_document->setModified(true);
+ /*AddTrackCommand* command = new AddTrackCommand(this, ix, info, false, true);
+ m_commandStack->push(command);*/
+ }
+}
+
+void CustomTrackView::slotChangeTrack(int ix) {
+ QDialog d(parentWidget());
+ Ui::AddTrack_UI view;
+ view.setupUi(&d);
+ view.label->setText(i18n("Change track"));
+ view.before_select->setHidden(true);
+ view.track_nb->setMaximum(m_document->tracksCount() - 1);
+ view.track_nb->setValue(ix);
+ d.setWindowTitle(i18n("Change Track Type"));
+
+ if (d.exec() == QDialog::Accepted) {
+ TrackInfo info;
+ info.isLocked = false;
+ info.isMute = false;
+ ix = view.track_nb->value();
+
+ if (view.video_track->isChecked()) {
+ info.type = VIDEOTRACK;
+ info.isBlind = false;
+ } else {
+ info.type = AUDIOTRACK;
+ info.isBlind = true;
+ }
+ changeTimelineTrack(ix, info);
+ m_document->setModified(true);
+ }
+}
+
+
+void CustomTrackView::deleteTimelineTrack(int ix, TrackInfo trackinfo) {
+ double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2;
+ QRectF r(0, startY, sceneRect().width(), m_tracksHeight / 2 - 1);
+ QList<QGraphicsItem *> selection = m_scene->items(r);
+ QUndoCommand *deleteTrack = new QUndoCommand();
+ deleteTrack->setText("Delete track");
+
+ // Delete all clips in selected track
+ for (int i = 0; i < selection.count(); i++) {
+ if (selection.at(i)->type() == AVWIDGET) {
+ ClipItem *item = static_cast <ClipItem *>(selection.at(i));
+ new AddTimelineClipCommand(this, item->xml(), item->clipProducer(), item->info(), item->effectList(), false, true, deleteTrack);
+ m_scene->removeItem(item);
+ delete item;
+ item = NULL;
+ } else if (selection.at(i)->type() == TRANSITIONWIDGET) {
+ Transition *item = static_cast <Transition *>(selection.at(i));
+ new AddTransitionCommand(this, item->info(), item->transitionEndTrack(), item->toXML(), true, false, deleteTrack);
+ m_scene->removeItem(item);
+ delete item;
+ item = NULL;
+ }
+ }
+
+ new AddTrackCommand(this, ix, trackinfo, false, true, deleteTrack);
+ m_commandStack->push(deleteTrack);
+}
+
+void CustomTrackView::changeTimelineTrack(int ix, TrackInfo trackinfo) {
+ TrackInfo oldinfo = m_document->trackInfoAt(m_document->tracksCount() - ix - 1);
+ ChangeTrackCommand *changeTrack = new ChangeTrackCommand(this, ix, oldinfo, trackinfo, true);
+ m_commandStack->push(changeTrack);
+}
+
+void CustomTrackView::autoTransition() {
+ QList<QGraphicsItem *> itemList = scene()->selectedItems();
+ if (itemList.count() != 1 || itemList.at(0)->type() != TRANSITIONWIDGET) {
+ emit displayMessage(i18n("You must select one transition for this action"), ErrorMessage);
+ return;
+ }
+ Transition *tr = static_cast <Transition*>(itemList.at(0));
+ tr->setAutomatic(!tr->isAutomatic());
+ QDomElement transition = tr->toXML();
+ m_document->renderer()->mltUpdateTransition(transition.attribute("tag"), transition.attribute("tag"), transition.attribute("transition_btrack").toInt(), m_document->tracksCount() - transition.attribute("transition_atrack").toInt(), tr->startPos(), tr->endPos(), transition);
+}
+
+
+QStringList CustomTrackView::getLadspaParams(QDomElement effect) const {
+ QStringList result;
+ 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") == "constant") {
+ if (e.hasAttribute("factor")) {
+ double factor = e.attribute("factor").toDouble();
+ double value = e.attribute("value").toDouble();
+ value = value / factor;
+ result.append(QString::number(value));
+ } else result.append(e.attribute("value"));
+ }
+ }
+ return result;
+}
+
+void CustomTrackView::clipNameChanged(const QString id, const QString name) {
+ QList<QGraphicsItem *> list = scene()->items();
+ ClipItem *clip = NULL;
+ for (int i = 0; i < list.size(); ++i) {
+ if (list.at(i)->type() == AVWIDGET) {
+ clip = static_cast <ClipItem *>(list.at(i));
+ if (clip->clipProducer() == id) {
+ clip->setClipName(name);
+ }
+ }
+ }
+ viewport()->update();
+}
+
+void CustomTrackView::getClipAvailableSpace(AbstractClipItem *item, GenTime &minimum, GenTime &maximum) {
+ minimum = GenTime();
+ maximum = GenTime();
+ QList<QGraphicsItem *> selection;
+ selection = m_scene->items(0, item->track() * m_tracksHeight + m_tracksHeight / 2, sceneRect().width(), 2);
+ selection.removeAll(item);
+ for (int i = 0; i < selection.count(); i++) {
+ AbstractClipItem *clip = static_cast <AbstractClipItem *>(selection.at(i));
+ if (clip && clip->type() == AVWIDGET) {
+ if (clip->endPos() <= item->startPos() && clip->endPos() > minimum) minimum = clip->endPos();
+ if (clip->startPos() > item->startPos() && (clip->startPos() < maximum || maximum == GenTime())) maximum = clip->startPos();
+ }
+ }
+}
+
+void CustomTrackView::getTransitionAvailableSpace(AbstractClipItem *item, GenTime &minimum, GenTime &maximum) {
+ minimum = GenTime();
+ maximum = GenTime();
+ QList<QGraphicsItem *> selection;
+ selection = m_scene->items(0, (item->track() + 1) * m_tracksHeight, sceneRect().width(), 2);
+ selection.removeAll(item);
+ for (int i = 0; i < selection.count(); i++) {
+ AbstractClipItem *clip = static_cast <AbstractClipItem *>(selection.at(i));
+ if (clip && clip->type() == TRANSITIONWIDGET) {
+ if (clip->endPos() <= item->startPos() && clip->endPos() > minimum) minimum = clip->endPos();
+ if (clip->startPos() > item->startPos() && (clip->startPos() < maximum || maximum == GenTime())) maximum = clip->startPos();
+ }
+ }
+}
+