diff --git a/app/main.cpp b/app/main.cpp
index e049b1415..a05769982 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -45,6 +45,14 @@ int main(int argc, char *argv[]) {
QCoreApplication::setOrganizationDomain("olivevideoeditor.org");
QCoreApplication::setApplicationName("Olive");
+ QString app_version = "0.2.0";
+#ifdef GITHASH
+ app_version.append("-");
+ app_version.append(GITHASH);
+#endif
+
+ QCoreApplication::setApplicationVersion(app_version);
+
#if (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0))
QGuiApplication::setDesktopFileName("org.olivevideoeditor.Olive");
#endif
diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt
index 662a8e73f..82920dc0d 100644
--- a/app/node/CMakeLists.txt
+++ b/app/node/CMakeLists.txt
@@ -15,6 +15,7 @@
# along with this program. If not, see .
add_subdirectory(generator)
+add_subdirectory(input)
add_subdirectory(output)
add_subdirectory(processor)
diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp
index 8c8fe9268..2a785ce9c 100644
--- a/app/node/generator/solid/solid.cpp
+++ b/app/node/generator/solid/solid.cpp
@@ -34,9 +34,9 @@ NodeOutput *SolidGenerator::texture_output()
void SolidGenerator::Process(const rational &time)
{
+ // FIXME: Test code
Q_UNUSED(time)
- // FIXME: Test code
if (texture_ == nullptr) {
QImage img(1920, 1080, QImage::Format_RGBA8888_Premultiplied);
img.fill(Qt::red);
diff --git a/app/node/input/CMakeLists.txt b/app/node/input/CMakeLists.txt
new file mode 100644
index 000000000..2a8c2fc3a
--- /dev/null
+++ b/app/node/input/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Olive - Non-Linear Video Editor
+# Copyright (C) 2019 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 .
+
+add_subdirectory(image)
+
+set(OLIVE_SOURCES
+ ${OLIVE_SOURCES}
+ PARENT_SCOPE
+)
diff --git a/app/node/input/image/CMakeLists.txt b/app/node/input/image/CMakeLists.txt
new file mode 100644
index 000000000..48e815787
--- /dev/null
+++ b/app/node/input/image/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Olive - Non-Linear Video Editor
+# Copyright (C) 2019 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 .
+
+set(OLIVE_SOURCES
+ ${OLIVE_SOURCES}
+ node/input/image/image.h
+ node/input/image/image.cpp
+ PARENT_SCOPE
+)
diff --git a/app/node/input/image/image.cpp b/app/node/input/image/image.cpp
new file mode 100644
index 000000000..9c8efb7f2
--- /dev/null
+++ b/app/node/input/image/image.cpp
@@ -0,0 +1,46 @@
+#include "image.h"
+
+ImageInput::ImageInput() :
+ texture_(nullptr)
+{
+ texture_output_ = new NodeOutput();
+ texture_output_->set_data_type(NodeOutput::kTexture);
+ AddParameter(texture_output_);
+}
+
+QString ImageInput::Name()
+{
+ return tr("Image");
+}
+
+QString ImageInput::Category()
+{
+ return tr("Input");
+}
+
+QString ImageInput::Description()
+{
+ return tr("Import an image file.");
+}
+
+NodeOutput *ImageInput::texture_output()
+{
+ return texture_output_;
+}
+
+void ImageInput::Process(const rational &time)
+{
+ // FIXME: Use OIIO and OCIO here
+
+ // FIXME: Test code
+ Q_UNUSED(time)
+
+ if (texture_ == nullptr) {
+ QImage img("/home/matt/Desktop/oof.png");
+
+ texture_ = new QOpenGLTexture(img);
+ }
+
+ texture_output_->set_value(texture_->textureId());
+ // End test code
+}
diff --git a/app/node/input/image/image.h b/app/node/input/image/image.h
new file mode 100644
index 000000000..e64a541c8
--- /dev/null
+++ b/app/node/input/image/image.h
@@ -0,0 +1,29 @@
+#ifndef IMAGE_H
+#define IMAGE_H
+
+#include
+
+#include "node/node.h"
+
+class ImageInput : public Node
+{
+ Q_OBJECT
+public:
+ ImageInput();
+
+ virtual QString Name() override;
+ virtual QString Category() override;
+ virtual QString Description() override;
+
+ NodeOutput* texture_output();
+
+public slots:
+ virtual void Process(const rational &time) override;
+
+private:
+ NodeOutput* texture_output_;
+
+ QOpenGLTexture* texture_;
+};
+
+#endif // IMAGE_H
diff --git a/app/node/param.cpp b/app/node/param.cpp
index 683013410..553b978ad 100644
--- a/app/node/param.cpp
+++ b/app/node/param.cpp
@@ -113,9 +113,7 @@ bool NodeParam::AreDataTypesCompatible(const DataType &output_type, const QList<
NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
{
// If the input can only accept one input (the default) and has one already, disconnect it
- if (!input->edges_.isEmpty() && !input->can_accept_multiple_inputs()) {
- DisconnectEdge(input->edges_.first());
- }
+ FreeSpaceForEdgeFromInput(input);
// Make sure it's not a duplicate of an edge that already exists
foreach (NodeEdgePtr existing, input->edges()) {
@@ -146,6 +144,31 @@ void NodeParam::DisconnectEdge(NodeEdgePtr edge)
emit output->EdgeRemoved(edge);
}
+void NodeParam::DisconnectEdge(NodeOutput *output, NodeInput *input)
+{
+ for (int i=0;iedges_.size();i++) {
+ NodeEdgePtr edge = output->edges_.at(i);
+ if (edge->input() == input) {
+ DisconnectEdge(edge);
+ break;
+ }
+ }
+}
+
+NodeEdgePtr NodeParam::FreeSpaceForEdgeFromInput(NodeInput *input)
+{
+ // If the input can only accept one input (the default) and has one already, disconnect it
+ if (!input->edges_.isEmpty() && !input->can_accept_multiple_inputs()) {
+ NodeEdgePtr edge = input->edges_.first();
+
+ DisconnectEdge(edge);
+
+ return edge;
+ }
+
+ return nullptr;
+}
+
QString NodeParam::GetDefaultDataTypeName(const DataType& type)
{
switch (type) {
diff --git a/app/node/param.h b/app/node/param.h
index 5f6f5a734..7ae269af8 100644
--- a/app/node/param.h
+++ b/app/node/param.h
@@ -72,6 +72,9 @@ public:
static NodeEdgePtr ConnectEdge(NodeOutput *output, NodeInput *input);
static void DisconnectEdge(NodeEdgePtr edge);
+ static void DisconnectEdge(NodeOutput* output, NodeInput* input);
+
+ static NodeEdgePtr FreeSpaceForEdgeFromInput(NodeInput* input);
static QString GetDefaultDataTypeName(const DataType &type);
diff --git a/app/widget/nodeview/CMakeLists.txt b/app/widget/nodeview/CMakeLists.txt
index 9103c574f..9f28bd1b1 100644
--- a/app/widget/nodeview/CMakeLists.txt
+++ b/app/widget/nodeview/CMakeLists.txt
@@ -24,5 +24,7 @@ set(OLIVE_SOURCES
widget/nodeview/nodeviewitem.cpp
widget/nodeview/nodeviewitemwidgetproxy.h
widget/nodeview/nodeviewitemwidgetproxy.cpp
+ widget/nodeview/nodeviewundo.h
+ widget/nodeview/nodeviewundo.cpp
PARENT_SCOPE
)
diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp
index 242b2f369..ea1b04c16 100644
--- a/app/widget/nodeview/nodeviewitem.cpp
+++ b/app/widget/nodeview/nodeviewitem.cpp
@@ -28,7 +28,9 @@
#include "core.h"
#include "nodeview.h"
+#include "nodeviewundo.h"
#include "ui/icons/icons.h"
+#include "undo/undostack.h"
#include "window/mainwindow/mainwindow.h"
NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
@@ -36,8 +38,10 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
node_(nullptr),
font_metrics(font),
dragging_edge_(nullptr),
+ drag_expanded_item_(nullptr),
expanded_(false),
- standard_click_(false)
+ standard_click_(false),
+ node_edge_change_command_(nullptr)
{
// Set flags for this widget
setFlag(QGraphicsItem::ItemIsMovable);
@@ -131,7 +135,6 @@ QRectF NodeViewItem::GetParameterConnectorRect(int index)
connector_rect.translate(0, font_metrics.height() * index);
}
- // FIXME: I don't know how this will work with NodeParam::kBidirectional
if (param->type() == NodeParam::kOutput) {
connector_rect.translate(rect().width() - node_connector_size_, 0);
}
@@ -147,7 +150,6 @@ QPointF NodeViewItem::GetParameterTextPoint(int index)
NodeParam* param = node_->ParamAt(index);
- // FIXME: I don't know how this will work with NodeParam::kBidirectional
if (param->type() == NodeParam::kOutput) {
return content_rect_.topRight() + QPointF(-(node_connector_size_ + node_text_padding_),
node_text_padding_ + font_metrics.ascent() + font_metrics.height()*index);
@@ -198,7 +200,6 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
// Draw text
QPointF text_pt = GetParameterTextPoint(i);
- // FIXME: I don't know how this will work for kBidirectional
if (param->type() == NodeParam::kOutput) {
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
text_pt -= QPointF(font_metrics.width(param->name()), 0);
@@ -281,6 +282,12 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
// Create draggable object
dragging_edge_ = new NodeViewEdge();
+ // Clear any existing node edge command
+ // FIXME: Is this ever necessary?
+ delete node_edge_change_command_;
+
+ node_edge_change_command_ = new QUndoCommand();
+
if (param->type() == NodeParam::kOutput || param->edges().isEmpty()) {
// For an output param (or an input param with no connections), we default to creating a new edge
drag_source_ = this;
@@ -295,7 +302,8 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
NodeEdgePtr edge = param->edges().last();
// Remove old edge
- NodeParam::DisconnectEdge(edge);
+ NodeEdgeRemoveCommand* remove_command = new NodeEdgeRemoveCommand(edge->output(), edge->input(), node_edge_change_command_);
+ remove_command->redo();
// The starting position will be the OPPOSING parameter's rectangle
@@ -335,8 +343,18 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
// See if the mouse is currently inside a node
NodeViewItem* drop_item = dynamic_cast(scene()->itemAt(event->scenePos(), sceneTransform()));
+
+ // If we expanded an item below but are no longer dragging over it, re-collapse it
+ if (drag_expanded_item_ != nullptr && drop_item != drag_expanded_item_) {
+ drag_expanded_item_->SetExpanded(false);
+ drag_expanded_item_ = nullptr;
+ }
+
if (drop_item != nullptr && drop_item != drag_source_) {
- if (drop_item->IsExpanded()) {
+
+ // If the item we're dragging over is collapsed, expand it
+ if (!drop_item->IsExpanded()) {
+ drag_expanded_item_ = drop_item;
drop_item->SetExpanded(true);
}
@@ -382,21 +400,41 @@ void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
// FIXME: Make this undoable
+ // Remove the drag object
scene()->removeItem(dragging_edge_);
+ // If we expanded an item in the drag, re-collapse it now
+ if (drag_expanded_item_ != nullptr) {
+ drag_expanded_item_->SetExpanded(false);
+ drag_expanded_item_ = nullptr;
+ }
+
if (drag_dest_param_ != nullptr) {
// We dragged to somewhere, so we'll make a new connection
NodeEdgePtr new_edge;
+ // Connecting will automatically add an edge UI object through the signal/slot system
if (drag_dest_param_->type() == NodeParam::kOutput) {
- new_edge = NodeParam::ConnectEdge(static_cast(drag_dest_param_), static_cast(drag_src_param_));
+ new NodeEdgeAddCommand(static_cast(drag_dest_param_),
+ static_cast(drag_src_param_),
+ node_edge_change_command_);
+
} else {
- new_edge = NodeParam::ConnectEdge(static_cast(drag_src_param_), static_cast(drag_dest_param_));
+ new NodeEdgeAddCommand(static_cast(drag_src_param_),
+ static_cast(drag_dest_param_),
+ node_edge_change_command_);
}
}
dragging_edge_ = nullptr;
+
+ if (node_edge_change_command_->childCount() > 0) {
+ olive::undo_stack.push(node_edge_change_command_);
+ } else {
+ delete node_edge_change_command_;
+ }
+ node_edge_change_command_ = nullptr;
return;
}
diff --git a/app/widget/nodeview/nodeviewitem.h b/app/widget/nodeview/nodeviewitem.h
index bee61490e..47b10632c 100644
--- a/app/widget/nodeview/nodeviewitem.h
+++ b/app/widget/nodeview/nodeviewitem.h
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include "node/node.h"
@@ -73,6 +74,7 @@ private:
NodeParam* drag_src_param_;
NodeParam* drag_dest_param_;
NodeViewItem* drag_source_;
+ NodeViewItem* drag_expanded_item_;
int node_connector_size_;
int node_text_padding_;
@@ -82,6 +84,9 @@ private:
bool expanded_;
bool standard_click_;
+
+ QUndoCommand* node_edge_change_command_;
+
};
#endif // NODEVIEWITEM_H
diff --git a/app/widget/nodeview/nodeviewundo.cpp b/app/widget/nodeview/nodeviewundo.cpp
new file mode 100644
index 000000000..9836bc97a
--- /dev/null
+++ b/app/widget/nodeview/nodeviewundo.cpp
@@ -0,0 +1,66 @@
+#include "nodeviewundo.h"
+
+NodeEdgeAddCommand::NodeEdgeAddCommand(NodeOutput *output, NodeInput *input, QUndoCommand *parent) :
+ QUndoCommand(parent),
+ output_(output),
+ input_(input),
+ old_edge_(nullptr),
+ done_(false)
+{
+}
+
+void NodeEdgeAddCommand::redo()
+{
+ if (done_) {
+ return;
+ }
+
+ old_edge_ = NodeParam::FreeSpaceForEdgeFromInput(input_);
+
+ NodeParam::ConnectEdge(output_, input_);
+
+ done_ = true;
+}
+
+void NodeEdgeAddCommand::undo()
+{
+ if (!done_) {
+ return;
+ }
+
+ NodeParam::DisconnectEdge(output_, input_);
+
+ if (old_edge_ != nullptr) {
+ NodeParam::ConnectEdge(old_edge_->output(), old_edge_->input());
+ }
+
+ done_ = false;
+}
+
+NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(NodeOutput *output, NodeInput *input, QUndoCommand *parent) :
+ QUndoCommand(parent),
+ output_(output),
+ input_(input),
+ done_(false)
+{
+}
+
+void NodeEdgeRemoveCommand::redo()
+{
+ if (done_) {
+ return;
+ }
+
+ NodeParam::DisconnectEdge(output_, input_);
+ done_ = true;
+}
+
+void NodeEdgeRemoveCommand::undo()
+{
+ if (!done_) {
+ return;
+ }
+
+ NodeParam::ConnectEdge(output_, input_);
+ done_ = false;
+}
diff --git a/app/widget/nodeview/nodeviewundo.h b/app/widget/nodeview/nodeviewundo.h
new file mode 100644
index 000000000..256a24537
--- /dev/null
+++ b/app/widget/nodeview/nodeviewundo.h
@@ -0,0 +1,38 @@
+#ifndef NODEVIEWUNDO_H
+#define NODEVIEWUNDO_H
+
+#include
+
+#include "node/node.h"
+
+class NodeEdgeAddCommand : public QUndoCommand {
+public:
+ NodeEdgeAddCommand(NodeOutput* output, NodeInput* input, QUndoCommand* parent = nullptr);
+
+ virtual void redo() override;
+ virtual void undo() override;
+
+private:
+ NodeOutput* output_;
+ NodeInput* input_;
+
+ NodeEdgePtr old_edge_;
+
+ bool done_;
+};
+
+class NodeEdgeRemoveCommand : public QUndoCommand {
+public:
+ NodeEdgeRemoveCommand(NodeOutput* output, NodeInput* input, QUndoCommand* parent = nullptr);
+
+ virtual void redo() override;
+ virtual void undo() override;
+
+private:
+ NodeOutput* output_;
+ NodeInput* input_;
+
+ bool done_;
+};
+
+#endif // NODEVIEWUNDO_H
diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp
index 427bf03ba..c8f80e78c 100644
--- a/app/window/mainwindow/mainwindow.cpp
+++ b/app/window/mainwindow/mainwindow.cpp
@@ -33,6 +33,7 @@
#include "mainmenu.h"
// FIXME: Test code
+#include "node/input/image/image.h"
#include "node/output/viewer/viewer.h"
#include "node/generator/solid/solid.h"
// End test code
@@ -89,6 +90,8 @@ void olive::MainWindow::ProjectOpen(Project* p)
SolidGenerator* sg = new SolidGenerator();
NodeInput::ConnectEdge(sg->texture_output(), vo->texture_input());
graph->AddNode(sg);
+ ImageInput* ii = new ImageInput();
+ graph->AddNode(ii);
task_panel->SetGraph(graph);
// End test code
}