diff --git a/app/timeline/timelinemarker.cpp b/app/timeline/timelinemarker.cpp
index 047afc779..1a0403568 100644
--- a/app/timeline/timelinemarker.cpp
+++ b/app/timeline/timelinemarker.cpp
@@ -78,19 +78,24 @@ void TimelineMarker::set_active(bool active)
emit ActiveChanged(active_);
}
+void TimelineMarker::Save(QXmlStreamWriter* writer) const
+{
+ writer->writeStartElement(QStringLiteral("marker"));
+
+ writer->writeAttribute(QStringLiteral("name"), name_);
+
+ writer->writeAttribute(QStringLiteral("color"), QString::number(color_));
+
+ writer->writeAttribute(QStringLiteral("in"), time_.in().toString());
+ writer->writeAttribute(QStringLiteral("out"), time_.out().toString());
+
+ writer->writeEndElement(); // marker
+}
+
void TimelineMarkerList::Save(QXmlStreamWriter *writer) const
{
foreach (TimelineMarker* marker, markers_) {
- writer->writeStartElement(QStringLiteral("marker"));
-
- writer->writeAttribute(QStringLiteral("name"), marker->name());
-
- writer->writeAttribute(QStringLiteral("color"), QString::number(marker->color()));
-
- writer->writeAttribute(QStringLiteral("in"), marker->time().in().toString());
- writer->writeAttribute(QStringLiteral("out"), marker->time().out().toString());
-
- writer->writeEndElement(); // marker
+ marker->Save(writer);
}
}
diff --git a/app/timeline/timelinemarker.h b/app/timeline/timelinemarker.h
index 1c921a311..f63a1f3d3 100644
--- a/app/timeline/timelinemarker.h
+++ b/app/timeline/timelinemarker.h
@@ -47,6 +47,9 @@ public:
bool active();
void set_active(bool active);
+ void Load(QXmlStreamReader* reader);
+ void Save(QXmlStreamWriter* writer) const;
+
signals:
void TimeChanged(const TimeRange& time);
diff --git a/app/widget/marker/CMakeLists.txt b/app/widget/marker/CMakeLists.txt
index 0f49bae01..01ce2157d 100644
--- a/app/widget/marker/CMakeLists.txt
+++ b/app/widget/marker/CMakeLists.txt
@@ -18,5 +18,7 @@ set(OLIVE_SOURCES
${OLIVE_SOURCES}
widget/marker/marker.h
widget/marker/marker.cpp
+ widget/marker/markercopypaste.h
+ widget/marker/markercopypaste.cpp
PARENT_SCOPE
)
diff --git a/app/widget/marker/markercopypaste.cpp b/app/widget/marker/markercopypaste.cpp
new file mode 100644
index 000000000..c5b48edb0
--- /dev/null
+++ b/app/widget/marker/markercopypaste.cpp
@@ -0,0 +1,124 @@
+/***
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2021 Olive Team
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+***/
+
+#include "markercopypaste.h"
+
+#include "core.h"
+#include "widget/timebased/timebasedwidget.h"
+
+namespace olive {
+
+void MarkerCopyPasteService::CopyMarkersToClipboard(const QVector& markers, void* userdata)
+{
+ QString copy_str;
+
+ QXmlStreamWriter writer(©_str);
+ writer.setAutoFormatting(true);
+
+ writer.writeStartDocument();
+ writer.writeStartElement(QStringLiteral("olive"));
+
+ writer.writeTextElement(QStringLiteral("version"), QString::number(Core::kProjectVersion));
+
+ writer.writeStartElement(QStringLiteral("markers"));
+
+ // Get earliest marker for offset
+ rational min_start = RATIONAL_MAX;
+ foreach (TimelineMarker* marker, markers) {
+ if (marker->time().in() < min_start) {
+ min_start = marker->time().in();
+ }
+ }
+
+ foreach(TimelineMarker * marker, markers) {
+ // Cache marker in/out points
+ rational in = marker->time().in();
+ rational out = marker->time().out();
+
+ // Temporarily set in/out to be relative to the earliest selected marker
+ marker->set_time(TimeRange(in - min_start, marker->time().out() - min_start));
+ marker->Save(&writer);
+
+ // Reset in/out
+ marker->set_time(TimeRange(in, out));
+ }
+
+
+ writer.writeEndElement(); // markers
+ writer.writeEndElement(); // olive
+ writer.writeEndDocument();
+
+ Core::CopyStringToClipboard(copy_str);
+}
+
+void MarkerCopyPasteService::PasteMarkersFromClipboard(TimelineMarkerList* list, MultiUndoCommand* command,
+ rational offset) {
+ QString clipboard = Core::PasteStringFromClipboard();
+
+ if (clipboard.isEmpty()) {
+ return;
+ }
+
+ QXmlStreamReader reader(clipboard);
+ uint data_version = 0;
+
+ QVector pasted_markers;
+
+ while (XMLReadNextStartElement(&reader)) {
+ if (reader.name() == QStringLiteral("olive")) {
+ while (XMLReadNextStartElement(&reader)) {
+ if (reader.name() == QStringLiteral("version")) {
+ data_version = reader.readElementText().toUInt();
+ } else if (reader.name() == QStringLiteral("markers")) {
+ while (XMLReadNextStartElement(&reader)) {
+ if (data_version == 0) {
+ return;
+ }
+ QString name;
+ int color;
+ rational in, out;
+
+ XMLAttributeLoop((&reader), attr) {
+ if (attr.name() == QStringLiteral("name")) {
+ name = attr.value().toString();
+ }
+
+ if (attr.name() == QStringLiteral("color")) {
+ color = attr.value().toInt();
+ }
+
+ if (attr.name() == QStringLiteral("in")) {
+ in = rational::fromString(attr.value().toString());
+ }
+
+ if (attr.name() == QStringLiteral("out")) {
+ out = rational::fromString(attr.value().toString());
+ }
+ }
+
+ command->add_child(new TimeBasedWidget::MarkerAddCommand(Core::instance()->GetActiveProject(),
+ list,
+ TimeRange(in + offset, out + offset),
+ name,
+ color));
+ }
+ }
+ }
+ }
+ }
+
+ Core::instance()->undo_stack()->pushIfHasChildren(command);
+}
+} //namespace olive
diff --git a/app/widget/marker/markercopypaste.h b/app/widget/marker/markercopypaste.h
new file mode 100644
index 000000000..7566ad805
--- /dev/null
+++ b/app/widget/marker/markercopypaste.h
@@ -0,0 +1,40 @@
+/***
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2021 Olive Team
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+***/
+
+#ifndef MARKERCOPYPASTEWIDGET_H
+#define MARKERCOPYPASTEWIDGET_H
+
+#include
+
+#include "widget/marker/marker.h"
+#include "timeline/timelinemarker.h"
+#include "undo/undocommand.h"
+
+namespace olive {
+
+class MarkerCopyPasteService
+{
+public:
+ MarkerCopyPasteService() = default;
+
+protected:
+ void CopyMarkersToClipboard(const QVector &markers, void* userdata = nullptr);
+
+ void PasteMarkersFromClipboard(TimelineMarkerList* list, MultiUndoCommand *command, rational offset);
+};
+
+}
+
+#endif // MARKERCOPYPASTEWIDGET_H
diff --git a/app/widget/timebased/timebasedwidget.cpp b/app/widget/timebased/timebasedwidget.cpp
index fcc53161f..28e8a7eab 100644
--- a/app/widget/timebased/timebasedwidget.cpp
+++ b/app/widget/timebased/timebasedwidget.cpp
@@ -665,11 +665,12 @@ void TimeBasedWidget::DeleteSelected()
}
}
-TimeBasedWidget::MarkerAddCommand::MarkerAddCommand(Project *project, TimelineMarkerList *marker_list, const TimeRange &range, const QString &name) :
+TimeBasedWidget::MarkerAddCommand::MarkerAddCommand(Project *project, TimelineMarkerList *marker_list, const TimeRange &range, const QString &name, int color) :
project_(project),
marker_list_(marker_list),
range_(range),
- name_(name)
+ name_(name),
+ color_(color)
{
}
@@ -680,7 +681,7 @@ Project *TimeBasedWidget::MarkerAddCommand::GetRelevantProject() const
void TimeBasedWidget::MarkerAddCommand::redo()
{
- added_marker_ = marker_list_->AddMarker(range_, name_);
+ added_marker_ = marker_list_->AddMarker(range_, name_, color_);
}
void TimeBasedWidget::MarkerAddCommand::undo()
diff --git a/app/widget/timebased/timebasedwidget.h b/app/widget/timebased/timebasedwidget.h
index ae6db3b56..1e6fb87fe 100644
--- a/app/widget/timebased/timebasedwidget.h
+++ b/app/widget/timebased/timebasedwidget.h
@@ -55,6 +55,27 @@ public:
virtual bool eventFilter(QObject* object, QEvent* event) override;
// Temp file location
+
+ class MarkerAddCommand : public UndoCommand {
+ public:
+ MarkerAddCommand(Project* project, TimelineMarkerList* marker_list, const TimeRange& range, const QString& name, int color = -1);
+
+ virtual Project* GetRelevantProject() const override;
+
+ protected:
+ virtual void redo() override;
+ virtual void undo() override;
+
+ private:
+ Project* project_;
+ TimelineMarkerList* marker_list_;
+ TimeRange range_;
+ QString name_;
+ int color_;
+
+ TimelineMarker* added_marker_;
+ };
+
class MarkerRemoveCommand : public UndoCommand {
public:
MarkerRemoveCommand(Project* project, TimelineMarker* marker, TimelineMarkerList* marker_list);
@@ -163,26 +184,7 @@ signals:
void ConnectedNodeChanged(ViewerOutput* old, ViewerOutput* now);
private:
- class MarkerAddCommand : public UndoCommand
- {
- public:
- MarkerAddCommand(Project* project, TimelineMarkerList* marker_list, const TimeRange& range, const QString& name);
-
- virtual Project* GetRelevantProject() const override;
-
- protected:
- virtual void redo() override;
- virtual void undo() override;
-
- private:
- Project* project_;
- TimelineMarkerList* marker_list_;
- TimeRange range_;
- QString name_;
-
- TimelineMarker* added_marker_;
-
- };
+
/**
* @brief Set either in or out point to the current playhead
diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp
index be6c68d1c..637203991 100644
--- a/app/widget/timelinewidget/timelinewidget.cpp
+++ b/app/widget/timelinewidget/timelinewidget.cpp
@@ -378,6 +378,15 @@ void TimelineWidget::DeselectAll()
SignalDeselectedAllBlocks();
}
+bool TimelineWidget::MarkersActive()
+{
+ if (ruler()->GetActiveTimelineMarkers().size() > 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
void TimelineWidget::RippleToIn()
{
RippleTo(Timeline::kTrimIn);
@@ -475,7 +484,7 @@ void TimelineWidget::ReplaceBlocksWithGaps(const QVector &blocks,
void TimelineWidget::DeleteSelected(bool ripple)
{
- if (ruler()->GetActiveTimelineMarkers().size() > 0) {
+ if (MarkersActive()) {
ruler()->DeleteSelected();
return;
}
@@ -598,6 +607,12 @@ void TimelineWidget::CopySelected(bool cut)
return;
}
+ if (MarkersActive()) {
+ ruler()->CopySelected(cut);
+ return;
+ }
+
+
QVector selected = GetSelectedBlocks();
if (selected.isEmpty()) {
@@ -631,6 +646,11 @@ void TimelineWidget::Paste(bool insert)
return;
}
+ if (ruler()->underMouse()) {
+ ruler()->PasteMarkers(insert, GetTime());
+ return;
+ }
+
MultiUndoCommand* command = new MultiUndoCommand();
QVector paste_data;
diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h
index 6082e2e51..080aa9fd5 100644
--- a/app/widget/timelinewidget/timelinewidget.h
+++ b/app/widget/timelinewidget/timelinewidget.h
@@ -59,6 +59,8 @@ public:
void DeselectAll();
+ bool MarkersActive();
+
void RippleToIn();
void RippleToOut();
diff --git a/app/widget/timeruler/seekablewidget.cpp b/app/widget/timeruler/seekablewidget.cpp
index 0764ee80b..90fb36c79 100644
--- a/app/widget/timeruler/seekablewidget.cpp
+++ b/app/widget/timeruler/seekablewidget.cpp
@@ -106,6 +106,21 @@ const int &SeekableWidget::GetScroll() const
return scroll_;
}
+void SeekableWidget::CopySelected(bool cut)
+{
+ CopyMarkersToClipboard(GetActiveTimelineMarkers());
+
+ if (cut) {
+ DeleteSelected();
+ }
+}
+
+void SeekableWidget::PasteMarkers(bool insert, rational insert_time)
+{
+ MultiUndoCommand *command = new MultiUndoCommand();
+ PasteMarkersFromClipboard(timeline_points()->markers(), command, insert_time);
+}
+
void SeekableWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
diff --git a/app/widget/timeruler/seekablewidget.h b/app/widget/timeruler/seekablewidget.h
index 05d9a81bb..7438245a3 100644
--- a/app/widget/timeruler/seekablewidget.h
+++ b/app/widget/timeruler/seekablewidget.h
@@ -28,10 +28,11 @@
#include "widget/snapservice/snapservice.h"
#include "widget/timebased/timescaledobject.h"
#include "widget/marker/marker.h"
+#include "widget/marker/markercopypaste.h"
namespace olive {
-class SeekableWidget : public TimelineScaledWidget
+class SeekableWidget : public TimelineScaledWidget, public MarkerCopyPasteService
{
Q_OBJECT
public:
@@ -55,6 +56,10 @@ public:
void DeleteSelected();
+ void CopySelected(bool cut);
+
+ void PasteMarkers(bool insert, rational insert_time);
+
QVector GetActiveTimelineMarkers();
void DeselectAllMarkers();