diff --git a/.bak/nodeview/nodeview.cpp b/.bak/nodeview/nodeview.cpp
deleted file mode 100644
index f7bd014c8..000000000
--- a/.bak/nodeview/nodeview.cpp
+++ /dev/null
@@ -1,1982 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "nodeview.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "node/audio/volume/volume.h"
-#include "node/distort/transform/transformdistortnode.h"
-#include "node/group/group.h"
-#include "node/nodeundo.h"
-#include "node/project/serializer/serializer.h"
-#include "panel/panelmanager.h"
-#include "node/traverser.h"
-#include "ui/icons/icons.h"
-#include "widget/menu/factorymenu.h"
-#include "widget/menu/menushared.h"
-#include "widget/timebased/timebasedview.h"
-
-#define super HandMovableView
-
-namespace olive
-{
-
-const double NodeView::k_minimum_scale = 0.1;
-const int NodeView::k_maximum_contexts = 10;
-
-NodeView::NodeView(QWidget *parent)
- : HandMovableView(parent)
- , drop_edge_(nullptr)
- , create_edge_(nullptr)
- , create_edge_output_item_(nullptr)
- , create_edge_input_item_(nullptr)
- , overlay_view_(nullptr)
- , scale_(1.0)
- , dont_emit_selection_signals_(false)
- , show_in_param_editor_action_(nullptr)
-{
- setScene(&scene_);
- set_default_drag_mode(RubberBandDrag);
- setContextMenuPolicy(Qt::CustomContextMenu);
- setMouseTracking(true);
- setRenderHint(QPainter::Antialiasing);
- setViewportUpdateMode(FullViewportUpdate);
-
- connect(this, &NodeView::customContextMenuRequested, this,
- &NodeView::show_context_menu);
-
- connect_selection_changed_signal();
-
- set_flow_direction(NodeViewCommon::k_left_to_right);
-
- show_in_param_editor_action_ =
- new QAction(tr("Show in Parameter Editor"), this);
- Menu::conform_item(show_in_param_editor_action_,
- QStringLiteral("shownodeparams"),
- QKeySequence(tr("Shift+P")));
- show_in_param_editor_action_->setShortcutContext(Qt::WindowShortcut);
- addAction(show_in_param_editor_action_);
- connect(show_in_param_editor_action_, &QAction::triggered, this,
- &NodeView::show_selected_node_in_param_editor);
-
- update_scene_bounding_rect();
- connect(&scene_, &QGraphicsScene::changed, this,
- &NodeView::update_scene_bounding_rect);
-
- minimap_ = new NodeViewMiniMap(&scene_, this);
- minimap_->show();
- connect(minimap_, &NodeViewMiniMap::resized, this,
- &NodeView::reposition_mini_map);
- connect(minimap_, &NodeViewMiniMap::move_to_scene_point, this,
- &NodeView::move_to_scene_point);
- connect(horizontalScrollBar(), &QScrollBar::valueChanged, this,
- &NodeView::update_viewport_on_mini_map);
- connect(verticalScrollBar(), &QScrollBar::valueChanged, this,
- &NodeView::update_viewport_on_mini_map);
-
- viewport()->installEventFilter(this);
-}
-
-NodeView::~NodeView()
-{
- // Unset the current graph
- clear_graph();
-}
-
-void NodeView::set_contexts(const QVector &nodes)
-{
- if (overlay_view_) {
- close_overlay();
- }
-
- // Remove contexts that are no longer in the list
- foreach (Node *n, contexts_) {
- if (!nodes.contains(n)) {
- remove_context(n);
- }
- }
-
- // Add contexts that are now in the list
- foreach (Node *n, nodes) {
- if (scene_.context_map().size() >= k_maximum_contexts) {
- break;
- }
-
- if (!contexts_.contains(n)) {
- add_context(n);
- }
- }
-
- contexts_ = nodes;
-
- center_on_items_bounding_rect();
-}
-
-void NodeView::close_contexts_belonging_to_project(Project *project)
-{
- QVector new_contexts = contexts_;
-
- for (auto it = new_contexts.begin(); it != new_contexts.end();) {
- if ((*it)->project() == project) {
- it = new_contexts.erase(it);
- } else {
- it++;
- }
- }
-
- set_contexts(new_contexts);
-}
-
-void NodeView::clear_graph()
-{
- set_contexts(QVector());
-}
-
-void NodeView::delete_selected()
-{
- NodeViewDeleteCommand *command = new NodeViewDeleteCommand();
-
- int count = 0;
-
- foreach (NodeViewContext *ctx, scene_.context_map()) {
- count += ctx->delete_selected(command);
- }
-
- Core::instance()->undo_stack()->push(command,
- tr("Deleted %1 Node(s)").arg(count));
-}
-
-void NodeView::select_all()
-{
- // Optimization: rather than respond to every single item being selected, ignore the signal and
- // then handle them all at the end.
- disconnect_selection_changed_signal();
-
- scene_.select_all();
-
- connect_selection_changed_signal();
-
- update_selection_cache();
-}
-
-void NodeView::deselect_all()
-{
- if (selected_nodes_.isEmpty()) {
- return;
- }
-
- // Optimization: rather than respond to every single item being selected, ignore the signal and
- // then handle them all at the end.
- disconnect_selection_changed_signal();
-
- scene_.deselect_all();
-
- connect_selection_changed_signal();
-
- // Just emit all the nodes that are currently selected as no longer selected
- emit nodes_deselected(selected_nodes_);
- selected_nodes_.clear();
- emit node_selection_changed(selected_nodes_);
- emit node_selection_changed_with_contexts(QVector());
-}
-
-void NodeView::select(const QVector &nodes,
- bool center_view_on_item)
-{
- // Optimization: rather than respond to every single item being selected, ignore the signal and
- // then handle them all at the end.
- disconnect_selection_changed_signal();
-
- QVector deselections = selected_nodes_;
- QVector new_selections;
-
- scene_.deselect_all();
-
- foreach (const Node::ContextPair &p, nodes) {
- NodeViewContext *ctx = scene_.context_map().value(p.context);
- if (ctx) {
- NodeViewItem *item = ctx->get_item_from_map(p.node);
- if (item) {
- item->setSelected(true);
- }
- }
- }
-
- // Center on something
- if (center_view_on_item && !nodes.isEmpty()) {
- QMetaObject::invokeMethod(this, "center_on_node", Qt::QueuedConnection,
- OLIVE_NS_ARG(Node *, nodes.first().node));
- }
-
- connect_selection_changed_signal();
-
- // Don't signal when this function was likely triggered from another widget's signal anyway
- dont_emit_selection_signals_ = true;
- update_selection_cache();
- dont_emit_selection_signals_ = false;
-}
-
-void NodeView::copy_selected(bool cut)
-{
- if (selected_nodes_.isEmpty()) {
- return;
- }
-
- QString copy_str;
- QXmlStreamWriter writer(©_str);
-
- ProjectSerializer::SaveData sdata(ProjectSerializer::k_only_nodes);
- sdata.set_only_serialize_nodes_and_resolve_groups(selected_nodes_);
-
- ProjectSerializer::SerializedProperties properties;
-
- for (Node *n : selected_nodes_) {
- NodeViewItem *item = get_assumed_item_for_selected_node(n);
-
- if (item) {
- Node::Position pos = item->get_node_position_data();
-
- properties[n][QStringLiteral("x")] =
- QString::number(pos.position.x());
- properties[n][QStringLiteral("y")] =
- QString::number(pos.position.y());
- properties[n][QStringLiteral("expanded")] =
- QString::number(pos.expanded);
- }
- }
-
- sdata.set_properties(properties);
-
- ProjectSerializer::save(&writer, sdata);
-
- Core::copy_string_to_clipboard(copy_str);
-
- if (cut) {
- delete_selected();
- }
-}
-
-void NodeView::paste()
-{
- if (contexts_.isEmpty()) {
- return;
- }
-
- ProjectSerializer::Result res =
- ProjectSerializer::paste(ProjectSerializer::k_only_nodes);
- if (res.get_load_data().nodes.isEmpty()) {
- return;
- }
-
- Node::PositionMap map;
-
- for (auto it = res.get_load_data().properties.cbegin();
- it != res.get_load_data().properties.cend(); it++) {
- Node::Position pos;
-
- const QMap &node_props = it.value();
- pos.position.setX(node_props.value(QStringLiteral("x")).toDouble());
- pos.position.setY(node_props.value(QStringLiteral("y")).toDouble());
- pos.expanded = node_props.value(QStringLiteral("expanded")).toDouble();
-
- map.insert(it.key(), pos);
- }
-
- post_paste(res.get_load_data().nodes, map);
-}
-
-void NodeView::duplicate()
-{
- if (!selected_nodes_.isEmpty()) {
- QVector selected = selected_nodes_;
- QVector new_nodes;
- Node::PositionMap map;
-
- new_nodes.resize(selected.size());
-
- // Create copies of each selected node, checking for groups and adding children if necessary
- for (int i = 0; i < selected.size(); i++) {
- new_nodes[i] = selected.at(i)->copy();
-
- if (NodeGroup *g = dynamic_cast(selected.at(i))) {
- for (auto it = g->get_context_positions().cbegin();
- it != g->get_context_positions().cend(); it++) {
- if (!selected.contains(it.key())) {
- // This should automatically recurse if this is a group inside a group
- selected.append(it.key());
- }
- }
- new_nodes.resize(selected.size());
- }
- }
-
- // Get positions in contexts, add input passthroughs, and copy input values/keyframes
- for (int i = 0; i < new_nodes.size(); i++) {
- Node *og = selected.at(i);
- Node *copy = new_nodes.at(i);
-
- Node::Position pos;
- if (get_assumed_position_for_selected_node(og, &pos)) {
- map.insert(copy, pos);
- }
-
- for (auto it = og->get_context_positions().cbegin();
- it != og->get_context_positions().cend(); it++) {
- Node *child_og = it.key();
- int child_index = selected.indexOf(child_og);
-
- if (child_index != -1) {
- Node *child_copy = new_nodes.at(child_index);
-
- copy->set_node_position_in_context(child_copy, it.value());
- }
- }
-
- if (NodeGroup *src_group = dynamic_cast(og)) {
- NodeGroup *dst_group = static_cast(copy);
-
- for (auto it = src_group->get_input_passthroughs().cbegin();
- it != src_group->get_input_passthroughs().cend(); it++) {
- NodeInput input = it->second;
- input.set_node(
- new_nodes.at(selected.indexOf(input.node())));
- dst_group->add_input_passthrough(input, it->first);
- }
-
- dst_group->set_output_passthrough(new_nodes.at(
- selected.indexOf(src_group->get_output_passthrough())));
- }
-
- Node::copy_inputs(selected.at(i), new_nodes.at(i), false);
- }
-
- // Copy connections
- Node::copy_dependency_graph(selected, new_nodes, nullptr);
-
- // Set root level context positions and attach to
- post_paste(new_nodes, map);
- }
-}
-
-void NodeView::set_color_label(int index)
-{
- MultiUndoCommand *command = new MultiUndoCommand();
-
- for (Node *node : qAsConst(selected_nodes_)) {
- command->add_child(new NodeOverrideColorCommand(node, index));
- }
-
- Core::instance()->undo_stack()->push(
- command, tr("Set Color of %1 Node(s)").arg(selected_nodes_.size()));
-}
-
-void NodeView::zoom_in()
-{
- zoom_from_keyboard(1.25);
-}
-
-void NodeView::zoom_out()
-{
- zoom_from_keyboard(0.8);
-}
-
-void NodeView::keyPressEvent(QKeyEvent *event)
-{
- switch (event->key()) {
- case Qt::Key_Left:
- case Qt::Key_Right:
- case Qt::Key_Up:
- case Qt::Key_Down: {
- MultiUndoCommand *pos_command = new MultiUndoCommand();
- for (Node *n : qAsConst(selected_nodes_)) {
- for (Node *context : qAsConst(contexts_)) {
- if (context->context_contains_node(n)) {
- Node::Position old_pos =
- context->get_node_position_in_context(n);
-
- // Determine one pixel in scene units
- double movement_amt = 1.0 / scale_;
-
- // Translate to 2D movement
- QPointF node_movement;
- switch (event->key()) {
- case Qt::Key_Left:
- node_movement.setX(-movement_amt);
- break;
- case Qt::Key_Right:
- node_movement.setX(movement_amt);
- break;
- case Qt::Key_Up:
- node_movement.setY(-movement_amt);
- break;
- case Qt::Key_Down:
- node_movement.setY(movement_amt);
- break;
- }
-
- // Translate from screen units into node units
- node_movement = NodeViewItem::screen_to_node_point(
- node_movement, scene_.get_flow_direction());
-
- // Move command
- pos_command->add_child(new NodeSetPositionCommand(
- n, context, old_pos + node_movement));
- }
- }
- }
- Core::instance()->undo_stack()->push(
- pos_command, tr("Moved %1 Node(s)").arg(selected_nodes_.size()));
- break;
- }
- case Qt::Key_Escape:
- if (!attached_items_.isEmpty()) {
- detach_items_from_cursor();
- break;
- }
-
- emit esc_pressed();
-
- /* fall through */
- default:
- super::keyPressEvent(event);
- break;
- }
-}
-
-void NodeView::mousePressEvent(QMouseEvent *event)
-{
- // Handle mouse press event
- if (hand_press(event))
- return;
-
- // Get the item that the user clicked on, if any
- QGraphicsItem *item = itemAt(event->pos());
-
- if (event->button() == Qt::LeftButton) {
- // Sane defaults
- create_edge_already_exists_ = false;
- create_edge_from_output_ = true;
- create_edge_input_.reset();
-
- if (event->modifiers() & Qt::ControlModifier) {
- NodeViewItem *mouse_item = dynamic_cast(item);
-
- if (mouse_item) {
- if (mouse_item->is_output_item()) {
- create_edge_output_item_ = mouse_item;
- } else {
- create_edge_input_item_ = mouse_item;
- create_edge_input_ = mouse_item->get_input();
- create_edge_from_output_ = false;
- }
-
- // Highlight start item for better user experience
- mouse_item->set_highlighted(true);
- }
- }
-
- if (!create_edge_output_item_ && !create_edge_input_item_) {
- // Determine if user clicked on a connector
- if (NodeViewItemConnector *connector =
- dynamic_cast(item)) {
- NodeViewItem *attached =
- static_cast(connector->parentItem());
-
- if (connector->is_output()) {
- create_edge_output_item_ = attached;
- } else {
- create_edge_input_item_ = attached;
-
- if (!create_edge_input_item_->edges().isEmpty()) {
- // Drag existing edge instead
- create_edge_ = create_edge_input_item_->edges().first();
- create_edge_input_item_ = nullptr;
- create_edge_output_item_ = create_edge_->from_item();
- create_edge_already_exists_ = true;
- } else {
- create_edge_from_output_ = false;
- create_edge_input_ =
- create_edge_input_item_->get_input();
- }
- }
- }
- }
-
- if ((create_edge_output_item_ || create_edge_input_item_) &&
- !create_edge_already_exists_) {
- // Create a new edge from this output
- create_edge_ = new NodeViewEdge();
- create_edge_->set_curved(scene_.get_edges_are_curved());
-
- // Add edge to scene
- scene_.addItem(create_edge_);
-
- // Position edge to mouse cursor
- position_new_edge(event->pos());
- return;
- }
- }
-
- // Handle selections with the right mouse button
- if (event->button() == Qt::RightButton) {
- if (!item || !item->isSelected()) {
- // Qt doesn't do this by default for some reason
- if (!(event->modifiers() & Qt::ShiftModifier)) {
- scene_.clearSelection();
- }
-
- // If there's an item here, select it
- if (item) {
- item->setSelected(true);
- }
- }
- }
-
- if (attached_items_.isEmpty()) {
- // Default QGraphicsView functionality (selecting, dragging, etc.)
- super::mousePressEvent(event);
- }
-
- // For any selected item, store its position in case the user is dragging it somewhere else
- auto selected_items = scene_.get_selected_items();
- foreach (NodeViewItem *i, selected_items) {
- // Ignore items attached to the cursor
- if (!is_item_attached_to_cursor(i)) {
- dragging_items_.insert(i, i->get_node_position());
- }
- }
-}
-
-void NodeView::mouseMoveEvent(QMouseEvent *event)
-{
- if (hand_move(event))
- return;
-
- if (create_edge_) {
- position_new_edge(event->pos());
- return;
- }
-
- if (attached_items_.isEmpty()) {
- super::mouseMoveEvent(event);
- }
-
- // See if there are any items attached
- if (!attached_items_.isEmpty()) {
- process_moving_attached_nodes(event->pos());
- }
-}
-
-void NodeView::mouseReleaseEvent(QMouseEvent *event)
-{
- if (hand_release(event))
- return;
-
- if (create_edge_) {
- end_edge_drag();
- }
-
- MultiUndoCommand *command = new MultiUndoCommand();
-
- Node *select_context = nullptr;
- QVector select_nodes;
-
- bool had_attached_items = !attached_items_.isEmpty();
-
- if (!attached_items_.isEmpty()) {
- select_context = get_context_at_mouse_pos(event->pos());
-
- if (select_context) {
- select_nodes = process_dropping_attached_nodes(command, select_context,
- event->pos());
- } else {
- QToolTip::showText(QCursor::pos(),
- tr("Nodes must be placed inside a context."));
- }
- }
-
- QList> dragged_items;
- for (auto it = dragging_items_.cbegin(); it != dragging_items_.cend();
- it++) {
- dragged_items.append(it.key());
- }
-
- foreach (NodeViewItem *i, dragged_items) {
- if (!i) {
- continue;
- }
- QPointF current_pos = i->get_node_position();
- if (dragging_items_.value(i) != current_pos) {
- command->add_child(new NodeSetPositionCommand(
- i->get_node(), i->get_context(), current_pos));
- }
- }
-
- Core::instance()->undo_stack()->push(
- command, tr("Moved %1 Node(s)").arg(dragging_items_.size()));
-
- dragging_items_.clear();
-
- if (!had_attached_items) {
- super::mouseReleaseEvent(event);
- }
-
- if (select_context) {
- deselect_all();
- scene_.context_map().value(select_context)->select(select_nodes);
- }
-}
-
-void NodeView::mouseDoubleClickEvent(QMouseEvent *event)
-{
- super::mouseDoubleClickEvent(event);
-
- if (!(event->modifiers() & Qt::ControlModifier)) {
- NodeViewItem *item_at_cursor =
- dynamic_cast(itemAt(event->pos()));
- if (item_at_cursor) {
- item_at_cursor->toggle_expanded();
- }
- }
-}
-
-void NodeView::dragEnterEvent(QDragEnterEvent *event)
-{
- if (contexts_.empty()) {
- event->ignore();
- return;
- }
-
- QStringList mime_fmts = event->mimeData()->formats();
-
- if (mime_fmts.contains(Project::k_item_mime_type)) {
- QByteArray model_data = event->mimeData()->data(Project::k_item_mime_type);
- QDataStream stream(&model_data, QIODevice::ReadOnly);
-
- // Variables to deserialize into
- quintptr item_ptr;
- QVector enabled_streams;
- QVector new_attached;
-
- int y = 0;
-
- while (!stream.atEnd()) {
- stream >> enabled_streams >> item_ptr;
-
- // Get Item object
- Node *item = reinterpret_cast(item_ptr);
-
- if (ViewerOutput *f = dynamic_cast(item)) {
- NodeViewItem *new_item;
-
- new_item = new NodeViewItem(f, nullptr);
- new_item->set_flow_direction(scene_.get_flow_direction());
- new_item->set_node_position(QPointF(0, y));
- y++;
- scene_.addItem(new_item);
-
- new_attached.append({ new_item, f, new_item->pos() });
- }
- }
-
- if (new_attached.empty()) {
- event->ignore();
- } else {
- set_attached_items(new_attached);
-
- event->accept();
- }
- }
-}
-
-void NodeView::dragMoveEvent(QDragMoveEvent *event)
-{
- if (attached_items_.empty()) {
- event->ignore();
- } else {
- process_moving_attached_nodes(event->pos());
-
- if (get_context_at_mouse_pos(event->pos())) {
- event->accept();
- } else {
- event->ignore();
- }
- }
-}
-
-void NodeView::dropEvent(QDropEvent *event)
-{
- if (Node *drop_ctx = get_context_at_mouse_pos(event->pos())) {
- MultiUndoCommand *command = new MultiUndoCommand();
- QVector select_nodes =
- process_dropping_attached_nodes(command, drop_ctx, event->pos());
- Core::instance()->undo_stack()->push(
- command, tr("Dropped %1 Node(s)").arg(select_nodes.size()));
-
- deselect_all();
- scene_.context_map().value(drop_ctx)->select(select_nodes);
-
- event->accept();
- } else {
- detach_items_from_cursor(false);
- event->ignore();
- }
-}
-
-void NodeView::dragLeaveEvent(QDragLeaveEvent *event)
-{
- if (attached_items_.empty()) {
- event->ignore();
- } else {
- detach_items_from_cursor(false);
-
- event->accept();
- }
-}
-
-void NodeView::resizeEvent(QResizeEvent *event)
-{
- super::resizeEvent(event);
-
- reposition_mini_map();
-
- if (overlay_view_) {
- resize_overlay();
- }
-}
-
-void NodeView::update_selection_cache()
-{
- QVector current_selection = scene_.get_selected_items();
-
- QVector selected;
- QVector deselected;
-
- QVector sel_with_ctx(current_selection.size());
-
- // Determine which nodes are newly selected
- for (int j = 0; j < current_selection.size(); j++) {
- NodeViewItem *i = current_selection.at(j);
- Node *n = i->get_node();
- if (!selected_nodes_.contains(n)) {
- selected.append(n);
- selected_nodes_.append(n);
- }
-
- sel_with_ctx[j] = { n, i->get_context() };
- }
-
- // Determine which nodes are newly deselected
- if (current_selection.isEmpty()) {
- // All nodes that were selected have been deselected, so we'll just set them all to `deselected`
- deselected = selected_nodes_;
- selected_nodes_.clear();
- } else {
- foreach (Node *n, selected_nodes_) {
- bool still_selected = false;
-
- foreach (NodeViewItem *i, current_selection) {
- if (i->get_node() == n) {
- still_selected = true;
- break;
- }
- }
-
- if (!still_selected) {
- deselected.append(n);
- selected_nodes_.removeOne(n);
- }
- }
- }
-
- if (!deselected.isEmpty()) {
- emit nodes_deselected(deselected);
- }
-
- if (!selected.isEmpty()) {
- emit nodes_selected(selected);
- }
-
- if (!dont_emit_selection_signals_) {
- emit node_selection_changed(selected_nodes_);
- emit node_selection_changed_with_contexts(sel_with_ctx);
- }
-}
-
-void NodeView::show_context_menu(const QPoint &pos)
-{
- if (contexts_.isEmpty()) {
- return;
- }
-
- Menu m;
-
- MenuShared::instance()->add_items_for_edit_menu(&m, false);
-
- m.addSeparator();
-
- QVector selected = scene_.get_selected_items();
-
- NodeViewItem *item_under_cursor = dynamic_cast(itemAt(pos));
-
- if (item_under_cursor && !selected.contains(item_under_cursor)) {
- // Right-clicked a node that isn't part of the current selection,
- // make the clicked node the sole selection so context-menu actions
- // operate on it.
- scene_.clearSelection();
- item_under_cursor->setSelected(true);
- selected = scene_.get_selected_items();
- }
-
- if (item_under_cursor && !selected.isEmpty()) {
- // Grouping
- if (selected.size() == 1 &&
- dynamic_cast(selected.first()->get_node())) {
- QAction *ungroup_action = m.addAction(tr("Ungroup"));
- connect(ungroup_action, &QAction::triggered, this,
- &NodeView::ungroup_nodes);
- } else {
- QAction *group_action = m.addAction(tr("Group"));
- connect(group_action, &QAction::triggered, this,
- &NodeView::group_nodes);
- }
-
- // Color menu
- MenuShared::instance()->add_color_coding_menu(&m);
-
- // Show in Viewer option for nodes based on Viewer
- if (ViewerOutput *viewer =
- dynamic_cast(selected.first()->get_node())) {
- Q_UNUSED(viewer)
- m.addSeparator();
- QAction *open_in_viewer_action = m.addAction(tr("Open in Viewer"));
- connect(open_in_viewer_action, &QAction::triggered, this,
- &NodeView::open_selected_node_in_viewer);
- }
-
- m.addSeparator();
-
- // Show in Parameter Editor
- QAction *show_in_param_editor_action =
- m.addAction(tr("Show in Parameter Editor"));
- show_in_param_editor_action->setShortcut(
- show_in_param_editor_action_->shortcut());
- connect(show_in_param_editor_action, &QAction::triggered, this,
- &NodeView::show_selected_node_in_param_editor);
-
- // Properties
- QAction *properties_action = m.addAction(tr("P&roperties"));
- connect(properties_action, &QAction::triggered, this,
- &NodeView::show_node_properties);
-
- } else {
- QAction *curved_action = m.addAction(tr("Smooth Edges"));
- curved_action->setCheckable(true);
- curved_action->setChecked(scene_.get_edges_are_curved());
- connect(curved_action, &QAction::triggered, &scene_,
- &NodeViewScene::set_edges_are_curved);
-
- m.addSeparator();
-
- Menu *direction_menu = new Menu(tr("Direction"), &m);
- m.addMenu(direction_menu);
-
- direction_menu->add_action_with_data(tr("Top to Bottom"),
- NodeViewCommon::k_top_to_bottom,
- scene_.get_flow_direction());
-
- direction_menu->add_action_with_data(tr("Bottom to Top"),
- NodeViewCommon::k_bottom_to_top,
- scene_.get_flow_direction());
-
- direction_menu->add_action_with_data(tr("Left to Right"),
- NodeViewCommon::k_left_to_right,
- scene_.get_flow_direction());
-
- direction_menu->add_action_with_data(tr("Right to Left"),
- NodeViewCommon::k_right_to_left,
- scene_.get_flow_direction());
-
- connect(direction_menu, &Menu::triggered, this,
- &NodeView::context_menu_set_direction);
-
- m.addSeparator();
-
- Menu *add_menu = create_add_menu(&m);
- m.addMenu(add_menu);
- }
-
- m.exec(mapToGlobal(pos));
-}
-
-void NodeView::create_node_slot(QAction *action)
-{
- Node *new_node = create_node_from_menu_action(action);
-
- if (new_node) {
- NodeViewItem *new_item = new NodeViewItem(new_node, nullptr);
- new_item->set_flow_direction(scene_.get_flow_direction());
- scene_.addItem(new_item);
-
- QVector new_attached;
-
- new_attached.append({ new_item, new_node, QPointF(0, 0) });
-
- if (NodeGroup *new_group = dynamic_cast(new_node)) {
- for (auto it = new_group->get_context_positions().cbegin();
- it != new_group->get_context_positions().cend(); it++) {
- new_attached.append({ nullptr, it.key(), QPointF(0, 0) });
- }
- }
-
- set_attached_items(new_attached);
- }
-}
-
-void NodeView::context_menu_set_direction(QAction *action)
-{
- set_flow_direction(
- static_cast(action->data().toInt()));
-}
-
-void NodeView::open_selected_node_in_viewer()
-{
- // Find first viewer in list of selected nodes and open it
- foreach (Node *n, selected_nodes_) {
- if (ViewerOutput *viewer = dynamic_cast(n)) {
- Core::instance()->open_node_in_viewer(viewer);
- break;
- }
- }
-}
-
-void NodeView::update_scene_bounding_rect()
-{
- // Get current items bounding rect
- QRectF r = scene_.itemsBoundingRect();
-
- // Adjust so that it fills the view
- r.adjust(-width(), -height(), width(), height());
-
- // Set it
- scene_.setSceneRect(r);
-}
-
-void NodeView::center_on_items_bounding_rect()
-{
- centerOn(scene_.itemsBoundingRect().center());
-}
-
-void NodeView::center_on_node(OakEngineNode *n)
-{
- foreach (NodeViewContext *ctx, scene_.context_map()) {
- if (NodeViewItem *item = ctx->get_item_from_map(
- reinterpret_cast(n))) {
- centerOn(item);
- break;
- }
- }
-}
-
-void NodeView::reposition_mini_map()
-{
- if (minimap_->isVisible()) {
- int margin = fontMetrics().height();
-
- int w = width() - minimap_->width() - margin;
- int h = height() - minimap_->height() - margin;
-
- if (verticalScrollBar()->isVisible()) {
- w -= verticalScrollBar()->width();
- }
-
- if (horizontalScrollBar()->isVisible()) {
- h -= horizontalScrollBar()->height();
- }
-
- minimap_->move(w, h);
-
- update_viewport_on_mini_map();
- }
-}
-
-void NodeView::update_viewport_on_mini_map()
-{
- if (minimap_->isVisible()) {
- minimap_->set_viewport_rect(mapToScene(viewport()->rect()));
- }
-}
-
-void NodeView::move_to_scene_point(const QPointF &pos)
-{
- centerOn(pos);
-}
-
-void NodeView::node_removed_from_graph()
-{
- Node *context = static_cast(sender());
-
- remove_context(context);
-
- contexts_.removeOne(context);
-}
-
-void NodeView::detach_items_from_cursor(bool delete_nodes_too)
-{
- foreach (const AttachedItem &ai, attached_items_) {
- delete ai.item;
-
- if (delete_nodes_too) {
- qDebug() << "deleting" << ai.node;
- delete ai.node;
- }
- }
-
- attached_items_.clear();
-}
-
-void NodeView::set_flow_direction(NodeViewCommon::FlowDirection dir)
-{
- scene_.set_flow_direction(dir);
-}
-
-void NodeView::move_attached_nodes_to_cursor(const QPoint &p)
-{
- QPointF item_pos = mapToScene(p);
-
- for (const AttachedItem &i : qAsConst(attached_items_)) {
- if (i.item) {
- i.item->setPos(item_pos + i.original_pos);
- }
- }
-}
-
-void NodeView::process_moving_attached_nodes(const QPoint &pos)
-{
- // Move those items to the cursor
- move_attached_nodes_to_cursor(pos);
-
- // See if the user clicked on an edge (only when dropping single nodes)
- if (attached_items_.size() == 1) {
- Node *attached_node = attached_items_.first().item->get_node();
-
- QRect edge_detect_rect(pos, pos);
-
- int edge_detect_radius = fontMetrics().height();
- edge_detect_rect.adjust(-edge_detect_radius, -edge_detect_radius,
- edge_detect_radius, edge_detect_radius);
-
- QList items = this->items(edge_detect_rect);
-
- NodeViewEdge *new_drop_edge = nullptr;
-
- // See if there is an edge here
- for (QGraphicsItem *item : qAsConst(items)) {
- new_drop_edge = dynamic_cast(item);
-
- if (new_drop_edge) {
- drop_input_.reset();
-
- NodeValue::Type drop_edge_data_type =
- new_drop_edge->input().get_data_type();
-
- // Determine best input to connect to our new node
- if (attached_node->get_effect_input().is_valid()) {
- // If node specifies an effect input, use that immediately
- drop_input_ = attached_node->get_effect_input();
- } else {
- // Otherwise, we may have to iterate to find a valid one
- for (const QString &input : attached_node->inputs()) {
- if (input == Node::k_enabled_input) {
- // Ignore enabled input
- continue;
- }
-
- NodeInput i(attached_node, input);
-
- if (attached_node->is_input_connectable(input)) {
- if (attached_node->get_input_data_type(input) ==
- drop_edge_data_type) {
- // Found exactly the type we're looking for, set and break this loop
- drop_input_ = i;
- break;
- } else if (!drop_input_.is_valid()) {
- // Default to first connectable input
- drop_input_ = i;
- }
- }
- }
- }
-
- if (attached_node->inputs_from(new_drop_edge->input().node(),
- true)) {
- drop_input_.reset();
- }
-
- if (drop_input_.is_valid()) {
- break;
- } else {
- new_drop_edge = nullptr;
- }
- }
- }
-
- if (drop_edge_ != new_drop_edge) {
- if (drop_edge_) {
- drop_edge_->set_highlighted(false);
- }
-
- drop_edge_ = new_drop_edge;
-
- if (drop_edge_) {
- drop_edge_->set_highlighted(true);
- }
- }
- }
-}
-
-QVector
-NodeView::process_dropping_attached_nodes(MultiUndoCommand *command,
- Node *select_context, const QPoint &pos)
-{
- QVector select_nodes;
-
- // Make a copy
- QVector attached = attached_items_;
-
- for (int i = 0; i < attached.size(); i++) {
- const AttachedItem &ai = attached.at(i);
-
- if (ai.node->inputs_from(select_context, true)) {
- attached.removeAt(i);
- } else if (select_context->context_contains_node(ai.node)) {
- select_nodes.append(ai.node);
- attached.removeAt(i);
- }
- }
-
- {
- MultiUndoCommand *add_command = new MultiUndoCommand();
-
- foreach (const AttachedItem &ai, attached) {
- // Add node to the same graph that the context is in
- if (ai.node->parent() != select_context->parent()) {
- add_command->add_child(
- new NodeAddCommand(select_context->parent(), ai.node));
- if (ai.node->is_item() && !ai.node->folder()) {
- add_command->add_child(new FolderAddChild(
- select_context->parent()->root(), ai.node));
- }
- }
-
- // Add node to the context
- if (ai.item) {
- select_nodes.append(ai.node);
- add_command->add_child(new NodeSetPositionCommand(
- ai.node, select_context,
- scene_.context_map()
- .value(select_context)
- ->map_scene_pos_to_node_pos_in_context(ai.item->pos())));
- }
- }
-
- if (add_command->child_count()) {
- add_command->redo_now();
- command->add_child(add_command);
- } else {
- delete add_command;
- }
- }
-
- {
- // Dropped attached item onto an edge, connect it between them
- MultiUndoCommand *drop_edge_command = new MultiUndoCommand();
- if (attached.size() == 1) {
- Node *dropping_node = nullptr;
-
- foreach (const AttachedItem &ai, attached) {
- if (ai.item && !ai.node->inputs_from(select_context, true)) {
- dropping_node = ai.node;
- break;
- }
- }
-
- if (dropping_node && drop_edge_) {
- // Remove old edge
- drop_edge_command->add_child(new NodeEdgeRemoveCommand(
- drop_edge_->output(), drop_edge_->input()));
-
- // Place new edges
- drop_edge_command->add_child(
- new NodeEdgeAddCommand(drop_edge_->output(), drop_input_));
- drop_edge_command->add_child(
- new NodeEdgeAddCommand(dropping_node, drop_edge_->input()));
- }
-
- drop_edge_ = nullptr;
- }
- if (drop_edge_command->child_count()) {
- drop_edge_command->redo_now();
- command->add_child(drop_edge_command);
- } else {
- delete drop_edge_command;
- }
- }
-
- detach_items_from_cursor(false);
-
- return select_nodes;
-}
-
-Node *NodeView::get_context_at_mouse_pos(const QPoint &p)
-{
- QList items_at_cursor = this->items(p);
- foreach (QGraphicsItem *i, items_at_cursor) {
- if (NodeViewContext *context_item =
- dynamic_cast(i)) {
- return context_item->get_context();
- }
- }
-
- return nullptr;
-}
-
-void NodeView::connect_selection_changed_signal()
-{
- connect(&scene_, &QGraphicsScene::selectionChanged, this,
- &NodeView::update_selection_cache);
-}
-
-void NodeView::disconnect_selection_changed_signal()
-{
- disconnect(&scene_, &QGraphicsScene::selectionChanged, this,
- &NodeView::update_selection_cache);
-}
-
-void NodeView::zoom_into_cursor_position(QWheelEvent *event, double multiplier,
- const QPointF &cursor_pos)
-{
- Q_UNUSED(event)
-
- double test_scale = scale_ * multiplier;
-
- if (test_scale > k_minimum_scale) {
- int anchor_x =
- qRound(double(cursor_pos.x() + horizontalScrollBar()->value()) /
- scale_ * test_scale -
- cursor_pos.x());
- int anchor_y =
- qRound(double(cursor_pos.y() + verticalScrollBar()->value()) /
- scale_ * test_scale -
- cursor_pos.y());
-
- scale(multiplier, multiplier);
-
- this->horizontalScrollBar()->setValue(anchor_x);
- this->verticalScrollBar()->setValue(anchor_y);
-
- scale_ = test_scale;
- }
-}
-
-bool NodeView::event(QEvent *event)
-{
- if (event->type() == QEvent::ShortcutOverride) {
- QKeyEvent *se = static_cast(event);
- if (se->key() == Qt::Key_Left || se->key() == Qt::Key_Right ||
- se->key() == Qt::Key_Up || se->key() == Qt::Key_Down) {
- se->accept();
- return true;
- }
- }
-
- return super::event(event);
-}
-
-bool NodeView::eventFilter(QObject *object, QEvent *event)
-{
- return super::eventFilter(object, event);
-}
-
-void NodeView::changeEvent(QEvent *e)
-{
- // Add translation code
-
- super::changeEvent(e);
-}
-
-void NodeView::zoom_from_keyboard(double multiplier)
-{
- QPoint cursor_pos = mapFromGlobal(QCursor::pos());
-
- // If the cursor is not currently within the widget, zoom into the center
- if (!rect().contains(cursor_pos)) {
- cursor_pos = QPoint(width() / 2, height() / 2);
- }
-
- zoom_into_cursor_position(nullptr, multiplier, cursor_pos);
-}
-
-void NodeView::clear_create_edge_input_if_necessary()
-{
- if (create_edge_from_output_ && create_edge_input_.is_valid()) {
- create_edge_input_.reset();
- }
-}
-
-QPointF NodeView::get_estimated_position_for_context(NodeViewItem *item,
- Node *context) const
-{
- return item->get_node_position() - context_offsets_.value(context);
-}
-
-NodeViewItem *NodeView::get_assumed_item_for_selected_node(Node *node)
-{
- // Try to find corresponding selected item
- foreach (NodeViewContext *ctx, scene_.context_map()) {
- NodeViewItem *item = ctx->get_item_from_map(node);
- if (item && item->get_node() == node && item->isSelected()) {
- // Good enough
- return item;
- }
- }
-
- return nullptr;
-}
-
-bool NodeView::get_assumed_position_for_selected_node(Node *node,
- Node::Position *pos)
-{
- if (NodeViewItem *item = get_assumed_item_for_selected_node(node)) {
- *pos = item->get_node_position_data();
- return true;
- } else {
- return false;
- }
-}
-
-Menu *NodeView::create_add_menu(Menu *parent)
-{
- Menu *add_menu = create_node_menu(parent);
- add_menu->setTitle(tr("Add"));
- connect(add_menu, &Menu::triggered, this, &NodeView::create_node_slot);
- return add_menu;
-}
-
-void NodeView::position_new_edge(const QPoint &pos)
-{
- // Determine scene coordinate
- QPointF scene_pt = mapToScene(pos);
-
- // Find if the cursor is currently inside an item
- NodeViewItem *item_at_cursor = dynamic_cast(itemAt(pos));
-
- NodeViewItem *source_item = create_edge_from_output_ ?
- create_edge_output_item_ :
- create_edge_input_item_;
- NodeViewItem *&opposing_item = create_edge_from_output_ ?
- create_edge_input_item_ :
- create_edge_output_item_;
-
- // Filter out connecting to self
- if (item_at_cursor && item_at_cursor->get_node() == source_item->get_node()) {
- item_at_cursor = nullptr;
- }
-
- // Collapse any items that the cursor is no longer inside
- int i = create_edge_expanded_items_.size() - 1;
- for (; i >= 0; i--) {
- NodeViewItem *nvi = create_edge_expanded_items_.at(i);
- QPointF local_pt = nvi->mapFromScene(scene_pt);
-
- if (nvi->scene() == &scene_ &&
- (nvi->contains(local_pt) ||
- (!nvi->is_output_item() &&
- nvi->parentItem()->contains(
- nvi->parentItem()->mapFromScene(scene_pt)) &&
- local_pt.y() > nvi->rect().bottom()))) {
- break;
- } else {
- // Collapsing an item will destroy its children, so if the cursor item happens to be a child
- // of the item we're about to collapse, set it to null
- if (item_at_cursor && item_at_cursor->parentItem() == nvi) {
- item_at_cursor = nullptr;
- }
-
- if (opposing_item && opposing_item->parentItem() == nvi) {
- opposing_item = nullptr;
- clear_create_edge_input_if_necessary();
- }
-
- collapse_item(nvi);
- }
- }
- create_edge_expanded_items_.resize(i + 1);
-
- // Expand item if possible
- if (item_at_cursor && item_at_cursor->can_be_expanded() &&
- !item_at_cursor->is_expanded() && create_edge_from_output_) {
- expand_item(item_at_cursor);
- create_edge_expanded_items_.append(item_at_cursor);
- }
-
- // Filter out connecting to a node that connects to us or an item of the same type
- if (item_at_cursor &&
- ((create_edge_from_output_ && source_item->get_node()->inputs_from(
- item_at_cursor->get_node(), true)) ||
- (!create_edge_from_output_ && item_at_cursor->get_node()->inputs_from(
- source_item->get_node(), true)) ||
- (create_edge_from_output_ == item_at_cursor->is_output_item()))) {
- item_at_cursor = nullptr;
- }
-
- // Filter out "output node" of the context, we assume users won't want to fetch the output of this
- if (item_at_cursor && !create_edge_from_output_ &&
- item_at_cursor->is_labelled_as_output_of_context()) {
- item_at_cursor = nullptr;
- }
-
- // If the item has changed
- if (item_at_cursor != opposing_item) {
- // If we had a destination active, disconnect from it since the item has changed
- if (opposing_item) {
- opposing_item->set_highlighted(false);
- opposing_item = nullptr;
- }
-
- // Clear cached input
- clear_create_edge_input_if_necessary();
-
- // If this is an input and we're
- opposing_item = item_at_cursor;
-
- if (opposing_item) {
- opposing_item->set_highlighted(true);
- if (!opposing_item->is_output_item()) {
- create_edge_input_ = opposing_item->get_input();
- }
- }
- }
-
- QPointF output_point = create_edge_output_item_ ?
- create_edge_output_item_->get_output_point() :
- scene_pt;
- QPointF input_point = create_edge_input_.is_valid() ?
- create_edge_input_item_->get_input_point() :
- scene_pt;
-
- create_edge_->set_points(output_point, input_point);
- create_edge_->set_connected(create_edge_output_item_ &&
- create_edge_input_.is_valid());
-}
-
-void NodeView::group_nodes()
-{
- // Get items
- QVector items = scene_.get_selected_items();
- if (items.isEmpty()) {
- return;
- }
-
- // Get node context
- Node *context = items.first()->get_context();
- QPointF avg_pos = items.first()->get_node_position();
- for (int i = 1; i < items.size(); i++) {
- if (items.at(i)->get_context() != context) {
- QMessageBox::critical(
- this, tr("Failed to group nodes"),
- tr("Nodes can only be grouped if they're in the same context."));
- return;
- }
-
- avg_pos += items.at(i)->get_node_position();
- }
- avg_pos /= items.size();
-
- // Create group
- NodeGroup *group = new NodeGroup();
-
- // Add group to graph and context
- MultiUndoCommand *command = new MultiUndoCommand();
-
- // Add nodes to group
- Node *output_passthrough = nullptr;
- QVector nodes_to_group = selected_nodes_;
- deselect_all();
- foreach (Node *n, nodes_to_group) {
- command->add_child(
- new NodeRemovePositionFromContextCommand(n, context));
- command->add_child(new NodeSetPositionCommand(
- n, group, context->get_node_position_data_in_context(n)));
-
- for (auto it = n->inputs().cbegin(); it != n->inputs().cend(); it++) {
- NodeInput input(n, *it, -1);
-
- if (!input.is_connected() ||
- !nodes_to_group.contains(input.get_connected_output())) {
- command->add_child(
- new NodeGroupAddInputPassthrough(group, input));
- }
- }
-
- if (!output_passthrough) {
- // Default to the first node we find that doesn't output to a node inside the group
- output_passthrough = nodes_to_group.first();
- foreach (Node *potential_in, nodes_to_group) {
- if (potential_in != n && !potential_in->inputs_from(n, false)) {
- output_passthrough = n;
- break;
- }
- }
- }
- }
-
- // Set output passthrough
- command->add_child(
- new NodeGroupSetOutputPassthrough(group, output_passthrough));
-
- // Add group to graph
- command->add_child(new NodeAddCommand(context->parent(), group));
- command->add_child(new NodeSetPositionCommand(group, context, avg_pos));
-
- // Do command
- Core::instance()->label_nodes({ group }, command);
-
- Core::instance()->undo_stack()->push(command, tr("Grouped Nodes"));
-}
-
-void NodeView::ungroup_nodes()
-{
- NodeViewItem *group_item = nullptr;
- QVector items = scene_.get_selected_items();
- if (items.isEmpty()) {
- return;
- }
-
- NodeGroup *group = nullptr;
- foreach (NodeViewItem *i, items) {
- if ((group = dynamic_cast(i->get_node()))) {
- group_item = i;
- break;
- }
- }
-
- if (!group_item) {
- return;
- }
-
- MultiUndoCommand *command = new MultiUndoCommand();
-
- Node *context = group_item->get_context();
-
- command->add_child(
- new NodeRemovePositionFromContextCommand(group, context));
- command->add_child(new NodeRemoveAndDisconnectCommand(group));
-
- for (auto it = group->get_context_positions().cbegin();
- it != group->get_context_positions().cend(); it++) {
- command->add_child(
- new NodeRemovePositionFromContextCommand(it.key(), group));
- command->add_child(new NodeSetPositionCommand(
- it.key(), context, group->get_node_position_data_in_context(it.key())));
- }
-
- Core::instance()->undo_stack()->push(command, tr("Ungrouped Nodes"));
-}
-
-void NodeView::show_node_properties()
-{
- Node *first_node = selected_nodes_.first();
-
- if (NodeGroup *group = dynamic_cast(first_node)) {
- if (!overlay_view_) {
- overlay_view_ = new NodeView(this);
- overlay_view_->show();
-
- QPushButton *overlay_close_btn = new QPushButton(overlay_view_);
- overlay_close_btn->setIcon(icon::error);
- int offset = overlay_close_btn->sizeHint().width() / 2;
- overlay_close_btn->move(offset, offset);
- overlay_close_btn->show();
-
- connect(overlay_view_, &NodeView::nodes_selected, this,
- &NodeView::nodes_selected);
- connect(overlay_view_, &NodeView::nodes_deselected, this,
- &NodeView::nodes_deselected);
- connect(overlay_view_, &NodeView::node_group_opened, this,
- &NodeView::node_group_opened);
- connect(overlay_view_, &NodeView::node_group_closed, this,
- &NodeView::node_group_closed);
- connect(overlay_view_, &NodeView::esc_pressed, this,
- &NodeView::close_overlay);
- connect(overlay_close_btn, &QPushButton::clicked, this,
- &NodeView::close_overlay);
-
- const QColor &bgcol = overlay_view_->palette().base().color();
- overlay_view_->setStyleSheet(
- QStringLiteral(
- "QGraphicsView { background: rgba(%1, %2, %3, 0.8); }")
- .arg(QString::number(bgcol.red()),
- QString::number(bgcol.green()),
- QString::number(bgcol.blue())));
-
- overlay_close_btn->setStyleSheet(
- QStringLiteral("background: transparent; border: none;"));
- }
- overlay_view_->set_contexts({ group });
- resize_overlay();
- QMetaObject::invokeMethod(overlay_view_,
- &NodeView::center_on_items_bounding_rect,
- Qt::QueuedConnection);
- overlay_view_->setFocus();
-
- emit nodes_deselected(selected_nodes_);
- emit node_selection_changed(QVector());
- emit node_selection_changed_with_contexts(QVector());
- overlay_view_->select_all();
-
- emit node_group_opened(group);
- } else {
- label_selected_nodes();
- }
-}
-
-void NodeView::show_selected_node_in_param_editor()
-{
- QVector selected = scene_.get_selected_items();
- if (selected.isEmpty()) {
- return;
- }
-
- QVector selection_with_contexts;
- selection_with_contexts.reserve(selected.size());
- foreach (NodeViewItem *item, selected) {
- if (item && item->get_node()) {
- selection_with_contexts.append(
- Node::ContextPair{ item->get_node(), item->get_context() });
- }
- }
-
- if (selection_with_contexts.isEmpty()) {
- return;
- }
-
- if (PanelManager::instance()) {
- if (PanelWidget *panel = PanelManager::instance()->get_panel_with_name(
- QStringLiteral("ParamPanel"))) {
- panel->show();
- QMetaObject::invokeMethod(panel, &PanelWidget::raise,
- Qt::QueuedConnection);
- QMetaObject::invokeMethod(
- panel,
- [panel]() {
- panel->activateWindow();
- panel->setFocus(Qt::OtherFocusReason);
- },
- Qt::QueuedConnection);
- }
- }
-
- emit node_selection_changed_with_contexts(selection_with_contexts);
-}
-
-void NodeView::label_selected_nodes()
-{
- Core::instance()->label_nodes(selected_nodes_);
-}
-
-void NodeView::item_about_to_be_deleted(NodeViewItem *item)
-{
- dragging_items_.remove(item);
-
- if (create_edge_) {
- // Item should be removed from scene, but not yet deleted, allowing a safe PositionNewEdge call
- // to disconnect
- position_new_edge(mapFromGlobal(QCursor::pos()));
-
- QGraphicsItem *test = item;
- do {
- if (test == item) {
- break;
- }
-
- test = test->parentItem();
- } while (test);
-
- if (test == item) {
- // Cancel edge function
- end_edge_drag(true);
- }
- }
-}
-
-void NodeView::close_overlay()
-{
- if (overlay_view_->overlay_view_) {
- overlay_view_->close_overlay();
- }
-
- overlay_view_->deleteLater();
- overlay_view_ = nullptr;
- emit node_group_closed();
-}
-
-void NodeView::add_context(Node *n)
-{
- NodeViewContext *ctx = scene_.add_context(n);
-
- connect(ctx, &NodeViewContext::item_about_to_be_deleted, this,
- &NodeView::item_about_to_be_deleted);
-
- connect(n, &Node::removed_from_graph, this, &NodeView::node_removed_from_graph);
-}
-
-void NodeView::remove_context(Node *n)
-{
- scene_.remove_context(n);
- disconnect(n, &Node::removed_from_graph, this,
- &NodeView::node_removed_from_graph);
-}
-
-bool NodeView::is_item_attached_to_cursor(NodeViewItem *item) const
-{
- foreach (const AttachedItem &ai, attached_items_) {
- if (ai.item == item) {
- return true;
- }
- }
-
- return false;
-}
-
-void NodeView::expand_item(NodeViewItem *item)
-{
- item->set_expanded(true);
- item->setZValue(100);
-}
-
-void NodeView::collapse_item(NodeViewItem *item)
-{
- item->set_expanded(false);
- item->setZValue(0);
-}
-
-void NodeView::end_edge_drag(bool cancel)
-{
- // Check if the edge was reconnected to the same place as before
- MultiUndoCommand *command = new MultiUndoCommand();
-
- bool reconnected_to_itself = false;
-
- if (create_edge_already_exists_) {
- if (!cancel) {
- if (create_edge_output_item_ == create_edge_->from_item() &&
- create_edge_->input() == create_edge_input_) {
- reconnected_to_itself = true;
- } else {
- // We are moving (or removing) an existing edge
- command->add_child(new NodeEdgeRemoveCommand(
- create_edge_->output(), create_edge_->input()));
- }
- }
- } else {
- // We're creating a new edge, which means this UI object is only temporary
- delete create_edge_;
- }
-
- create_edge_ = nullptr;
-
- // Clear highlight if we set one
- if (create_edge_output_item_) {
- create_edge_output_item_->set_highlighted(false);
- }
- if (create_edge_input_item_) {
- create_edge_input_item_->set_highlighted(false);
- }
-
- QString command_name;
-
- NodeInput &creating_input = create_edge_input_;
- if (create_edge_output_item_ && create_edge_input_item_ && !cancel) {
- if (creating_input.is_valid()) {
- // Make connection
- if (!reconnected_to_itself) {
- Node *creating_output = create_edge_output_item_->get_node();
-
- while (NodeGroup *output_group =
- dynamic_cast(creating_output)) {
- creating_output = output_group->get_output_passthrough();
- }
-
- while (NodeGroup *input_group =
- dynamic_cast(creating_input.node())) {
- creating_input =
- input_group->get_input_from_id(creating_input.input());
- }
-
- if (creating_input.is_connected()) {
- Node::OutputConnection existing_edge_to_remove = {
- creating_input.get_connected_output(), creating_input
- };
-
- Node *already_connected_output =
- creating_input.get_connected_output();
- NodeViewContext *ctx =
- get_context_item_from_node_item(create_edge_input_item_);
- if (ctx && !ctx->get_item_from_map(already_connected_output)) {
- if (QMessageBox::warning(
- this, QString(),
- tr("Input \"%1\" is currently connected to node \"%2\", which is not visible in this context. "
- "By connecting this, that connection will be removed. Do you wish to continue?")
- .arg(creating_input.name(),
- already_connected_output
- ->get_label_and_name()),
- QMessageBox::Yes | QMessageBox::No) ==
- QMessageBox::No) {
- cancel = true;
- }
- }
-
- if (!cancel) {
- command->add_child(new NodeEdgeRemoveCommand(
- existing_edge_to_remove.first,
- existing_edge_to_remove.second));
- }
- }
-
- if (!cancel) {
- command->add_child(new NodeEdgeAddCommand(creating_output,
- creating_input));
-
- command_name = Node::get_connect_command_string(
- creating_output, creating_input);
-
- // If the output is not in the input's context, add it now. We check the item rather than
- // the node itself, because sometimes a node may not be in the context but another node
- // representing it will be (e.g. groups)
- if (!scene_.context_map()
- .value(create_edge_input_item_->get_context())
- ->get_item_from_map(creating_output)) {
- command->add_child(new NodeSetPositionCommand(
- creating_output,
- create_edge_input_item_->get_context(),
- scene_.context_map()
- .value(create_edge_input_item_->get_context())
- ->map_scene_pos_to_node_pos_in_context(
- create_edge_output_item_->scenePos())));
- }
- }
- }
- }
- }
-
- creating_input.reset();
- create_edge_output_item_ = nullptr;
- create_edge_input_item_ = nullptr;
-
- // Collapse any items we expanded
- for (auto it = create_edge_expanded_items_.crbegin();
- it != create_edge_expanded_items_.crend(); it++) {
- collapse_item(*it);
- }
- create_edge_expanded_items_.clear();
-
- Core::instance()->undo_stack()->push(command, command_name);
-}
-
-void NodeView::post_paste(const QVector &new_nodes,
- const Node::PositionMap &map)
-{
- QVector new_attached;
-
- NodeViewItem *first_item = nullptr;
-
- for (int i = 0; i < new_nodes.size(); i++) {
- Node *node = new_nodes.at(i);
-
- // Determine if item had a position, if not don't create an item for it
- NodeViewItem *new_item;
-
- if (map.contains(node)) {
- new_item = new NodeViewItem(node, nullptr);
- new_item->set_flow_direction(scene_.get_flow_direction());
- new_item->set_node_position(map.value(node));
- scene_.addItem(new_item);
-
- if (!first_item) {
- first_item = new_item;
- }
- } else {
- new_item = nullptr;
- }
-
- new_attached.append({ new_item, node, QPointF(0, 0) });
- }
-
- // Correct positions
- if (first_item) {
- for (int i = 0; i < new_attached.size(); i++) {
- AttachedItem &ai = new_attached[i];
-
- if (ai.item) {
- ai.original_pos = ai.item->pos() - first_item->pos();
- }
- }
- }
-
- set_attached_items(new_attached);
-}
-
-void NodeView::resize_overlay()
-{
- overlay_view_->resize(this->size());
-}
-
-NodeViewContext *NodeView::get_context_item_from_node_item(NodeViewItem *item)
-{
- QGraphicsItem *i = item;
- while ((i = i->parentItem())) {
- if (NodeViewContext *nvc = dynamic_cast(i)) {
- return nvc;
- }
- }
- return nullptr;
-}
-
-void NodeView::set_attached_items(const QVector &items)
-{
- // Detach anything currently attached
- detach_items_from_cursor();
-
- attached_items_ = items;
-
- // Move to cursor
- move_attached_nodes_to_cursor(mapFromGlobal(QCursor::pos()));
-}
-
-}
diff --git a/.bak/nodeview/nodeview.h b/.bak/nodeview/nodeview.h
deleted file mode 100644
index 9298180d6..000000000
--- a/.bak/nodeview/nodeview.h
+++ /dev/null
@@ -1,301 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_NODEVIEW_H
-#define OAK_NODEVIEW_H
-
-#include
-#include
-
-#include "core.h"
-#include "node/group/group.h"
-#include "nodeviewedge.h"
-#include "nodeviewcontext.h"
-#include "nodeviewminimap.h"
-#include "nodeviewscene.h"
-#include "widget/handmovableview/handmovableview.h"
-#include "widget/menu/menu.h"
-
-namespace olive
-{
-
-/**
- * @brief A widget for viewing and editing node graphs
- *
- * This widget takes a NodeGraph object and constructs a QGraphicsScene representing its data, viewing and allowing
- * the user to make modifications to it.
- */
-class NodeView : public HandMovableView {
- Q_OBJECT
-public:
- NodeView(QWidget *parent = nullptr);
-
- virtual ~NodeView() override;
-
- void set_contexts(const QVector &nodes);
-
- const QVector &get_contexts() const
- {
- if (overlay_view_) {
- return overlay_view_->get_contexts();
- } else {
- return contexts_;
- }
- }
-
- bool is_group_overlay() const
- {
- return overlay_view_;
- }
-
- void close_contexts_belonging_to_project(Project *project);
-
- void clear_graph();
-
- /**
- * @brief Delete selected nodes from graph (user-friendly/undoable)
- */
- void delete_selected();
-
- void select_all();
- void deselect_all();
-
- void select(const QVector &nodes,
- bool center_view_on_item);
-
- void copy_selected(bool cut);
- void paste();
-
- void duplicate();
-
- void set_color_label(int index);
-
- void zoom_in();
-
- void zoom_out();
-
- const QVector &get_current_contexts() const
- {
- return contexts_;
- }
-
-public slots:
- void set_mini_map_enabled(bool e)
- {
- minimap_->setVisible(e);
- }
-
- void show_add_menu()
- {
- Menu *m = create_add_menu(nullptr);
- m->exec(QCursor::pos());
- delete m;
- }
-
- void center_on_items_bounding_rect();
-
- void center_on_node(OakEngineNode *n);
-
- void label_selected_nodes();
-
-signals:
- void nodes_selected(const QVector &nodes);
-
- void nodes_deselected(const QVector &nodes);
-
- void node_selection_changed(const QVector &nodes);
- void
- node_selection_changed_with_contexts(const QVector &nodes);
-
- void node_group_opened(NodeGroup *group);
- void node_group_closed();
-
- void esc_pressed();
-
-protected:
- virtual void keyPressEvent(QKeyEvent *event) override;
-
- virtual void mousePressEvent(QMouseEvent *event) override;
- virtual void mouseMoveEvent(QMouseEvent *event) override;
- virtual void mouseReleaseEvent(QMouseEvent *event) override;
- virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
-
- virtual void dragEnterEvent(QDragEnterEvent *event) override;
- virtual void dragMoveEvent(QDragMoveEvent *event) override;
- virtual void dropEvent(QDropEvent *event) override;
- virtual void dragLeaveEvent(QDragLeaveEvent *event) override;
-
- virtual void resizeEvent(QResizeEvent *event) override;
-
- virtual void zoom_into_cursor_position(QWheelEvent *event, double multiplier,
- const QPointF &cursor_pos) override;
-
- virtual bool event(QEvent *event) override;
-
- virtual bool eventFilter(QObject *object, QEvent *event) override;
-
- virtual void changeEvent(QEvent *e) override;
-
-private:
- void detach_items_from_cursor(bool delete_nodes_too = true);
-
- void set_flow_direction(NodeViewCommon::FlowDirection dir);
-
- void move_attached_nodes_to_cursor(const QPoint &p);
- void process_moving_attached_nodes(const QPoint &pos);
- QVector process_dropping_attached_nodes(MultiUndoCommand *command,
- Node *select_context,
- const QPoint &pos);
- Node *get_context_at_mouse_pos(const QPoint &p);
-
- void connect_selection_changed_signal();
- void disconnect_selection_changed_signal();
-
- void zoom_from_keyboard(double multiplier);
-
- void clear_create_edge_input_if_necessary();
-
- QPointF get_estimated_position_for_context(NodeViewItem *item,
- Node *context) const;
-
- NodeViewItem *get_assumed_item_for_selected_node(Node *node);
- bool get_assumed_position_for_selected_node(Node *node, Node::Position *pos);
-
- Menu *create_add_menu(Menu *parent);
-
- void position_new_edge(const QPoint &pos);
-
- void add_context(Node *n);
-
- void remove_context(Node *n);
-
- bool is_item_attached_to_cursor(NodeViewItem *item) const;
-
- void expand_item(NodeViewItem *item);
-
- void collapse_item(NodeViewItem *item);
-
- void end_edge_drag(bool cancel = false);
-
- void post_paste(const QVector &new_nodes,
- const Node::PositionMap &map);
-
- void resize_overlay();
-
- NodeViewMiniMap *minimap_;
-
- NodeViewContext *get_context_item_from_node_item(NodeViewItem *item);
-
- struct AttachedItem {
- NodeViewItem *item;
- Node *node;
- QPointF original_pos;
- };
-
- void set_attached_items(const QVector &items);
- QVector attached_items_;
-
- NodeViewEdge *drop_edge_;
- NodeInput drop_input_;
-
- NodeViewEdge *create_edge_;
- NodeViewItem *create_edge_output_item_;
- NodeViewItem *create_edge_input_item_;
- NodeInput create_edge_input_;
- bool create_edge_already_exists_;
- bool create_edge_from_output_;
-
- QVector create_edge_expanded_items_;
-
- NodeViewScene scene_;
-
- QVector selected_nodes_;
-
- QVector contexts_;
- QVector last_set_filter_nodes_;
- QMap context_offsets_;
-
- QMap dragging_items_;
-
- NodeView *overlay_view_;
-
- double scale_;
-
- bool dont_emit_selection_signals_;
-
- QAction *show_in_param_editor_action_;
-
- static const double k_minimum_scale;
-
- static const int k_maximum_contexts;
-
-private slots:
- /**
- * @brief Receiver for when the scene's selected items change
- */
- void update_selection_cache();
-
- /**
- * @brief Receiver for when the user right clicks (or otherwise requests a context menu)
- */
- void show_context_menu(const QPoint &pos);
-
- /**
- * @brief Receiver for when the user requests a new node from the add menu
- */
- void create_node_slot(QAction *action);
-
- /**
- * @brief Receiver for setting the direction from the context menu
- */
- void context_menu_set_direction(QAction *action);
-
- /**
- * @brief Opens the selected node in a Viewer
- */
- void open_selected_node_in_viewer();
-
- void update_scene_bounding_rect();
-
- void reposition_mini_map();
-
- void update_viewport_on_mini_map();
-
- void move_to_scene_point(const QPointF &pos);
-
- void node_removed_from_graph();
-
- void group_nodes();
-
- void ungroup_nodes();
-
- void show_node_properties();
-
- void show_selected_node_in_param_editor();
-
- void item_about_to_be_deleted(NodeViewItem *item);
-
- void close_overlay();
-};
-
-}
-
-#endif // OAK_NODEVIEW_H
diff --git a/.bak/nodeview/nodeviewcommon.h b/.bak/nodeview/nodeviewcommon.h
deleted file mode 100644
index c6562e5c4..000000000
--- a/.bak/nodeview/nodeviewcommon.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_NODEVIEWCOMMON_H
-#define OAK_NODEVIEWCOMMON_H
-
-#include
-
-#include "common/define.h"
-
-namespace olive
-{
-
-class NodeViewCommon {
-public:
- enum FlowDirection {
- k_invalid_direction = -1,
- k_top_to_bottom,
- k_bottom_to_top,
- k_left_to_right,
- k_right_to_left
- };
-
- static Qt::Orientation get_flow_orientation(FlowDirection dir)
- {
- if (dir == k_top_to_bottom || dir == k_bottom_to_top) {
- return Qt::Vertical;
- } else {
- return Qt::Horizontal;
- }
- }
-
- static bool is_flow_vertical(FlowDirection dir)
- {
- return dir == k_top_to_bottom || dir == k_bottom_to_top;
- }
-
- static bool is_flow_horizontal(FlowDirection dir)
- {
- return dir == k_left_to_right || dir == k_right_to_left;
- }
-
- static bool directions_are_opposing(FlowDirection a, FlowDirection b)
- {
- return ((a == NodeViewCommon::k_left_to_right &&
- b == NodeViewCommon::k_right_to_left) ||
- (a == NodeViewCommon::k_right_to_left &&
- b == NodeViewCommon::k_left_to_right) ||
- (a == NodeViewCommon::k_top_to_bottom &&
- b == NodeViewCommon::k_bottom_to_top) ||
- (a == NodeViewCommon::k_bottom_to_top &&
- b == NodeViewCommon::k_top_to_bottom));
- }
-};
-
-}
-
-#endif // OAK_NODEVIEWCOMMON_H
diff --git a/.bak/nodeview/nodeviewcontext.cpp b/.bak/nodeview/nodeviewcontext.cpp
deleted file mode 100644
index 4175a3c2a..000000000
--- a/.bak/nodeview/nodeviewcontext.cpp
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Oak Video Editor - Non-Linear Video Editor
- * Copyright (C) 2025 Olive CE 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 "nodeviewcontext.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "core.h"
-#include "node/block/block.h"
-#include "node/group/group.h"
-#include "node/output/track/track.h"
-#include "node/project.h"
-#include "node/project/sequence/sequence.h"
-#include "nodeviewitem.h"
-#include "ui/colorcoding.h"
-
-namespace olive
-{
-
-#define super QGraphicsRectItem
-
-NodeViewContext::NodeViewContext(Node *context, QGraphicsItem *item)
- : super(item)
- , context_(context)
-{
- Block *block = dynamic_cast(context_);
- if (block && block->track() && block->track()->sequence()) {
- Rational timebase = block->track()
- ->sequence()
- ->get_video_params()
- .frame_rate_as_time_base();
- lbl_ =
- QCoreApplication::translate("NodeViewContext", "%1 [%2] :: %3 - %4")
- .arg(block->get_label_and_name(),
- Track::Reference::type_to_translated_string(
- block->track()->type()),
- QString::fromStdString(Timecode::time_to_timecode(
- block->in(), timebase,
- Core::instance()->get_timecode_display())),
- QString::fromStdString(Timecode::time_to_timecode(
- block->out(), timebase,
- Core::instance()->get_timecode_display())));
- } else {
- lbl_ = context_->get_label_and_name();
- }
-
- const Node::PositionMap &map = context_->get_context_positions();
- for (auto it = map.cbegin(); it != map.cend(); it++) {
- add_child(it.key());
- }
-
- connect(context_, &Node::node_added_to_context, this,
- &NodeViewContext::add_child, Qt::DirectConnection);
- connect(context_, &Node::node_position_in_context_changed, this,
- &NodeViewContext::set_child_position, Qt::DirectConnection);
- connect(context_, &Node::node_removed_from_context, this,
- &NodeViewContext::remove_child, Qt::DirectConnection);
-}
-
-NodeViewContext::~NodeViewContext()
-{
- // Delete edges before items, because the edge constructor references the items
- qDeleteAll(edges_);
- edges_.clear();
-}
-
-void NodeViewContext::add_child(Node *node)
-{
- if (!context_) {
- return;
- }
-
- NodeViewItem *item = new NodeViewItem(node, context_, this);
- item->set_flow_direction(flow_dir_);
-
- add_node_internal(node, item);
-
- if (NodeGroup *group = dynamic_cast(node)) {
- for (auto it = group->get_context_positions().cbegin();
- it != group->get_context_positions().cend(); it++) {
- // Use this item as the representative for all of these nodes too
- add_node_internal(it.key(), item);
- }
-
- connect(group, &NodeGroup::node_added_to_context, this,
- &NodeViewContext::group_added_node);
- connect(group, &NodeGroup::node_removed_from_context, this,
- &NodeViewContext::group_removed_node);
- }
-
- update_rect();
-}
-
-void NodeViewContext::set_child_position(Node *node, const QPointF &pos)
-{
- item_map_.value(node)->set_node_position(pos);
-}
-
-void NodeViewContext::remove_child(Node *node)
-{
- disconnect(node, &Node::input_connected, this,
- &NodeViewContext::child_input_connected);
- disconnect(node, &Node::input_disconnected, this,
- &NodeViewContext::child_input_disconnected);
-
- if (NodeGroup *group = dynamic_cast(node)) {
- disconnect(group, &NodeGroup::node_added_to_context, this,
- &NodeViewContext::group_added_node);
- disconnect(group, &NodeGroup::node_removed_from_context, this,
- &NodeViewContext::group_removed_node);
- }
-
- NodeViewItem *item = item_map_.take(node);
-
- // Remove from scene before emitting signal so that any drag functions that might be happening
- // now can be handled before the item is destroyed
- scene()->removeItem(item);
-
- emit item_about_to_be_deleted(item);
-
- // Delete edges first because the edge destructor will try to reference item (maybe that should
- // be changed...)
- QVector edges_to_remove = item->get_all_edges_recursively();
- foreach (NodeViewEdge *edge, edges_to_remove) {
- if (node == item->get_node() || edge->output() == node ||
- edge->input().node() == node) {
- child_input_disconnected(edge->output(), edge->input());
- }
- }
-
- // Check if this item is specifically for this node and the node is a group. If so, remove it for
- // all other entries in the map.
- if (item->get_node() == node) {
- if (dynamic_cast(item->get_node())) {
- for (auto it = item_map_.begin(); it != item_map_.end();) {
- if (it.value() == item) {
- it = item_map_.erase(it);
- } else {
- it++;
- }
- }
- }
-
- delete item;
- }
-
- update_rect();
-}
-
-void NodeViewContext::child_input_connected(Node *output, const NodeInput &input)
-{
- // Add edge
- if (!input.is_hidden()) {
- if (NodeViewItem *output_item = item_map_.value(output)) {
- add_edge_internal(
- output, input, output_item,
- item_map_.value(input.node())->get_item_for_input(input));
- }
- }
-}
-
-bool NodeViewContext::child_input_disconnected(Node *output,
- const NodeInput &input)
-{
- // Remove edge
- for (int i = 0; i < edges_.size(); i++) {
- NodeViewEdge *e = edges_.at(i);
- if (e->output() == output && e->input() == input) {
- delete e;
- edges_.removeAt(i);
- return true;
- }
- }
-
- return false;
-}
-
-qreal get_text_offset(const QFontMetricsF &fm)
-{
- return fm.height() / 2;
-}
-
-void NodeViewContext::update_rect()
-{
- QFont f;
- QFontMetricsF fm(f);
- qreal lbl_offset = get_text_offset(fm);
-
- QRectF cbr = childrenBoundingRect();
- QRectF rect = cbr;
- int pad = NodeViewItem::default_item_height();
- rect.adjust(-pad, -lbl_offset * 2 - fm.height() - pad, pad, pad);
- setRect(rect);
-
- last_titlebar_height_ = rect.y() + (cbr.y() - rect.y()) - pad;
-}
-
-void NodeViewContext::set_flow_direction(NodeViewCommon::FlowDirection dir)
-{
- flow_dir_ = dir;
-
- foreach (NodeViewItem *item, item_map_) {
- item->set_flow_direction(dir);
- }
-}
-
-void NodeViewContext::set_curved_edges(bool e)
-{
- curved_edges_ = e;
-
- foreach (NodeViewEdge *edge, edges_) {
- edge->set_curved(e);
- }
-}
-
-int NodeViewContext::delete_selected(NodeViewDeleteCommand *command)
-{
- int count = 0;
-
- // Delete any selected edges
- foreach (NodeViewEdge *edge, edges_) {
- if (edge->isSelected()) {
- command->add_edge(edge->output(), edge->input());
- }
- }
-
- // Delete any selected nodes
- foreach (NodeViewItem *node, item_map_) {
- if (node->isSelected()) {
- command->add_node(node->get_node(), context_);
- count++;
- }
- }
-
- return count;
-}
-
-void NodeViewContext::select(const QVector &nodes)
-{
- foreach (Node *n, nodes) {
- if (NodeViewItem *item = item_map_.value(n)) {
- item->setSelected(true);
- }
- }
-}
-
-QVector NodeViewContext::get_selected_items() const
-{
- QVector items;
-
- for (auto it = item_map_.cbegin(); it != item_map_.cend(); it++) {
- if (it.value()->isSelected()) {
- if (!items.contains(it.value())) {
- items.append(it.value());
- }
- }
- }
-
- return items;
-}
-
-QPointF NodeViewContext::map_scene_pos_to_node_pos_in_context(const QPointF &pos) const
-{
- for (auto it = item_map_.cbegin(); it != item_map_.cend(); it++) {
- QPointF pos_inside_parent =
- it.value()->mapToParent(it.value()->mapFromScene(pos));
- return NodeViewItem::screen_to_node_point(pos_inside_parent, flow_dir_);
- }
- return QPointF(0, 0);
-}
-
-void NodeViewContext::paint(QPainter *painter,
- const QStyleOptionGraphicsItem *option,
- QWidget *widget)
-{
- // Set pen and brush
- Color color = context_->color();
- QColor c = QtUtils::to_q_color(color);
- QPen pen(c, 2);
- if (option->state & QStyle::State_Selected) {
- pen.setStyle(Qt::DotLine);
- }
- painter->setPen(pen);
-
- QColor bg = c;
- bg.setAlpha(128);
- painter->setBrush(bg);
-
- // Draw semi-transparent rect for whole item
- int rounded = painter->fontMetrics().height();
- painter->drawRoundedRect(rect(), rounded, rounded);
-
- // Draw solid background for titlebar
- QRectF titlebar_rect = rect();
- titlebar_rect.setHeight(last_titlebar_height_ - rect().top());
- painter->setClipRect(titlebar_rect);
- painter->setBrush(c);
- painter->drawRoundedRect(rect(), rounded, rounded);
- painter->setClipping(false);
-
- // Draw titlebar text
- painter->setPen(ColorCoding::get_ui_selector_color(color));
-
- int offset = get_text_offset(painter->fontMetrics());
-
- QRectF text_rect = rect();
- text_rect.adjust(offset, offset, -offset, -offset);
- painter->drawText(text_rect, lbl_);
-}
-
-QVariant NodeViewContext::itemChange(GraphicsItemChange change,
- const QVariant &value)
-{
- return super::itemChange(change, value);
-}
-
-void NodeViewContext::mousePressEvent(QGraphicsSceneMouseEvent *event)
-{
- bool clicked_inside_titlebar = (event->pos().y() < last_titlebar_height_);
-
- setFlag(ItemIsMovable, clicked_inside_titlebar);
- setFlag(ItemIsSelectable, clicked_inside_titlebar);
-
- super::mousePressEvent(event);
-}
-
-void NodeViewContext::add_node_internal(Node *node, NodeViewItem *item)
-{
- connect(node, &Node::input_connected, this,
- &NodeViewContext::child_input_connected);
- connect(node, &Node::input_disconnected, this,
- &NodeViewContext::child_input_disconnected);
-
- item_map_.insert(node, item);
-
- if (node == context_) {
- item->set_label_as_output(true);
- }
-
- for (auto it = node->output_connections().cbegin();
- it != node->output_connections().cend(); it++) {
- if (!it->second.is_hidden()) {
- if (NodeViewItem *other_item = item_map_.value(it->second.node())) {
- add_edge_internal(node, it->second, item,
- other_item->get_item_for_input(it->second));
- }
- }
- }
-
- for (auto it = node->input_connections().cbegin();
- it != node->input_connections().cend(); it++) {
- if (!it->first.is_hidden()) {
- if (NodeViewItem *other_item = item_map_.value(it->second)) {
- add_edge_internal(it->second, it->first, other_item,
- item->get_item_for_input(it->first));
- }
- }
- }
-}
-
-void NodeViewContext::add_edge_internal(Node *output, const NodeInput &input,
- NodeViewItem *from, NodeViewItem *to)
-{
- if (from == to) {
- return;
- }
-
- NodeViewEdge *edge_ui = new NodeViewEdge(output, input, from, to, this);
-
- edge_ui->adjust();
- edge_ui->set_curved(curved_edges_);
-
- edges_.append(edge_ui);
-}
-
-void NodeViewContext::group_added_node(Node *node)
-{
- NodeGroup *group = static_cast(sender());
-
- add_node_internal(node, item_map_.value(group));
-}
-
-void NodeViewContext::group_removed_node(Node *node)
-{
- NodeGroup *group = static_cast(sender());
-
- if (item_map_.value(node) == item_map_.value(group)) {
- item_map_.remove(node);
- }
-}
-
-}
diff --git a/.bak/nodeview/nodeviewcontext.h b/.bak/nodeview/nodeviewcontext.h
deleted file mode 100644
index f62ef6ff4..000000000
--- a/.bak/nodeview/nodeviewcontext.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Oak Video Editor - Non-Linear Video Editor
- * Copyright (C) 2025 Olive CE 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 OAK_NODEVIEWCONTEXT_H
-#define OAK_NODEVIEWCONTEXT_H
-
-#include
-#include
-
-#include "node/node.h"
-#include "node/nodeundo.h"
-#include "nodeviewcommon.h"
-#include "nodeviewedge.h"
-
-namespace olive
-{
-
-class NodeViewContext : public QObject, public QGraphicsRectItem {
- Q_OBJECT
-public:
- NodeViewContext(Node *context, QGraphicsItem *item = nullptr);
-
- virtual ~NodeViewContext() override;
-
- Node *get_context() const
- {
- return context_;
- }
-
- void update_rect();
-
- void set_flow_direction(NodeViewCommon::FlowDirection dir);
-
- void set_curved_edges(bool e);
-
- int delete_selected(NodeViewDeleteCommand *command);
-
- void select(const QVector &nodes);
-
- QVector get_selected_items() const;
-
- QPointF map_scene_pos_to_node_pos_in_context(const QPointF &pos) const;
-
- NodeViewItem *get_item_from_map(Node *node) const
- {
- return item_map_.value(node);
- }
-
- virtual void paint(QPainter *painter,
- const QStyleOptionGraphicsItem *option,
- QWidget *widget = nullptr) override;
-
-public slots:
- void add_child(Node *node);
-
- void set_child_position(Node *node, const QPointF &pos);
-
- void remove_child(Node *node);
-
- void child_input_connected(Node *output, const NodeInput &input);
-
- bool child_input_disconnected(Node *output, const NodeInput &input);
-
-signals:
- void item_about_to_be_deleted(NodeViewItem *item);
-
-protected:
- virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange change,
- const QVariant &value) override;
-
- virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
-
-private:
- void add_node_internal(Node *node, NodeViewItem *item);
-
- void add_edge_internal(Node *output, const NodeInput &input,
- NodeViewItem *from, NodeViewItem *to);
-
- Node *context_;
-
- QString lbl_;
-
- NodeViewCommon::FlowDirection flow_dir_;
-
- bool curved_edges_;
-
- int last_titlebar_height_;
-
- QMap item_map_;
-
- QVector edges_;
-
-private slots:
- void group_added_node(Node *node);
-
- void group_removed_node(Node *node);
-};
-
-}
-
-#endif // OAK_NODEVIEWCONTEXT_H
diff --git a/.bak/nodeview/nodeviewedge.cpp b/.bak/nodeview/nodeviewedge.cpp
deleted file mode 100644
index 7625ef94a..000000000
--- a/.bak/nodeview/nodeviewedge.cpp
+++ /dev/null
@@ -1,260 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "nodeviewedge.h"
-
-#include
-#include
-#include
-#include
-
-#include "common/lerp.h"
-#include "nodeview.h"
-#include "nodeviewitem.h"
-#include "nodeviewscene.h"
-
-namespace olive
-{
-
-#define super QGraphicsPathItem
-
-NodeViewEdge::NodeViewEdge(Node *output, const NodeInput &input,
- NodeViewItem *from_item, NodeViewItem *to_item,
- QGraphicsItem *parent)
- : super(parent)
- , output_(output)
- , input_(input)
- , from_item_(from_item)
- , to_item_(to_item)
-{
- init();
- set_connected(true);
-
- from_item_->add_edge(this);
- to_item_->add_edge(this);
-}
-
-NodeViewEdge::NodeViewEdge(QGraphicsItem *parent)
- : QGraphicsPathItem(parent)
- , from_item_(nullptr)
- , to_item_(nullptr)
-{
- init();
-}
-
-NodeViewEdge::~NodeViewEdge()
-{
- if (from_item_) {
- from_item_->remove_edge(this);
- }
-
- if (to_item_) {
- to_item_->remove_edge(this);
- }
-}
-
-void NodeViewEdge::set_from_item(NodeViewItem *i)
-{
- if (from_item_) {
- from_item_->remove_edge(this);
- }
-
- from_item_ = i;
-
- if (from_item_) {
- from_item_->add_edge(this);
- }
-
- adjust();
-}
-
-void NodeViewEdge::set_to_item(NodeViewItem *i)
-{
- if (to_item_) {
- to_item_->remove_edge(this);
- }
-
- to_item_ = i;
-
- if (to_item_) {
- to_item_->add_edge(this);
- }
-
- adjust();
-}
-
-void NodeViewEdge::adjust()
-{
- // Draw a line between the two
- set_points(from_item()->get_output_point(), to_item()->get_input_point());
-}
-
-void NodeViewEdge::set_connected(bool c)
-{
- connected_ = c;
-
- update();
-}
-
-void NodeViewEdge::set_highlighted(bool e)
-{
- highlighted_ = e;
-
- update();
-}
-
-void NodeViewEdge::set_points(const QPointF &start, const QPointF &end)
-{
- cached_start_ = start;
- cached_end_ = end;
-
- update_curve();
-}
-
-void NodeViewEdge::set_curved(bool e)
-{
- curved_ = e;
-
- update_curve();
-}
-
-void NodeViewEdge::paint(QPainter *painter,
- const QStyleOptionGraphicsItem *option, QWidget *)
-{
- QPalette::ColorGroup group;
- QPalette::ColorRole role;
-
- if (connected_) {
- group = QPalette::Active;
- } else {
- group = QPalette::Disabled;
- }
-
- if (highlighted_ != bool(option->state & QStyle::State_Selected)) {
- role = QPalette::Highlight;
- } else {
- role = QPalette::Text;
- }
-
- // Draw main path
- QColor edge_color = qApp->palette().color(group, role);
-
- painter->setPen(QPen(edge_color, edge_width_));
- painter->setBrush(Qt::NoBrush);
- painter->drawPath(path());
-}
-
-void NodeViewEdge::init()
-{
- connected_ = false;
- highlighted_ = false;
- curved_ = true;
-
- setFlag(QGraphicsItem::ItemIsSelectable);
-
- // Ensures this UI object is drawn behind other objects
- setZValue(-1);
-
- // Use font metrics to set edge width for basic high DPI support
- edge_width_ = QFontMetrics(QFont()).height() / 12;
-}
-
-void NodeViewEdge::update_curve()
-{
- const QPointF &start = cached_start_;
- const QPointF &end = cached_end_;
-
- QPainterPath path;
- path.moveTo(start);
-
- double angle = std::atan2(end.y() - start.y(), end.x() - start.x());
-
- if (curved_) {
- double half_x = lerp(start.x(), end.x(), 0.5);
- double half_y = lerp(start.y(), end.y(), 0.5);
-
- QPointF cp1, cp2;
-
- NodeViewCommon::FlowDirection from_flow =
- from_item_ ? from_item_->get_flow_direction() :
- NodeViewCommon::k_invalid_direction;
- NodeViewCommon::FlowDirection to_flow =
- to_item_ ? to_item_->get_flow_direction() :
- NodeViewCommon::k_invalid_direction;
-
- if (from_flow == NodeViewCommon::k_invalid_direction &&
- to_flow == NodeViewCommon::k_invalid_direction) {
- // This is a technically unsupported scenario, but to avoid issues, we'll use a fallback
- from_flow = NodeViewCommon::k_left_to_right;
- to_flow = NodeViewCommon::k_left_to_right;
- } else if (from_flow == NodeViewCommon::k_invalid_direction) {
- from_flow = to_flow;
- } else if (to_flow == NodeViewCommon::k_invalid_direction) {
- to_flow = from_flow;
- }
-
- if (NodeViewCommon::get_flow_orientation(from_flow) == Qt::Horizontal) {
- cp1 = QPointF(half_x, start.y());
- } else {
- cp1 = QPointF(start.x(), half_y);
- }
-
- if (NodeViewCommon::get_flow_orientation(to_flow) == Qt::Horizontal) {
- cp2 = QPointF(half_x, end.y());
- } else {
- cp2 = QPointF(end.x(), half_y);
- }
-
- path.cubicTo(cp1, cp2, end);
-
- if (!qFuzzyCompare(start.x(), end.x())) {
- double continue_x = end.x() - std::cos(angle);
-
- double x1 = start.x();
- double x2 = cp1.x();
- double x3 = cp2.x();
- double x4 = end.x();
- double y1 = start.y();
- double y2 = cp1.y();
- double y3 = cp2.y();
- double y4 = end.y();
-
- if (start.x() >= end.x()) {
- std::swap(x1, x4);
- std::swap(x2, x3);
- std::swap(y1, y4);
- std::swap(y2, y3);
- }
-
- double t = Bezier::cubic_xto_t(continue_x, x1, x2, x3, x4);
- double y = Bezier::cubic_tto_y(y1, y2, y3, y4, t);
-
- angle = std::atan2(end.y() - y, end.x() - continue_x);
- }
-
- } else {
- path.lineTo(end);
- }
-
- setPath(mapFromScene(path));
-}
-
-}
diff --git a/.bak/nodeview/nodeviewedge.h b/.bak/nodeview/nodeviewedge.h
deleted file mode 100644
index db33ab60d..000000000
--- a/.bak/nodeview/nodeviewedge.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_NODEEDGEITEM_H
-#define OAK_NODEEDGEITEM_H
-
-#include
-#include
-
-#include "nodeviewcommon.h"
-#include "node/node.h"
-
-namespace olive
-{
-
-class NodeViewItem;
-
-/**
- * @brief A graphical representation of a NodeEdge to be used in NodeView
- *
- * A fairly simple line widget use to visualize a connection between two node parameters (a NodeEdge).
- */
-class NodeViewEdge : public QGraphicsPathItem {
-public:
- NodeViewEdge(Node *output, const NodeInput &input, NodeViewItem *from_item,
- NodeViewItem *to_item, QGraphicsItem *parent = nullptr);
-
- NodeViewEdge(QGraphicsItem *parent = nullptr);
-
- virtual ~NodeViewEdge() override;
-
- Node *output() const
- {
- return output_;
- }
-
- const NodeInput &input() const
- {
- return input_;
- }
-
- int element() const
- {
- return element_;
- }
-
- NodeViewItem *from_item() const
- {
- return from_item_;
- }
-
- NodeViewItem *to_item() const
- {
- return to_item_;
- }
-
- void set_from_item(NodeViewItem *i);
-
- void set_to_item(NodeViewItem *i);
-
- void adjust();
-
- /**
- * @brief Set the connected state of this line
- *
- * When the edge is not connected, it visually depicts this by coloring the line grey. When an edge is connected or
- * a potential connection is valid, the line is colored white. This function sets whether the line should be grey
- * (false) or white (true).
- *
- * Using SetEdge() automatically sets this to true. Under most circumstances this should be left alone, and only
- * be set when an edge is being created/dragged.
- */
- void set_connected(bool c);
-
- bool is_connected() const
- {
- return connected_;
- }
-
- /**
- * @brief Set highlighted state
- *
- * Changes color of edge.
- */
- void set_highlighted(bool e);
-
- /**
- * @brief Set points to create curve from
- */
- void set_points(const QPointF &start, const QPointF &end);
-
- /**
- * @brief Set whether edges should be drawn as curved or as straight lines
- */
- void set_curved(bool e);
-
-protected:
- virtual void paint(QPainter *painter,
- const QStyleOptionGraphicsItem *option,
- QWidget *widget = nullptr) override;
-
-private:
- void init();
-
- void update_curve();
-
- Node *output_;
-
- NodeInput input_;
-
- int element_;
-
- NodeViewItem *from_item_;
-
- NodeViewItem *to_item_;
-
- int edge_width_;
-
- bool connected_;
-
- bool highlighted_;
-
- bool curved_;
-
- QPointF cached_start_;
- QPointF cached_end_;
-};
-
-}
-
-#endif // OAK_NODEEDGEITEM_H
diff --git a/.bak/nodeview/nodeviewitem.cpp b/.bak/nodeview/nodeviewitem.cpp
deleted file mode 100644
index 320e0469b..000000000
--- a/.bak/nodeview/nodeviewitem.cpp
+++ /dev/null
@@ -1,907 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "nodeviewitem.h"
-
-#include
-#include
-#include
-#include
-#include
-
-#include "common/qtutils.h"
-#include "config/config.h"
-#include "core.h"
-#include "node/nodeundo.h"
-#include "node/value.h"
-#include "pluginSupport/oliveplugininstance.h"
-#include "nodeview.h"
-#include "nodeviewscene.h"
-#include "ui/colorcoding.h"
-#include "ui/icons/icons.h"
-#include "window/mainwindow/mainwindow.h"
-
-namespace olive
-{
-
-NodeViewItem::NodeViewItem(Node *node, const QString &input, int element,
- Node *context, QGraphicsItem *parent)
- : QGraphicsRectItem(parent)
- , node_(node)
- , input_(input)
- , element_(element)
- , context_(context)
- , expanded_(false)
- , highlighted_(false)
- , flow_dir_(NodeViewCommon::k_invalid_direction)
- , arrow_click_(false)
- , label_as_output_(false)
-{
- //
- // We use font metrics to set all the UI measurements for DPI-awareness
- //
-
- // Set border width
- node_border_width_ = default_item_border();
-
- // Set rect size to default
- set_rect_size();
-
- // Create connector
- input_connector_ = new NodeViewItemConnector(false, this);
- output_connector_ = new NodeViewItemConnector(true, this);
-
- connect(node_, &Node::label_changed, this,
- &NodeViewItem::node_appearance_changed);
- connect(node_, &Node::color_changed, this,
- &NodeViewItem::node_appearance_changed);
- connect(node_, &Node::message_count_changed, this,
- &NodeViewItem::node_appearance_changed);
-
- if (is_output_item()) {
- connect(node_, &Node::input_added, this,
- &NodeViewItem::repopulate_inputs);
- connect(node_, &Node::input_removed, this,
- &NodeViewItem::repopulate_inputs);
- repopulate_inputs();
-
- // Set flags for this widget
- setFlag(QGraphicsItem::ItemSendsGeometryChanges);
- setFlag(QGraphicsItem::ItemIsMovable);
- setFlag(QGraphicsItem::ItemIsSelectable);
-
- if (context_) {
- set_node_position(context_->get_node_position_data_in_context(node_));
- }
- } else {
- output_connector_->setVisible(false);
-
- connect(node_, &Node::input_array_size_changed, this,
- &NodeViewItem::input_array_size_changed);
- connect(node_, &Node::input_array_size_changed, this,
- &NodeViewItem::input_array_size_changed);
- }
-
- // This should be set during runtime, but just in case here's a default fallback
- set_flow_direction(NodeViewCommon::k_left_to_right);
-}
-
-NodeViewItem::~NodeViewItem()
-{
- Q_ASSERT(edges_.isEmpty());
-}
-
-Node::Position NodeViewItem::get_node_position_data() const
-{
- return Node::Position(get_node_position(), is_expanded());
-}
-
-QPointF NodeViewItem::get_node_position() const
-{
- return screen_to_node_point(pos(), flow_dir_);
-}
-
-void NodeViewItem::set_node_position(const QPointF &pos)
-{
- cached_node_pos_ = pos;
-
- update_node_position();
-}
-
-void NodeViewItem::set_node_position(const Node::Position &pos)
-{
- set_node_position(pos.position);
- set_expanded(pos.expanded);
-}
-
-QVector NodeViewItem::get_all_edges_recursively() const
-{
- QVector list = edges_;
-
- foreach (NodeViewItem *item, children_) {
- list.append(item->get_all_edges_recursively());
- }
-
- return list;
-}
-
-int NodeViewItem::default_text_padding()
-{
- return QFontMetrics(QFont()).height() / 4;
-}
-
-int NodeViewItem::default_item_height()
-{
- return QFontMetrics(QFont()).height() + default_text_padding() * 2;
-}
-
-int NodeViewItem::default_item_width()
-{
- return QtUtils::q_font_metrics_width(QFontMetrics(QFont()),
- "HHHHHHHHHHHHHHHH");
- ;
-}
-
-int NodeViewItem::default_item_border()
-{
- return QFontMetrics(QFont()).height() / 12;
-}
-
-QPointF NodeViewItem::node_to_screen_point(QPointF p,
- NodeViewCommon::FlowDirection direction)
-{
- switch (direction) {
- case NodeViewCommon::k_left_to_right:
- // NodeGraphs are always left-to-right internally, no need to translate
- break;
- case NodeViewCommon::k_right_to_left:
- // Invert X value
- p.setX(-p.x());
- break;
- case NodeViewCommon::k_top_to_bottom:
- // Swap X/Y
- p = QPointF(p.y(), p.x());
- break;
- case NodeViewCommon::k_bottom_to_top:
- // Swap X/Y and invert Y
- p = QPointF(p.y(), -p.x());
- break;
- case NodeViewCommon::k_invalid_direction:
- break;
- }
-
- // Multiply by item sizes for this direction
- p.setX(p.x() * default_item_horizontal_padding(direction));
- p.setY(p.y() * default_item_vertical_padding(direction));
-
- return p;
-}
-
-QPointF NodeViewItem::screen_to_node_point(QPointF p,
- NodeViewCommon::FlowDirection direction)
-{
- // Divide by item sizes for this direction
- p.setX(p.x() / default_item_horizontal_padding(direction));
- p.setY(p.y() / default_item_vertical_padding(direction));
-
- switch (direction) {
- case NodeViewCommon::k_left_to_right:
- // NodeGraphs are always left-to-right internally, no need to translate
- break;
- case NodeViewCommon::k_right_to_left:
- // Invert X value
- p.setX(-p.x());
- break;
- case NodeViewCommon::k_top_to_bottom:
- // Swap X/Y
- p = QPointF(p.y(), p.x());
- break;
- case NodeViewCommon::k_bottom_to_top:
- // Swap X/Y and invert Y
- p = QPointF(-p.y(), p.x());
- break;
- case NodeViewCommon::k_invalid_direction:
- break;
- }
-
- return p;
-}
-
-qreal NodeViewItem::default_item_horizontal_padding(
- NodeViewCommon::FlowDirection dir)
-{
- if (NodeViewCommon::get_flow_orientation(dir) == Qt::Horizontal) {
- return default_item_width() * 1.5;
- } else {
- return default_item_width() * 1.25;
- }
-}
-
-qreal NodeViewItem::default_item_vertical_padding(NodeViewCommon::FlowDirection dir)
-{
- if (NodeViewCommon::get_flow_orientation(dir) == Qt::Horizontal) {
- return default_item_height() * 1.5;
- } else {
- return default_item_height() * 2.0;
- }
-}
-
-qreal NodeViewItem::default_item_horizontal_padding() const
-{
- return default_item_horizontal_padding(flow_dir_);
-}
-
-qreal NodeViewItem::default_item_vertical_padding() const
-{
- return default_item_vertical_padding(flow_dir_);
-}
-
-void NodeViewItem::add_edge(NodeViewEdge *edge)
-{
- edges_.append(edge);
-}
-
-void NodeViewItem::remove_edge(NodeViewEdge *edge)
-{
- edges_.removeOne(edge);
-}
-
-void NodeViewItem::set_expanded(bool e, bool hide_titlebar)
-{
- if (!can_be_expanded() || (expanded_ == e)) {
- return;
- }
-
- expanded_ = e;
-
- if (context_) {
- context_->set_node_expanded_in_context(node_, e);
- }
-
- if (is_output_item()) {
- // We don't have to check has_connectable_inputs_ here because we did it at the top
- input_connector_->setVisible(!expanded_);
- }
-
- if (expanded_) {
- node_->retranslate();
-
- if (is_output_item()) {
- // Create items for each input of the node
- foreach (const QString &input, node_->inputs()) {
- if (is_input_valid(input)) {
- NodeViewItem *item =
- new NodeViewItem(node_, input, -1, context_, this);
- children_.append(item);
- }
- }
-
- QVector edges = edges_;
- for (auto it = edges.cbegin(); it != edges.cend(); it++) {
- if ((*it)->to_item() == this) {
- (*it)->set_to_item(get_item_for_input((*it)->input()));
- }
- }
- } else {
- // Create items for each element of the input array
- int arr_sz = node_->input_array_size(input_);
- children_.resize(arr_sz);
- for (int i = 0; i < arr_sz; i++) {
- NodeViewItem *item =
- new NodeViewItem(node_, input_, i, context_, this);
- children_[i] = item;
- }
-
- QVector edges = edges_;
- for (auto it = edges.cbegin(); it != edges.cend(); it++) {
- if ((*it)->to_item() == this) {
- (*it)->set_to_item(get_item_for_input((*it)->input()));
- }
- }
- }
- } else {
- foreach (NodeViewItem *child, children_) {
- QVector child_edges = child->edges();
- foreach (NodeViewEdge *edge, child_edges) {
- edge->set_to_item(this);
- }
- delete child;
- }
- children_.clear();
- }
-
- update_children_positions();
-
- if (flow_dir_ == NodeViewCommon::k_top_to_bottom) {
- update_output_connector_position();
- }
-
- readjust_all_edges();
-
- update_context_rect();
-
- update();
-}
-
-void NodeViewItem::toggle_expanded()
-{
- set_expanded(!is_expanded());
-}
-
-void NodeViewItem::paint(QPainter *painter,
- const QStyleOptionGraphicsItem *option, QWidget *)
-{
- // Use main window palette since the palette passed in `widget` is the NodeView palette which
- // has been slightly modified
- QPalette app_pal = Core::instance()->main_window()->palette();
-
- // We only draw a single unit's worth
- QRectF single_unit_rect = rect();
- single_unit_rect.setHeight(default_item_height());
-
- if (is_output_item()) {
- // Set output item colors
- painter->setPen(Qt::black);
- painter->setBrush(
- node_->brush(single_unit_rect.top(), single_unit_rect.bottom()));
- } else {
- // Set input item colors
- painter->setPen(Qt::NoPen);
- painter->setBrush(element_ == -1 ? app_pal.color(QPalette::Window) :
- app_pal.color(QPalette::Base));
- }
-
- painter->drawRect(single_unit_rect);
-
- // Draw highlight if applicable
- if (highlighted_) {
- QColor highlight_col = app_pal.color(QPalette::Text);
- highlight_col.setAlpha(64);
- painter->setBrush(highlight_col);
- painter->drawRect(rect());
- }
-
- // Determine what text to draw and whether to draw an arrow
- QString node_label, node_name;
-
- if (is_output_item()) {
- if (label_as_output_) {
- node_name = QCoreApplication::translate("NodeViewItem", "Output");
- } else {
- node_label = node_->get_label();
- node_name = node_->short_name();
- }
- } else {
- if (element_ == -1) {
- node_name = node_->get_input_name(input_);
- } else {
- node_name = QString::number(
- element_ +
- node_->get_input_property(input_, QStringLiteral("arraystart"))
- .toInt());
- }
- }
-
- // Draw arrow if necessary
- int arrow_size = can_be_expanded() ? draw_expand_arrow(painter) : 0;
-
- if (is_output_item()) {
- // Determine the text color (automatically calculate from node background color)
- painter->setPen(ColorCoding::get_ui_selector_color(node_->color()));
- } else {
- // Just use text item
- painter->setPen(app_pal.text().color());
- }
-
- if (node_label.isEmpty()) {
- // Draw name only
- draw_node_title(painter, node_name, single_unit_rect, Qt::AlignVCenter,
- arrow_size);
- } else {
- int text_pad = default_text_padding() / 2;
- QRectF safe_label_bounds =
- single_unit_rect.adjusted(text_pad, text_pad, -text_pad, -text_pad);
- QFont f;
- qreal font_sz = f.pointSizeF();
-
- // Draw label as larger/upper text
- f.setPointSizeF(font_sz * 0.8);
- painter->setFont(f);
- draw_node_title(painter, node_label, safe_label_bounds, Qt::AlignTop,
- arrow_size);
-
- // Draw node name as smaller/lower text
- f.setPointSizeF(font_sz * 0.6);
- painter->setFont(f);
- draw_node_title(painter, node_name, safe_label_bounds, Qt::AlignBottom,
- arrow_size);
- }
-
- if (is_output_item()) {
- auto *instance = node_->getPluginInstance();
- auto *olive_instance =
- dynamic_cast(instance);
- int message_count =
- olive_instance ? olive_instance->persistent_message_count() : 0;
-
- if (message_count > 0) {
- QString badge_text = QString::number(message_count);
- QFont badge_font = painter->font();
- badge_font.setPointSizeF(badge_font.pointSizeF() * 0.7);
- painter->setFont(badge_font);
-
- QFontMetrics badge_metrics(badge_font);
- int text_width = badge_metrics.horizontalAdvance(badge_text);
- int text_height = badge_metrics.height();
- int pad = text_height / 3;
- int badge_width = qMax(text_width + pad * 2, text_height + pad);
- int badge_height = text_height + pad;
-
- QRectF badge_rect(single_unit_rect.right() - badge_width - 4,
- single_unit_rect.top() + 4, badge_width,
- badge_height);
-
- painter->setPen(Qt::NoPen);
- painter->setBrush(QColor(220, 50, 47));
- painter->drawRoundedRect(badge_rect, badge_height / 2,
- badge_height / 2);
-
- painter->setPen(Qt::white);
- painter->drawText(badge_rect, Qt::AlignCenter, badge_text);
- }
- }
-
- // Draw final border (output only)
- if (is_output_item()) {
- QPen border_pen;
- border_pen.setWidth(node_border_width_);
-
- if (option->state & QStyle::State_Selected) {
- border_pen.setColor(app_pal.color(QPalette::Highlight));
- } else {
- border_pen.setColor(Qt::black);
- }
-
- painter->setPen(border_pen);
- painter->setBrush(Qt::NoBrush);
-
- painter->drawRect(rect());
- }
-}
-
-void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
-{
- if (last_arrow_rect_.contains(event->pos().toPoint())) {
- arrow_click_ = true;
- toggle_expanded();
- return;
- }
-
- event->setModifiers(
- QtUtils::flip_control_and_shift_modifiers(event->modifiers()));
-
- QGraphicsRectItem::mousePressEvent(event);
-}
-
-void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
-{
- if (arrow_click_) {
- return;
- }
-
- event->setModifiers(
- QtUtils::flip_control_and_shift_modifiers(event->modifiers()));
-
- QGraphicsRectItem::mouseMoveEvent(event);
-}
-
-void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
-{
- if (arrow_click_) {
- arrow_click_ = false;
- return;
- }
-
- event->setModifiers(
- QtUtils::flip_control_and_shift_modifiers(event->modifiers()));
-
- QGraphicsRectItem::mouseReleaseEvent(event);
-}
-
-QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change,
- const QVariant &value)
-{
- if (node_) {
- if (change == ItemPositionHasChanged) {
- readjust_all_edges();
-
- update_context_rect();
- } else if (change == ItemSelectedHasChanged) {
- if (value.toBool()) {
- qDebug() << "Selected node:" << node_;
- }
- }
- }
-
- return QGraphicsItem::itemChange(change, value);
-}
-
-void NodeViewItem::readjust_all_edges()
-{
- foreach (NodeViewEdge *edge, edges_) {
- if (NodeViewItem *to_item = edge->to_item()) {
- static_cast(to_item->parentItem())
- ->update_flow_direction_of_input_item(to_item);
- }
-
- edge->adjust();
- }
- foreach (NodeViewItem *child, children_) {
- child->readjust_all_edges();
- }
-}
-
-void NodeViewItem::update_context_rect()
-{
- QGraphicsItem *item = parentItem();
-
- while (item) {
- if (NodeViewContext *ctx = dynamic_cast(item)) {
- ctx->update_rect();
- break;
- }
-
- item = item->parentItem();
- }
-}
-
-void NodeViewItem::draw_node_title(QPainter *painter, QString text,
- const QRectF &rect,
- Qt::Alignment vertical_align,
- int icon_full_size)
-{
- QFontMetrics fm = painter->fontMetrics();
-
- // Calculate how much space we have for text
- int item_width = this->rect().width();
- int max_text_width = item_width - default_text_padding() * 2 - icon_full_size;
- int label_width = QtUtils::q_font_metrics_width(fm, text);
-
- // Concatenate text if necessary (adds a "..." to the end and removes characters until the
- // string fits in the bounds)
- if (label_width > max_text_width) {
- QString concatenated;
-
- do {
- text.chop(1);
- concatenated =
- QCoreApplication::translate("NodeViewItem", "%1...").arg(text);
- } while ((label_width = QtUtils::q_font_metrics_width(fm, concatenated)) >
- max_text_width);
-
- text = concatenated;
- }
-
- // Determine X position (favors horizontal centering unless it'll overrun the arrow)
- QRectF text_rect = rect;
- Qt::Alignment text_align = Qt::AlignHCenter | vertical_align;
- int likely_x = item_width / 2 - label_width / 2;
- if (likely_x < icon_full_size) {
- text_rect.adjust(icon_full_size, 0, 0, 0);
- text_align = Qt::AlignLeft | vertical_align;
- }
-
- // Draw the text in a rect (the rect is sized around text already in the constructor)
- painter->drawText(text_rect, text_align, text);
-}
-
-int NodeViewItem::draw_expand_arrow(QPainter *painter)
-{
- // Draw right or down arrow based on expanded state
- int icon_size = painter->fontMetrics().height() / 2;
- int icon_padding = default_item_height() / 2 - icon_size / 2;
- int icon_full_size = icon_size + icon_padding * 2;
-
- painter->setRenderHint(QPainter::SmoothPixmapTransform);
-
- const QIcon &expand_icon = is_expanded() ? icon::tri_down : icon::tri_right;
- int icon_size_scaled = icon_size * painter->transform().m11();
-
- last_arrow_rect_ = QRect(this->rect().x() + icon_padding,
- this->rect().y() + icon_padding, icon_size,
- icon_size);
-
- painter->drawPixmap(
- last_arrow_rect_,
- expand_icon.pixmap(QSize(icon_size_scaled, icon_size_scaled)));
-
- return icon_full_size;
-}
-
-void NodeViewItem::set_label_as_output(bool e)
-{
- label_as_output_ = e;
- output_connector_->setVisible(!e);
- update();
-}
-
-QPointF NodeViewItem::get_input_point() const
-{
- return input_connector_->scenePos();
-}
-
-QPointF NodeViewItem::get_output_point() const
-{
- QPointF p = output_connector_->scenePos();
- QRectF r = output_connector_->polygon().boundingRect();
-
- switch (flow_dir_) {
- case NodeViewCommon::k_left_to_right:
- default:
- p.setX(p.x() + r.width());
- break;
- case NodeViewCommon::k_right_to_left:
- p.setX(p.x() - r.width());
- break;
- case NodeViewCommon::k_top_to_bottom:
- p.setY(p.y() + r.height());
- break;
- case NodeViewCommon::k_bottom_to_top:
- p.setY(p.y() - r.height());
- break;
- }
-
- return p;
-}
-
-void NodeViewItem::set_flow_direction(NodeViewCommon::FlowDirection dir)
-{
- if (flow_dir_ != dir) {
- flow_dir_ = dir;
-
- input_connector_->set_flow_direction(dir);
- output_connector_->set_flow_direction(dir);
-
- update_input_connector_position();
- update_output_connector_position();
-
- if (is_output_item()) {
- update_node_position();
- }
-
- readjust_all_edges();
- }
-}
-
-void NodeViewItem::update_node_position()
-{
- setPos(node_to_screen_point(cached_node_pos_, flow_dir_));
-}
-
-void NodeViewItem::update_input_connector_position()
-{
- QRectF output_rect = input_connector_->polygon().boundingRect();
-
- NodeViewCommon::FlowDirection using_flow_dir = flow_dir_;
-
- if (is_expanded() && !NodeViewCommon::is_flow_horizontal(flow_dir_)) {
- if (edges_.isEmpty() || edges_.first()->from_item()->x() < this->x()) {
- using_flow_dir = NodeViewCommon::k_left_to_right;
- } else {
- using_flow_dir = NodeViewCommon::k_right_to_left;
- }
- }
-
- // Input connector flow directions change conditionally
- switch (using_flow_dir) {
- case NodeViewCommon::k_left_to_right:
- input_connector_->setPos(rect().left() - output_rect.width(), 0);
- break;
- case NodeViewCommon::k_right_to_left:
- input_connector_->setPos(rect().right() + output_rect.width(), 0);
- break;
- case NodeViewCommon::k_top_to_bottom:
- input_connector_->setPos(rect().center().x(),
- rect().top() - output_rect.height());
- break;
- case NodeViewCommon::k_bottom_to_top:
- input_connector_->setPos(rect().center().x(),
- rect().bottom() + output_rect.height());
- break;
- case NodeViewCommon::k_invalid_direction:
- break;
- }
-}
-
-void NodeViewItem::update_output_connector_position()
-{
- switch (flow_dir_) {
- case NodeViewCommon::k_left_to_right:
- output_connector_->setPos(rect().right(), 0);
- break;
- case NodeViewCommon::k_right_to_left:
- output_connector_->setPos(rect().left(), 0);
- break;
- case NodeViewCommon::k_top_to_bottom:
- output_connector_->setPos(rect().center().x(), rect().bottom());
- break;
- case NodeViewCommon::k_bottom_to_top:
- output_connector_->setPos(rect().center().x(), rect().top());
- break;
- case NodeViewCommon::k_invalid_direction:
- break;
- }
-}
-
-bool NodeViewItem::is_input_valid(const QString &input)
-{
- if (!node_->is_input_connectable(input) || node_->is_input_hidden(input)) {
- return false;
- }
- // For OFX plugin nodes, only show texture inputs in the node graph
- // to avoid excessively tall nodes with dozens of scalar parameters.
- // Scalar parameters are still visible in the parameter panel.
- if (node_->getPluginInstance() != nullptr &&
- node_->get_input_data_type(input) != NodeValue::k_texture) {
- return false;
- }
- return true;
-}
-
-void NodeViewItem::set_rect_size(int height_units)
-{
- // Set rect
- int widget_width = default_item_width();
- int widget_height = default_item_height();
-
- setRect(QRectF(-widget_width / 2, -widget_height / 2, widget_width,
- widget_height * height_units));
-}
-
-bool NodeViewItem::can_be_expanded() const
-{
- if (is_output_item()) {
- return has_connectable_inputs_;
- } else {
- return node_->get_input_flags(input_) & k_input_flag_array &&
- element_ == -1 && !node_->is_input_connected(input_);
- }
-}
-
-void NodeViewItem::update_children_positions()
-{
- int y = 1;
- int h = default_item_height();
-
- foreach (NodeViewItem *c, children_) {
- c->setPos(QPointF(0, y * h));
-
- y += c->get_logical_height_with_children();
- }
-
- set_rect_size(y);
-
- if (NodeViewItem *p = dynamic_cast(parentItem())) {
- p->update_children_positions();
- }
-}
-
-int NodeViewItem::get_logical_height_with_children() const
-{
- int h = 1;
-
- foreach (NodeViewItem *c, children_) {
- h += c->get_logical_height_with_children();
- }
-
- return h;
-}
-
-void NodeViewItem::update_flow_direction_of_input_item(NodeViewItem *child)
-{
- if (!child->is_output_item()) {
- if (NodeViewCommon::is_flow_vertical(flow_dir_)) {
- if (!child->edges().isEmpty() &&
- child->edges().first()->from_item()->scenePos().x() >
- child->scenePos().x()) {
- child->set_flow_direction(NodeViewCommon::k_right_to_left);
- } else {
- child->set_flow_direction(NodeViewCommon::k_left_to_right);
- }
- } else {
- child->set_flow_direction(flow_dir_);
- }
- }
-}
-
-void NodeViewItem::repopulate_inputs()
-{
- if (is_output_item()) {
- has_connectable_inputs_ = false;
-
- foreach (const QString &input, node_->inputs()) {
- if (is_input_valid(input)) {
- has_connectable_inputs_ = true;
- break;
- }
- }
-
- input_connector_->setVisible(has_connectable_inputs_);
- }
-
- if (is_expanded() && (is_output_item() || element_ == -1)) {
- // Create or remove inputs when necessary
- // NOTE: This is not the most efficient thing in the world, but it does work
- set_expanded(false);
- set_expanded(true);
- }
-}
-
-void NodeViewItem::input_array_size_changed(const QString &input)
-{
- if (input == input_) {
- repopulate_inputs();
- }
-}
-
-void NodeViewItem::node_appearance_changed()
-{
- update();
-}
-
-void NodeViewItem::set_highlighted(bool e)
-{
- highlighted_ = e;
- update();
-}
-
-NodeViewItem *NodeViewItem::get_item_for_input(NodeInput input)
-{
- if (NodeGroup *group = dynamic_cast(node_)) {
- if (input.node() != group) {
- // Translate input to group input
- QString id = group->get_id_of_passthrough(input);
- input.set_node(group);
- input.set_input(id);
- }
- }
-
- if (is_expanded()) {
- if (input_.isEmpty()) {
- // Look for the input in our children
- foreach (NodeViewItem *i, children_) {
- if (i->input_ == input.input()) {
- return i->get_item_for_input(input);
- }
- }
- } else {
- // Look for element in our children
- if (input.element() >= 0 && input.element() < children_.size()) {
- return children_.at(input.element())->get_item_for_input(input);
- }
- }
- }
-
- // Fallback to this object
- return this;
-}
-
-}
diff --git a/.bak/nodeview/nodeviewitem.h b/.bak/nodeview/nodeviewitem.h
deleted file mode 100644
index 93d4f13ac..000000000
--- a/.bak/nodeview/nodeviewitem.h
+++ /dev/null
@@ -1,246 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_NODEVIEWITEM_H
-#define OAK_NODEVIEWITEM_H
-
-#include
-#include
-#include
-#include
-
-#include "node/node.h"
-#include "nodeviewcommon.h"
-#include "nodeviewitemconnector.h"
-
-namespace olive
-{
-
-class NodeViewItem;
-class NodeViewEdge;
-
-/**
- * @brief A visual widget representation of a Node object to be used in a NodeView
- *
- * This widget can be collapsed or expanded to show/hide the node's various parameters.
- *
- * To retrieve the NodeViewItem for a certain Node, use NodeView::NodeToUIObject().
- */
-class NodeViewItem : public QObject, public QGraphicsRectItem {
- Q_OBJECT
-public:
- NodeViewItem(Node *node, const QString &input, int element, Node *context,
- QGraphicsItem *parent = nullptr);
- NodeViewItem(Node *node, Node *context, QGraphicsItem *parent = nullptr)
- : NodeViewItem(node, QString(), -1, context, parent)
- {
- }
-
- virtual ~NodeViewItem() override;
-
- Node::Position get_node_position_data() const;
- QPointF get_node_position() const;
- void set_node_position(const QPointF &pos);
- void set_node_position(const Node::Position &pos);
-
- QVector get_all_edges_recursively() const;
-
- /**
- * @brief Get currently attached node
- */
- Node *get_node() const
- {
- return node_;
- }
-
- NodeInput get_input() const
- {
- return NodeInput(node_, input_, element_);
- }
-
- Node *get_context() const
- {
- return context_;
- }
-
- /**
- * @brief Get expanded state
- */
- bool is_expanded() const
- {
- return expanded_;
- }
-
- const QVector &edges() const
- {
- return edges_;
- }
-
- /**
- * @brief Set expanded state
- */
- void set_expanded(bool e, bool hide_titlebar = false);
- void toggle_expanded();
-
- QPointF get_input_point() const;
- QPointF get_output_point() const;
-
- /**
- * @brief Sets the direction nodes are flowing
- */
- void set_flow_direction(NodeViewCommon::FlowDirection dir);
-
- NodeViewCommon::FlowDirection get_flow_direction() const
- {
- return flow_dir_;
- }
-
- static int default_text_padding();
-
- static int default_item_height();
-
- static int default_item_width();
-
- static int default_item_border();
-
- static QPointF node_to_screen_point(QPointF p,
- NodeViewCommon::FlowDirection direction);
- static QPointF screen_to_node_point(QPointF p,
- NodeViewCommon::FlowDirection direction);
-
- static qreal
- default_item_horizontal_padding(NodeViewCommon::FlowDirection dir);
- static qreal default_item_vertical_padding(NodeViewCommon::FlowDirection dir);
- qreal default_item_horizontal_padding() const;
- qreal default_item_vertical_padding() const;
-
- void add_edge(NodeViewEdge *edge);
- void remove_edge(NodeViewEdge *edge);
-
- bool is_labelled_as_output_of_context() const
- {
- return label_as_output_;
- }
-
- void set_label_as_output(bool e);
-
- void set_highlighted(bool e);
-
- NodeViewItem *get_item_for_input(NodeInput input);
-
- bool is_output_item() const
- {
- return input_.isEmpty();
- }
-
- void readjust_all_edges();
-
- void update_flow_direction_of_input_item(NodeViewItem *child);
-
- bool can_be_expanded() const;
-
-protected:
- virtual void paint(QPainter *painter,
- const QStyleOptionGraphicsItem *option,
- QWidget *widget = nullptr) override;
-
- virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
- virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
- virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
-
- virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange change,
- const QVariant &value) override;
-
-private:
- void update_context_rect();
-
- void draw_node_title(QPainter *painter, QString text, const QRectF &rect,
- Qt::Alignment vertical_align, int icon_full_size);
-
- int draw_expand_arrow(QPainter *painter);
-
- /**
- * @brief Internal update function when logical position changes
- */
- void update_node_position();
-
- void update_input_connector_position();
- void update_output_connector_position();
-
- bool is_input_valid(const QString &input);
-
- void set_rect_size(int height_units = 1);
-
- void update_children_positions();
-
- int get_logical_height_with_children() const;
-
- /**
- * @brief Reference to attached Node
- */
- Node *node_;
- QString input_;
- int element_;
-
- Node *context_;
-
- /**
- * @brief Cached list of node inputs
- */
- QVector children_;
-
- /// Sizing variables to use when drawing
- int node_border_width_;
-
- /**
- * @brief Expanded state
- */
- bool expanded_;
-
- bool highlighted_;
-
- NodeViewCommon::FlowDirection flow_dir_;
-
- QVector edges_;
-
- QPointF cached_node_pos_;
-
- QRect last_arrow_rect_;
- bool arrow_click_;
-
- NodeViewItemConnector *input_connector_;
- NodeViewItemConnector *output_connector_;
-
- bool has_connectable_inputs_;
-
- bool label_as_output_;
-
-private slots:
- void node_appearance_changed();
-
- void repopulate_inputs();
-
- void input_array_size_changed(const QString &input);
-};
-
-}
-
-#endif // OAK_NODEVIEWITEM_H
diff --git a/.bak/nodeview/nodeviewitemconnector.cpp b/.bak/nodeview/nodeviewitemconnector.cpp
deleted file mode 100644
index 8e05ade87..000000000
--- a/.bak/nodeview/nodeviewitemconnector.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "nodeviewitemconnector.h"
-
-#include
-#include
-#include
-#include
-
-#include "nodeviewitem.h"
-
-namespace olive
-{
-
-NodeViewItemConnector::NodeViewItemConnector(bool is_output,
- QGraphicsItem *parent)
- : QGraphicsPolygonItem(parent)
- , output_(is_output)
-{
- QColor c = qApp->palette().text().color();
- setPen(QPen(c, NodeViewItem::default_item_border()));
- setBrush(c);
-}
-
-void NodeViewItemConnector::set_flow_direction(NodeViewCommon::FlowDirection dir)
-{
- QFont f;
- QFontMetricsF fm(f);
-
- int triangle_sz = fm.height() / 2;
- int triangle_sz_half = triangle_sz / 2;
-
- QPolygonF p;
- p.resize(3);
-
- switch (dir) {
- case NodeViewCommon::k_left_to_right:
- // Triangle pointing right
- p[0] = QPointF(0, -triangle_sz_half);
- p[1] = QPointF(triangle_sz_half, 0);
- p[2] = QPointF(0, triangle_sz_half);
- break;
- case NodeViewCommon::k_top_to_bottom:
- // Triangle pointing down
- p[0] = QPointF(-triangle_sz_half, 0);
- p[1] = QPointF(0, triangle_sz_half);
- p[2] = QPointF(triangle_sz_half, 0);
- break;
- case NodeViewCommon::k_bottom_to_top:
- // Triangle pointing up
- p[0] = QPointF(-triangle_sz_half, 0);
- p[1] = QPointF(0, -triangle_sz_half);
- p[2] = QPointF(triangle_sz_half, 0);
- break;
- case NodeViewCommon::k_right_to_left:
- // Triangle pointing left
- p[0] = QPointF(0, -triangle_sz_half);
- p[1] = QPointF(-triangle_sz_half, 0);
- p[2] = QPointF(0, triangle_sz_half);
- break;
- case NodeViewCommon::k_invalid_direction:
- break;
- }
-
- setPolygon(p);
-}
-
-QPainterPath NodeViewItemConnector::shape() const
-{
- // Yes, we skip QGraphicsPolygonItem because it adds the polygon. QGraphicsItem adds the
- // boundingRect which we modify below
- return QGraphicsItem::shape(); // clazy:exclude=skipped-base-method
-}
-
-QRectF NodeViewItemConnector::boundingRect() const
-{
- QRectF b = this->polygon().boundingRect();
- const int radius = QFontMetrics(QFont()).height() / 2;
- b.adjust(-radius, -radius, radius, radius);
- return b;
-}
-
-}
diff --git a/.bak/nodeview/nodeviewitemconnector.h b/.bak/nodeview/nodeviewitemconnector.h
deleted file mode 100644
index 253e15014..000000000
--- a/.bak/nodeview/nodeviewitemconnector.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_NODEVIEWITEMCONNECTOR_H
-#define OAK_NODEVIEWITEMCONNECTOR_H
-
-#include
-
-#include "nodeviewcommon.h"
-
-namespace olive
-{
-
-class NodeViewItemConnector : public QGraphicsPolygonItem {
-public:
- NodeViewItemConnector(bool is_output, QGraphicsItem *parent = nullptr);
-
- void set_flow_direction(NodeViewCommon::FlowDirection dir);
-
- bool is_output() const
- {
- return output_;
- }
-
- virtual QPainterPath shape() const override;
- virtual QRectF boundingRect() const override;
-
-private:
- bool output_;
-};
-
-}
-
-#endif // OAK_NODEVIEWITEMCONNECTOR_H
diff --git a/.bak/nodeview/nodeviewminimap.cpp b/.bak/nodeview/nodeviewminimap.cpp
deleted file mode 100644
index 3e7baace5..000000000
--- a/.bak/nodeview/nodeviewminimap.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "nodeviewminimap.h"
-
-#include
-
-namespace olive
-{
-
-#define super QGraphicsView
-
-NodeViewMiniMap::NodeViewMiniMap(NodeViewScene *scene, QWidget *parent)
- : super(parent)
- , resizing_(false)
-{
- connect(scene, &QGraphicsScene::sceneRectChanged, this,
- &NodeViewMiniMap::scene_changed);
- setScene(scene);
-
- setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setViewportUpdateMode(FullViewportUpdate);
- setFrameShape(QFrame::Panel);
- setFrameShadow(QFrame::Plain);
- setMouseTracking(true);
-
- QMetaObject::invokeMethod(this, &NodeViewMiniMap::set_default_size,
- Qt::QueuedConnection);
-
- resize_triangle_sz_ = fontMetrics().height() / 2;
-}
-
-void NodeViewMiniMap::set_viewport_rect(const QPolygonF &rect)
-{
- viewport_rect_ = rect;
-
- viewport()->update();
-}
-
-void NodeViewMiniMap::drawForeground(QPainter *painter, const QRectF &rect)
-{
- super::drawForeground(painter, rect);
-
- QColor viewport_color = palette().text().color();
-
- // Draw resize triangle
- painter->save();
- painter->resetTransform();
-
- QPointF triangle[3] = { QPointF(0, 0), QPointF(resize_triangle_sz_, 0),
- QPointF(0, resize_triangle_sz_) };
- painter->setBrush(viewport_color);
- painter->setPen(viewport_color);
- painter->drawPolygon(triangle, 3);
-
- painter->restore();
-
- // Draw viewport rectangle
- viewport_color.setAlphaF(0.25);
- painter->setBrush(viewport_color);
-
- painter->drawPolygon(viewport_rect_);
-}
-
-void NodeViewMiniMap::resizeEvent(QResizeEvent *event)
-{
- super::resizeEvent(event);
-
- emit resized();
-
- scene_changed(sceneRect());
-}
-
-void NodeViewMiniMap::mousePressEvent(QMouseEvent *event)
-{
- if (event->button() == Qt::LeftButton) {
- if (mouse_inside_resize_triangle(event)) {
- // Resizing!
- resizing_ = true;
- resize_anchor_ = QCursor::pos();
- } else {
- emit_move_signal(event);
- }
- }
-}
-
-void NodeViewMiniMap::mouseMoveEvent(QMouseEvent *event)
-{
- if (event->buttons() & Qt::LeftButton) {
- if (resizing_) {
- QPointF movement = QCursor::pos() - resize_anchor_;
- resize(QSize(width() - movement.x(), height() - movement.y()));
- resize_anchor_ = QCursor::pos();
- } else {
- emit_move_signal(event);
- }
- } else {
- if (mouse_inside_resize_triangle(event)) {
- setCursor(Qt::SizeFDiagCursor);
- } else {
- unsetCursor();
- }
- }
-}
-
-void NodeViewMiniMap::mouseReleaseEvent(QMouseEvent *event)
-{
- resizing_ = false;
-}
-
-void NodeViewMiniMap::scene_changed(const QRectF &bounding)
-{
- double x_scale = double(this->width()) / bounding.width();
- double y_scale = double(this->height()) / bounding.height();
-
- double min_scale = qMin(x_scale, y_scale);
-
- QTransform transform;
- transform.scale(min_scale, min_scale);
-
- setTransform(transform);
-}
-
-void NodeViewMiniMap::set_default_size()
-{
- if (parentWidget()) {
- resize(parentWidget()->width() / 4, parentWidget()->height() / 4);
- }
-}
-
-bool NodeViewMiniMap::mouse_inside_resize_triangle(QMouseEvent *event)
-{
- return event->pos().x() <= resize_triangle_sz_ &&
- event->pos().y() <= resize_triangle_sz_;
-}
-
-void NodeViewMiniMap::emit_move_signal(QMouseEvent *event)
-{
- emit move_to_scene_point(mapToScene(event->pos()));
-}
-
-}
diff --git a/.bak/nodeview/nodeviewminimap.h b/.bak/nodeview/nodeviewminimap.h
deleted file mode 100644
index de7176d50..000000000
--- a/.bak/nodeview/nodeviewminimap.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_NODEVIEWMINIMAP_H
-#define OAK_NODEVIEWMINIMAP_H
-
-#include
-
-#include "nodeviewscene.h"
-
-namespace olive
-{
-
-class NodeViewMiniMap : public QGraphicsView {
- Q_OBJECT
-public:
- NodeViewMiniMap(NodeViewScene *scene, QWidget *parent = nullptr);
-
-public slots:
- void set_viewport_rect(const QPolygonF &rect);
-
-signals:
- void resized();
-
- void move_to_scene_point(const QPointF &pos);
-
-protected:
- virtual void drawForeground(QPainter *painter, const QRectF &rect) override;
-
- virtual void resizeEvent(QResizeEvent *event) override;
-
- virtual void mousePressEvent(QMouseEvent *event) override;
- virtual void mouseMoveEvent(QMouseEvent *event) override;
- virtual void mouseReleaseEvent(QMouseEvent *event) override;
- virtual void mouseDoubleClickEvent(QMouseEvent *event) override
- {
- }
-
-private slots:
- void scene_changed(const QRectF &bounding);
-
- void set_default_size();
-
-private:
- bool mouse_inside_resize_triangle(QMouseEvent *event);
-
- void emit_move_signal(QMouseEvent *event);
-
- int resize_triangle_sz_;
-
- QPolygonF viewport_rect_;
-
- bool resizing_;
-
- QPoint resize_anchor_;
-};
-
-}
-
-#endif // OAK_NODEVIEWMINIMAP_H
diff --git a/.bak/nodeview/nodeviewscene.cpp b/.bak/nodeview/nodeviewscene.cpp
deleted file mode 100644
index bb302efcc..000000000
--- a/.bak/nodeview/nodeviewscene.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "nodeviewscene.h"
-
-#include "core.h"
-#include "node/project/sequence/sequence.h"
-#include "nodeviewedge.h"
-#include "nodeviewitem.h"
-
-namespace olive
-{
-
-NodeViewScene::NodeViewScene(QObject *parent)
- : QGraphicsScene(parent)
- , direction_(NodeViewCommon::k_left_to_right)
- , curved_edges_(true)
-{
-}
-
-void NodeViewScene::set_flow_direction(NodeViewCommon::FlowDirection direction)
-{
- direction_ = direction;
-
- foreach (NodeViewContext *ctx, context_map_) {
- ctx->set_flow_direction(direction_);
- }
-}
-
-void NodeViewScene::select_all()
-{
- foreach (QGraphicsItem *i, items()) {
- i->setSelected(true);
- }
-}
-
-void NodeViewScene::deselect_all()
-{
- foreach (QGraphicsItem *i, items()) {
- i->setSelected(false);
- }
-}
-
-QVector NodeViewScene::get_selected_items() const
-{
- QVector items;
-
- foreach (NodeViewContext *ctx, context_map_) {
- items.append(ctx->get_selected_items());
- }
-
- return items;
-}
-
-NodeViewContext *NodeViewScene::add_context(Node *node)
-{
- NodeViewContext *context_item = context_map_.value(node);
-
- if (!context_item) {
- context_item = new NodeViewContext(node);
-
- context_item->set_flow_direction(get_flow_direction());
- context_item->set_curved_edges(get_edges_are_curved());
-
- QPointF pos(0, 0);
- QRectF item_rect = context_item->rect();
- while (!items(item_rect).isEmpty()) {
- pos.setY(pos.y() + item_rect.height());
- item_rect = context_item->rect().translated(pos);
- }
- context_item->setPos(pos);
-
- addItem(context_item);
-
- context_map_.insert(node, context_item);
- }
-
- return context_item;
-}
-
-void NodeViewScene::remove_context(Node *node)
-{
- delete context_map_.take(node);
-}
-
-Qt::Orientation NodeViewScene::get_flow_orientation() const
-{
- return NodeViewCommon::get_flow_orientation(direction_);
-}
-
-void NodeViewScene::set_edges_are_curved(bool curved)
-{
- if (curved_edges_ != curved) {
- curved_edges_ = curved;
-
- foreach (NodeViewContext *ctx, context_map_) {
- ctx->set_curved_edges(curved_edges_);
- }
- }
-}
-
-}
diff --git a/.bak/nodeview/nodeviewscene.h b/.bak/nodeview/nodeviewscene.h
deleted file mode 100644
index 297f16447..000000000
--- a/.bak/nodeview/nodeviewscene.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_NODEVIEWSCENE_H
-#define OAK_NODEVIEWSCENE_H
-
-#include
-#include
-
-#include "node/project.h"
-#include "nodeviewcontext.h"
-#include "nodeviewedge.h"
-#include "nodeviewitem.h"
-#include "undo/undostack.h"
-
-namespace olive
-{
-
-class NodeViewScene : public QGraphicsScene {
- Q_OBJECT
-public:
- NodeViewScene(QObject *parent = nullptr);
-
- void select_all();
- void deselect_all();
-
- QVector get_selected_items() const;
-
- const QHash &context_map() const
- {
- return context_map_;
- }
-
- Qt::Orientation get_flow_orientation() const;
-
- NodeViewCommon::FlowDirection get_flow_direction() const
- {
- return direction_;
- }
-
- void set_flow_direction(NodeViewCommon::FlowDirection direction);
-
- bool get_edges_are_curved() const
- {
- return curved_edges_;
- }
-
-public slots:
- NodeViewContext *add_context(Node *node);
- void remove_context(Node *node);
-
- /**
- * @brief Set whether edges in this scene should be curved or not
- */
- void set_edges_are_curved(bool curved);
-
-private:
- QHash context_map_;
-
- Project *graph_;
-
- NodeViewCommon::FlowDirection direction_;
-
- bool curved_edges_;
-};
-
-}
-
-#endif // OAK_NODEVIEWSCENE_H
diff --git a/.bak/nodeview/nodeviewtoolbar.cpp b/.bak/nodeview/nodeviewtoolbar.cpp
deleted file mode 100644
index 54d377407..000000000
--- a/.bak/nodeview/nodeviewtoolbar.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Oak Video Editor - Non-Linear Video Editor
- * Copyright (C) 2025 Olive CE 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 "nodeviewtoolbar.h"
-
-#include
-#include
-
-#include "ui/icons/icons.h"
-
-namespace olive
-{
-
-#define super QWidget
-
-NodeViewToolBar::NodeViewToolBar(QWidget *parent)
- : QWidget(parent)
-{
- QHBoxLayout *layout = new QHBoxLayout(this);
- layout->setContentsMargins(0, 0, 0, 0);
-
- add_node_btn_ = new QPushButton();
- connect(add_node_btn_, &QPushButton::clicked, this,
- &NodeViewToolBar::add_node_clicked);
- layout->addWidget(add_node_btn_);
-
- minimap_btn_ = new QPushButton();
- minimap_btn_->setCheckable(true);
- connect(minimap_btn_, &QPushButton::clicked, this,
- &NodeViewToolBar::mini_map_enabled_toggled);
- layout->addWidget(minimap_btn_);
-
- layout->addStretch();
-
- retranslate();
- update_icons();
-}
-
-void NodeViewToolBar::changeEvent(QEvent *e)
-{
- if (e->type() == QEvent::LanguageChange) {
- retranslate();
- } else if (e->type() == QEvent::StyleChange) {
- update_icons();
- }
- super::changeEvent(e);
-}
-
-void NodeViewToolBar::retranslate()
-{
- add_node_btn_->setToolTip(tr("Add Node"));
- minimap_btn_->setToolTip(tr("Toggle Mini-Map"));
-}
-
-void NodeViewToolBar::update_icons()
-{
- add_node_btn_->setIcon(icon::add);
- minimap_btn_->setIcon(icon::mini_map);
-}
-
-}
diff --git a/.bak/nodeview/nodeviewtoolbar.h b/.bak/nodeview/nodeviewtoolbar.h
deleted file mode 100644
index 9dbe49bbf..000000000
--- a/.bak/nodeview/nodeviewtoolbar.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Oak Video Editor - Non-Linear Video Editor
- * Copyright (C) 2025 Olive CE 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 OAK_NODEVIEWTOOLBAR_H
-#define OAK_NODEVIEWTOOLBAR_H
-
-#include
-#include
-
-namespace olive
-{
-
-class NodeViewToolBar : public QWidget {
- Q_OBJECT
-public:
- NodeViewToolBar(QWidget *parent = nullptr);
-
-public slots:
- void set_mini_map_enabled(bool e)
- {
- minimap_btn_->setChecked(e);
- }
-
-signals:
- void add_node_clicked();
-
- void mini_map_enabled_toggled(bool e);
-
-protected:
- virtual void changeEvent(QEvent *e) override;
-
-private:
- void retranslate();
-
- void update_icons();
-
- QPushButton *add_node_btn_;
-
- QPushButton *minimap_btn_;
-};
-
-}
-
-#endif // OAK_NODEVIEWTOOLBAR_H
diff --git a/.bak/nodeview/nodewidget.cpp b/.bak/nodeview/nodewidget.cpp
deleted file mode 100644
index 0f3b62e90..000000000
--- a/.bak/nodeview/nodewidget.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "nodewidget.h"
-
-#include
-
-namespace olive
-{
-
-NodeWidget::NodeWidget(QWidget *parent)
- : QWidget(parent)
-{
- QVBoxLayout *outer_layout = new QVBoxLayout(this);
- outer_layout->setContentsMargins(0, 0, 0, 0);
-
- toolbar_ = new NodeViewToolBar();
- outer_layout->addWidget(toolbar_);
-
- // Create NodeView widget
- node_view_ = new NodeView(this);
- outer_layout->addWidget(node_view_);
-
- // Connect toolbar to NodeView
- connect(toolbar_, &NodeViewToolBar::mini_map_enabled_toggled, node_view_,
- &NodeView::set_mini_map_enabled);
- connect(toolbar_, &NodeViewToolBar::add_node_clicked, node_view_,
- &NodeView::show_add_menu);
-
- // Set defaults
- toolbar_->set_mini_map_enabled(true);
- node_view_->set_mini_map_enabled(true);
-
- setSizePolicy(node_view_->sizePolicy());
-}
-
-}
diff --git a/.bak/nodeview/nodewidget.h b/.bak/nodeview/nodewidget.h
deleted file mode 100644
index 2715a2863..000000000
--- a/.bak/nodeview/nodewidget.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_NODEWIDGET_H
-#define OAK_NODEWIDGET_H
-
-#include
-
-#include "nodeview.h"
-#include "nodeviewtoolbar.h"
-
-namespace olive
-{
-
-class NodeWidget : public QWidget {
- Q_OBJECT
-public:
- NodeWidget(QWidget *parent = nullptr);
-
- NodeView *view() const
- {
- return node_view_;
- }
-
- void set_contexts(const QVector &nodes)
- {
- node_view_->set_contexts(nodes);
- toolbar_->setEnabled(!nodes.isEmpty());
- }
-
-private:
- NodeView *node_view_;
-
- NodeViewToolBar *toolbar_;
-};
-
-}
-
-#endif // OAK_NODEWIDGET_H
diff --git a/.bak/projectexplorer/projectexplorer.cpp b/.bak/projectexplorer/projectexplorer.cpp
deleted file mode 100644
index 26db30d42..000000000
--- a/.bak/projectexplorer/projectexplorer.cpp
+++ /dev/null
@@ -1,947 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "projectexplorer.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "common/define.h"
-#include "core.h"
-#include "dialog/footageproperties/footageproperties.h"
-#include "dialog/proxy/proxydialog.h"
-#include "dialog/sequence/sequence.h"
-#include "projectexplorerundo.h"
-#include "oakengine/footage.h"
-#include "oakengine/node.h"
-#include "oakengine/task.h"
-#include "widget/menu/menu.h"
-#include "widget/menu/menushared.h"
-#include "node/nodeundo.h"
-#include "window/mainwindow/mainwindow.h"
-#include "window/mainwindow/mainwindowundo.h"
-#include "widget/timelinewidget/timelinewidget.h"
-
-namespace olive
-{
-
-namespace
-{
-QVector get_selected_proxy_footage(const QVector &items)
-{
- QVector footage;
- for (Node *node : items) {
- Footage *candidate = dynamic_cast(node);
- if (!candidate || !candidate->get_first_enabled_video_stream().is_valid() ||
- footage.contains(candidate)) {
- continue;
- }
- footage.append(candidate);
- }
- return footage;
-}
-
-/**
- * @brief Proxy generation driven by the liboakengine C ABI facade
- *
- * Replaces the direct ProxyManager::get_or_start_proxy() drive: the actual
- * transcode and its synchronous wait live behind
- * oakengine_footage_proxy_generate() (which also records the proxy state
- * on the footage and invalidates it), while the task stays on the
- * TaskManager queue like before.
- */
-class FacadeProxyTask : public Task {
-public:
- FacadeProxyTask(Footage *footage)
- : footage_(footage)
- {
- set_title(tr("Generating proxy for \"%1\"")
- .arg(footage->get_label_or_name()));
- }
-
-protected:
- virtual bool run() override
- {
- OakEngineFootage *handle = oakengine_footage_borrow(
- reinterpret_cast(footage_));
- const int rc = oakengine_footage_proxy_generate(handle);
- oakengine_footage_free(handle);
- if (rc != OAKENGINE_OK) {
- char err[512];
- err[0] = '\0';
- oakengine_footage_last_error(err, sizeof(err));
- set_error(err[0] ? QString::fromUtf8(err) :
- tr("Proxy generation failed"));
- return false;
- }
- return true;
- }
-
-private:
- Footage *footage_;
-};
-}
-
-ProjectExplorer::ProjectExplorer(QWidget *parent)
- : QWidget(parent)
- , model_(this)
-{
- // Create layout
- QVBoxLayout *layout = new QVBoxLayout(this);
- layout->setSpacing(0);
- layout->setContentsMargins(0, 0, 0, 0);
-
- // Set up navigation bar
- nav_bar_ = new ProjectExplorerNavigation(this);
- connect(nav_bar_, &ProjectExplorerNavigation::size_changed, this,
- &ProjectExplorer::size_changed_slot);
- connect(nav_bar_, &ProjectExplorerNavigation::directory_up_clicked, this,
- &ProjectExplorer::dir_up_slot);
- layout->addWidget(nav_bar_);
-
- // Set up stacked widget
- stacked_widget_ = new QStackedWidget(this);
- layout->addWidget(stacked_widget_);
-
- // Set up sort filter proxy model
- sort_model_.setSourceModel(&model_);
- sort_model_.setFilterCaseSensitivity(Qt::CaseInsensitive);
- sort_model_.setSortRole(ProjectViewModel::k_inner_text_role);
-
- // Add tree view to stacked widget
- tree_view_ = new ProjectExplorerTreeView(stacked_widget_);
- tree_view_->setSortingEnabled(true);
- tree_view_->sortByColumn(0, Qt::AscendingOrder);
- tree_view_->setContextMenuPolicy(Qt::CustomContextMenu);
- add_view(tree_view_);
-
- // Add list view to stacked widget
- list_view_ = new ProjectExplorerListView(stacked_widget_);
- list_view_->setContextMenuPolicy(Qt::CustomContextMenu);
- add_view(list_view_);
-
- // Add icon view to stacked widget
- icon_view_ = new ProjectExplorerIconView(stacked_widget_);
- icon_view_->setContextMenuPolicy(Qt::CustomContextMenu);
- add_view(icon_view_);
-
- // Set default view to tree view
- set_view_type(ProjectToolbar::tree_view);
-
- // Set default icon size
- size_changed_slot(k_project_icon_size_default);
-
- connect(tree_view_, &ProjectExplorerTreeView::customContextMenuRequested,
- this, &ProjectExplorer::show_context_menu);
- connect(list_view_, &ProjectExplorerListView::customContextMenuRequested,
- this, &ProjectExplorer::show_context_menu);
- connect(icon_view_, &ProjectExplorerIconView::customContextMenuRequested,
- this, &ProjectExplorer::show_context_menu);
-
- update_nav_bar_text();
-}
-
-const ProjectToolbar::ViewType &ProjectExplorer::view_type() const
-{
- return view_type_;
-}
-
-void ProjectExplorer::set_view_type(ProjectToolbar::ViewType type)
-{
- view_type_ = type;
-
- // Set widget based on view type
- switch (view_type_) {
- case ProjectToolbar::tree_view:
- stacked_widget_->setCurrentWidget(tree_view_);
- nav_bar_->setVisible(false);
- break;
- case ProjectToolbar::list_view:
- stacked_widget_->setCurrentWidget(list_view_);
- nav_bar_->setVisible(true);
- break;
- case ProjectToolbar::icon_view:
- stacked_widget_->setCurrentWidget(icon_view_);
- nav_bar_->setVisible(true);
- break;
- }
-}
-
-void ProjectExplorer::edit(Node *item)
-{
- current_view()->edit(
- sort_model_.mapFromSource(model_.create_index_from_item(item)));
-}
-
-void ProjectExplorer::add_view(QAbstractItemView *view)
-{
- view->setModel(&sort_model_);
- view->setEditTriggers(QAbstractItemView::SelectedClicked);
- connect(view, &QAbstractItemView::doubleClicked, this,
- &ProjectExplorer::item_double_clicked_slot);
- connect(view->selectionModel(), &QItemSelectionModel::selectionChanged,
- this, &ProjectExplorer::view_selection_changed);
- connect(view, SIGNAL(double_clicked_empty_area()), this,
- SLOT(view_empty_area_double_clicked_slot()));
- stacked_widget_->addWidget(view);
-}
-
-void ProjectExplorer::browse_to_folder(const QModelIndex &index)
-{
- // Set appropriate views to this index
- icon_view_->setRootIndex(index);
- list_view_->setRootIndex(index);
-
- // Set navbar text to folder's name
- update_nav_bar_text();
-
- // Set directory up enabled button based on whether we're in root or not
- nav_bar_->set_dir_up_enabled(index.isValid());
-}
-
-int ProjectExplorer::confirm_item_deletion(Node *item)
-{
- QMessageBox msgbox(this);
- msgbox.setWindowTitle(tr("Confirm Item Deletion"));
- msgbox.setIcon(QMessageBox::Warning);
-
- QStringList connected_nodes_names;
- foreach (const Node::OutputConnection &connected,
- item->output_connections()) {
- if (!dynamic_cast(connected.second.node())) {
- connected_nodes_names.append(
- get_human_readable_node_name(connected.second.node()));
- }
- }
-
- msgbox.setText(
- tr("The item \"%1\" is currently connected to the following nodes:\n\n"
- "%2\n\n"
- "Are you sure you wish to delete this footage?")
- .arg(get_human_readable_node_name(item),
- connected_nodes_names.join('\n')));
-
- // Set up buttons
- msgbox.addButton(QMessageBox::Yes);
- msgbox.addButton(QMessageBox::YesToAll);
- msgbox.addButton(QMessageBox::No);
- msgbox.addButton(QMessageBox::Cancel);
-
- // Run messagebox
- return msgbox.exec();
-}
-
-bool ProjectExplorer::delete_items_internal(const QVector &selected,
- bool &check_if_item_is_in_use,
- MultiUndoCommand *command)
-{
- for (int i = 0; i < selected.size(); i++) {
- // Delete sequences first
- Node *node = selected.at(i);
-
- bool can_delete_item = true;
-
- if (check_if_item_is_in_use) {
- foreach (const Node::OutputConnection &oc,
- node->output_connections()) {
- Folder *folder_test = dynamic_cast(oc.second.node());
- if (!folder_test) {
- // This sequence outputs to SOMETHING, confirm the user if they want to delete this
- int r = confirm_item_deletion(node);
-
- switch (r) {
- case QMessageBox::No:
- can_delete_item = false;
- break;
- case QMessageBox::Cancel:
- return false;
- case QMessageBox::YesToAll:
- check_if_item_is_in_use = false;
- break;
- }
- }
- }
- }
-
- if (can_delete_item) {
- Sequence *sequence = dynamic_cast(node);
- if (sequence &&
- Core::instance()->main_window()->is_sequence_open(sequence)) {
- command->add_child(new CloseSequenceCommand(sequence));
- }
-
- if (node->folder()) {
- command->add_child(
- new Folder::RemoveElementCommand(node->folder(), node));
- }
-
- command->add_child(
- new NodeRemoveWithExclusiveDependenciesAndDisconnect(node));
- }
- }
-
- return true;
-}
-
-QString ProjectExplorer::get_human_readable_node_name(Node *node)
-{
- if (node->get_label().isEmpty()) {
- return node->name();
- } else {
- return tr("%1 (%2)").arg(node->get_label(), node->name());
- }
-}
-
-void ProjectExplorer::update_nav_bar_text()
-{
- QString absolute;
-
- Folder *f = static_cast(
- sort_model_.mapToSource(list_view_->rootIndex()).internalPointer());
- while (f && f != project()->root()) {
- absolute.prepend(QStringLiteral("%1 / ").arg(f->get_label()));
- f = f->folder();
- }
-
- absolute.prepend(QStringLiteral("/ "));
-
- nav_bar_->set_text(absolute);
-}
-
-QAbstractItemView *ProjectExplorer::current_view() const
-{
- return static_cast(stacked_widget_->currentWidget());
-}
-
-void ProjectExplorer::view_empty_area_double_clicked_slot()
-{
- emit double_clicked_item(nullptr);
-}
-
-void ProjectExplorer::item_double_clicked_slot(const QModelIndex &index)
-{
- // Retrieve source item from index
- Node *i =
- static_cast(sort_model_.mapToSource(index).internalPointer());
-
- // If the item is a folder, browse to it
- if (dynamic_cast(i) &&
- (view_type() == ProjectToolbar::list_view ||
- view_type() == ProjectToolbar::icon_view)) {
- browse_to_folder(index);
- }
-
- // Emit a signal
- emit double_clicked_item(i);
-}
-
-void ProjectExplorer::size_changed_slot(int s)
-{
- icon_view_->setGridSize(QSize(s, s));
-
- list_view_->setIconSize(QSize(s, s));
-}
-
-void ProjectExplorer::dir_up_slot()
-{
- QModelIndex current_root = icon_view_->rootIndex();
-
- if (current_root.isValid()) {
- QModelIndex parent = current_root.parent();
-
- browse_to_folder(parent);
- }
-}
-
-void ProjectExplorer::rename_selected_item()
-{
- auto indexes = current_view()->selectionModel()->selectedRows();
- if (!indexes.empty()) {
- current_view()->edit(indexes.first());
- }
-}
-
-void ProjectExplorer::set_search_filter(const QString &s)
-{
- sort_model_.setFilterFixedString(s);
-}
-
-void ProjectExplorer::show_context_menu()
-{
- Menu menu;
- Menu new_menu;
-
- context_menu_items_ = selected_items();
-
- if (context_menu_items_.isEmpty()) {
- // Items to show if no items are selected
-
- // "New" menu
- new_menu.setTitle(tr("&New"));
- MenuShared::instance()->add_items_for_new_menu(&new_menu);
- menu.addMenu(&new_menu);
-
- // "Import" action
- QAction *import_action = menu.addAction(tr("&Import..."));
- connect(import_action, &QAction::triggered, Core::instance(),
- &Core::dialog_import_show);
- } else {
- // Actions to add when only one item is selected
- if (context_menu_items_.size() == 1) {
- Node *context_menu_item = context_menu_items_.first();
-
- if (dynamic_cast(context_menu_item)) {
- QAction *open_in_new_tab =
- menu.addAction(tr("Open in New Tab"));
- connect(open_in_new_tab, &QAction::triggered, this,
- &ProjectExplorer::open_context_menu_item_in_new_tab);
-
- QAction *open_in_new_window =
- menu.addAction(tr("Open in New Window"));
- connect(open_in_new_window, &QAction::triggered, this,
- &ProjectExplorer::open_context_menu_item_in_new_window);
-
- } else if (dynamic_cast(context_menu_item)) {
- QString reveal_text;
-
-#if defined(Q_OS_WINDOWS)
- reveal_text = tr("Reveal in Explorer");
-#elif defined(Q_OS_MAC)
- reveal_text = tr("Reveal in Finder");
-#else
- reveal_text = tr("Reveal in File Manager");
-#endif
-
- QAction *reveal_action = menu.addAction(reveal_text);
- connect(reveal_action, &QAction::triggered, this,
- &ProjectExplorer::reveal_selected_footage);
-
- QAction *replace_action = menu.addAction(tr("Replace Footage"));
- connect(replace_action, &QAction::triggered, this,
- &ProjectExplorer::replace_selected_footage);
- }
-
- menu.addSeparator();
- }
-
- bool all_items_are_footage = true;
- bool all_items_have_video_streams = true;
- bool all_items_are_footage_or_sequence = true;
-
- foreach (Node *i, context_menu_items_) {
- Footage *footage_cast_test = dynamic_cast(i);
- Sequence *sequence_cast_test = dynamic_cast(i);
-
- if (footage_cast_test &&
- !footage_cast_test->has_enabled_video_streams()) {
- all_items_have_video_streams = false;
- }
-
- if (!footage_cast_test) {
- all_items_are_footage = false;
- }
-
- if (!footage_cast_test && !sequence_cast_test) {
- all_items_are_footage_or_sequence = false;
- }
- }
-
- if (all_items_are_footage && all_items_have_video_streams) {
- const QVector proxy_footage =
- get_selected_proxy_footage(context_menu_items_);
-
- Menu *proxy_menu = new Menu(tr("Proxy"), &menu);
- menu.addMenu(proxy_menu);
-
- QAction *generate_proxy =
- proxy_menu->addAction(tr("Generate Proxy"));
- generate_proxy->setEnabled(!proxy_footage.isEmpty());
- connect(generate_proxy, &QAction::triggered, this,
- &ProjectExplorer::generate_proxies_for_selected_footage);
-
- QAction *use_proxy = proxy_menu->addAction(tr("Use Proxy"));
- use_proxy->setCheckable(true);
- use_proxy->setEnabled(!proxy_footage.isEmpty());
- use_proxy->setChecked(
- !proxy_footage.isEmpty() &&
- std::all_of(proxy_footage.cbegin(), proxy_footage.cend(),
- [](const Footage *footage) {
- return footage->proxy_enabled();
- }));
- connect(use_proxy, &QAction::triggered, this,
- &ProjectExplorer::set_selected_footage_proxy_enabled);
-
- QAction *reveal_proxy = proxy_menu->addAction(tr("Reveal Proxy"));
- reveal_proxy->setEnabled(
- std::any_of(proxy_footage.cbegin(), proxy_footage.cend(),
- [](const Footage *footage) {
- return !footage->proxy_path().isEmpty();
- }));
- connect(reveal_proxy, &QAction::triggered, this,
- &ProjectExplorer::reveal_proxy_for_selected_footage);
-
- QAction *delete_proxy = proxy_menu->addAction(tr("Delete Proxy"));
- delete_proxy->setEnabled(
- std::any_of(proxy_footage.cbegin(), proxy_footage.cend(),
- [](const Footage *footage) {
- return !footage->proxy_path().isEmpty();
- }));
- connect(delete_proxy, &QAction::triggered, this,
- &ProjectExplorer::delete_proxies_for_selected_footage);
-
- QAction *proxy_settings =
- proxy_menu->addAction(tr("Proxy Settings..."));
- connect(proxy_settings, &QAction::triggered, this,
- &ProjectExplorer::show_proxy_dialog_for_selected_footage);
- }
-
- Q_UNUSED(all_items_are_footage_or_sequence)
-
- if (context_menu_items_.size() == 1) {
- menu.addSeparator();
-
- auto rename_action = menu.addAction(tr("Rename"));
- connect(rename_action, &QAction::triggered, this,
- &ProjectExplorer::rename_selected_item);
- }
-
- auto delete_action = menu.addAction(tr("Delete"));
- connect(delete_action, &QAction::triggered, this,
- &ProjectExplorer::delete_selected);
-
- if (context_menu_items_.size() == 1) {
- menu.addSeparator();
-
- QAction *properties_action = menu.addAction(tr("P&roperties"));
- connect(properties_action, &QAction::triggered, this,
- &ProjectExplorer::show_item_properties_dialog);
- }
- }
-
- menu.exec(QCursor::pos());
-}
-
-void ProjectExplorer::show_item_properties_dialog()
-{
- Node *sel = context_menu_items_.first();
-
- // FIXME: Support for multiple items
- if (dynamic_cast(sel)) {
- FootagePropertiesDialog fpd(this, static_cast(sel));
- fpd.exec();
-
- } else if (dynamic_cast(sel)) {
- Core::instance()->label_nodes(context_menu_items_);
-
- } else if (dynamic_cast(sel)) {
- SequenceDialog sd(static_cast(sel),
- SequenceDialog::k_existing, this);
- sd.exec();
- }
-}
-
-void ProjectExplorer::reveal_selected_footage()
-{
- Footage *footage = static_cast(context_menu_items_.first());
-
-#if defined(Q_OS_WINDOWS)
- // Explorer
- QStringList args;
- args << "/select," << QDir::toNativeSeparators(footage->filename());
- QProcess::startDetached("explorer", args);
-#elif defined(Q_OS_MAC)
- QStringList args;
- args << "-e";
- args << "tell application \"Finder\"";
- args << "-e";
- args << "activate";
- args << "-e";
- args << "select POSIX file \"" + footage->filename() + "\"";
- args << "-e";
- args << "end tell";
- QProcess::startDetached("osascript", args);
-#else
- QDesktopServices::openUrl(QUrl::fromLocalFile(
- QFileInfo(footage->filename()).dir().absolutePath()));
-#endif
-}
-
-void ProjectExplorer::replace_selected_footage()
-{
- Footage *footage = static_cast(context_menu_items_.first());
-
- QString file =
- QFileDialog::getOpenFileName(this, tr("Replace Footage"), QString(),
- Core::footage_file_dialog_filter());
- if (!file.isEmpty()) {
- if (!Core::is_footage_extension_allowed(file)) {
- QMessageBox::warning(
- this, tr("Unsupported media"),
- tr("This file type is not allowed by the current media type "
- "filter."));
- return;
- }
-
- // Change the filename through the facade relink (reprobes the new
- // file and resets proxy/stream state); the label policy stays here.
- OakEngineFootage *facade_handle = oakengine_footage_borrow(
- reinterpret_cast(footage));
- const int relink_rc = oakengine_footage_relink(
- facade_handle, file.toUtf8().constData());
- oakengine_footage_free(facade_handle);
- if (relink_rc != OAKENGINE_OK) {
- char err[512];
- err[0] = '\0';
- oakengine_footage_last_error(err, sizeof(err));
- QMessageBox::warning(
- this, tr("Cannot replace footage"),
- err[0] ? QString::fromUtf8(err) :
- tr("The file could not be used as media."));
- return;
- }
-
- if (QFileInfo(footage->filename()).fileName() ==
- footage->get_label()) {
- // Footage label == filename, change label too
- oakengine_node_set_label(
- reinterpret_cast(footage),
- QFileInfo(file).fileName().toUtf8().constData());
- }
- }
-}
-
-void ProjectExplorer::open_context_menu_item_in_new_tab()
-{
- Core::instance()->main_window()->open_folder(
- static_cast(context_menu_items_.first()), false);
-}
-
-void ProjectExplorer::open_context_menu_item_in_new_window()
-{
- Core::instance()->main_window()->open_folder(
- static_cast(context_menu_items_.first()), true);
-}
-
-void ProjectExplorer::generate_proxies_for_selected_footage()
-{
- if (!project()) {
- qWarning() << "GenerateProxiesForSelectedFootage: no project";
- return;
- }
-
- const QVector footage =
- get_selected_proxy_footage(context_menu_items_);
- qDebug()
- << "GenerateProxiesForSelectedFootage: starting proxy generation for"
- << footage.size() << "footage item(s)";
- for (Footage *item : footage) {
- const VideoParams video = item->get_first_enabled_video_stream();
- if (!video.is_valid()) {
- qWarning()
- << "GenerateProxiesForSelectedFootage: skipping item with no valid video stream"
- << item->filename();
- continue;
- }
-
- // Queue one facade-backed task per footage item (same queueing
- // semantics as the old per-footage proxy tasks).
- oakengine_task_manager_add(
- reinterpret_cast(new FacadeProxyTask(item)));
- }
-}
-
-void ProjectExplorer::set_selected_footage_proxy_enabled(bool enabled)
-{
- const QVector footage =
- get_selected_proxy_footage(context_menu_items_);
- qDebug() << "ProjectExplorer::SetSelectedFootageProxyEnabled:" << enabled
- << "footage count=" << footage.size();
- for (Footage *item : footage) {
- if (item->proxy_path().isEmpty()) {
- qDebug()
- << " skipping item with empty proxy path" << item->filename();
- continue;
- }
-
- OakEngineFootage *handle = oakengine_footage_borrow(
- reinterpret_cast(item));
- oakengine_footage_proxy_set_enabled(handle, enabled ? 1 : 0);
- oakengine_footage_free(handle);
- // The facade call toggles the flag; cache invalidation for the UI
- // stays here.
- item->invalidate_all(Footage::k_filename_input);
- }
-}
-
-void ProjectExplorer::reveal_proxy_for_selected_footage()
-{
- const QVector footage =
- get_selected_proxy_footage(context_menu_items_);
- for (Footage *item : footage) {
- char proxy_path[4096];
- proxy_path[0] = '\0';
- OakEngineFootage *handle = oakengine_footage_borrow(
- reinterpret_cast(item));
- oakengine_footage_proxy_get_path(handle, proxy_path,
- sizeof(proxy_path));
- oakengine_footage_free(handle);
- if (proxy_path[0] == '\0') {
- continue;
- }
- const QString path = QString::fromUtf8(proxy_path);
-
-#if defined(Q_OS_WINDOWS)
- QStringList args;
- args << "/select," << QDir::toNativeSeparators(path);
- QProcess::startDetached(QStringLiteral("explorer"), args);
-#elif defined(Q_OS_MAC)
- QStringList args;
- args << "-e";
- args << "tell application \"Finder\"";
- args << "-e";
- args << "activate";
- args << "-e";
- args << "select POSIX file \"" + path + "\"";
- args << "-e";
- args << "end tell";
- QProcess::startDetached(QStringLiteral("osascript"), args);
-#else
- QDesktopServices::openUrl(QUrl::fromLocalFile(
- QFileInfo(path).dir().absolutePath()));
-#endif
- }
-}
-
-void ProjectExplorer::delete_proxies_for_selected_footage()
-{
- const QVector footage =
- get_selected_proxy_footage(context_menu_items_);
- for (Footage *item : footage) {
- if (item->proxy_path().isEmpty()) {
- continue;
- }
-
- // Facade delete: removes the file, clears the proxy state and
- // invalidates the footage.
- OakEngineFootage *handle = oakengine_footage_borrow(
- reinterpret_cast(item));
- oakengine_footage_proxy_delete(handle);
- oakengine_footage_free(handle);
- }
-}
-
-void ProjectExplorer::show_proxy_dialog_for_selected_footage()
-{
- ProxyDialog d(this, get_selected_proxy_footage(context_menu_items_));
- d.exec();
-}
-
-void ProjectExplorer::view_selection_changed()
-{
- QItemSelectionModel *model = static_cast(sender());
-
- QModelIndexList selection = model->selectedIndexes();
-
- QVector nodes;
-
- foreach (const QModelIndex &index, selection) {
- Node *sel = static_cast(
- sort_model_.mapToSource(index).internalPointer());
- if (!nodes.contains(sel)) {
- nodes.append(sel);
- }
- }
-
- if (nodes.isEmpty()) {
- nodes.append(get_root());
- }
-
- emit selection_changed(nodes);
-}
-
-Project *ProjectExplorer::project() const
-{
- return model_.project();
-}
-
-void ProjectExplorer::set_project(Project *p)
-{
- model_.set_project(p);
-}
-
-Folder *ProjectExplorer::get_root() const
-{
- QModelIndex root_index = sort_model_.mapToSource(tree_view_->rootIndex());
-
- if (!root_index.isValid()) {
- return project()->root();
- }
-
- return static_cast(root_index.internalPointer());
-}
-
-void ProjectExplorer::set_root(Folder *item)
-{
- QModelIndex index =
- sort_model_.mapFromSource(model_.create_index_from_item(item));
-
- browse_to_folder(index);
- tree_view_->setRootIndex(index);
-}
-
-QVector ProjectExplorer::selected_items() const
-{
- // Determine which view is active and get its selected indexes
- QModelIndexList index_list =
- current_view()->selectionModel()->selectedRows();
-
- // Convert indexes to item objects
- QVector selected_items;
-
- for (int i = 0; i < index_list.size(); i++) {
- QModelIndex index = sort_model_.mapToSource(index_list.at(i));
-
- Node *item = static_cast(index.internalPointer());
-
- selected_items.append(item);
- }
-
- return selected_items;
-}
-
-Folder *ProjectExplorer::get_selected_folder() const
-{
- if (project() == nullptr) {
- return nullptr;
- }
-
- Folder *folder = nullptr;
-
- // Get the selected items from the panel
- QVector selected_nodes = selected_items();
-
- // Heuristic for finding the selected folder:
- //
- // - If `folder` is nullptr, we set the first folder we find. Either the item itself if it's a folder, or the
- // item's parent.
- // - Otherwise, if all folders found are the same, we'll use that to import into.
- // - If more than one folder is found, we play it safe and import into the root folder
-
- for (int i = 0; i < selected_nodes.size(); i++) {
- Node *sel_item = selected_nodes.at(i);
-
- // If this item is not a folder, presumably it's parent is
- if (!dynamic_cast(sel_item)) {
- sel_item = sel_item->folder();
- }
-
- if (folder == nullptr) {
- // If the folder is nullptr, cache it as this folder
- folder = static_cast(sel_item);
- } else if (folder != sel_item) {
- // If not, we've already cached a folder so we check if it's the same
- // If it isn't, we "play it safe" and use the root folder
- folder = nullptr;
- break;
- }
- }
-
- // If we didn't pick up a folder from the heuristic above for whatever reason, use root
- if (folder == nullptr) {
- folder = project()->root();
- }
-
- return folder;
-}
-
-ProjectViewModel *ProjectExplorer::model()
-{
- return &model_;
-}
-
-void ProjectExplorer::select_all()
-{
- current_view()->selectAll();
-}
-
-void ProjectExplorer::deselect_all()
-{
- current_view()->selectionModel()->clearSelection();
-}
-
-void ProjectExplorer::delete_selected()
-{
- QVector selected = selected_items();
-
- if (selected.isEmpty()) {
- return;
- }
-
- MultiUndoCommand *command = new MultiUndoCommand();
-
- bool check_if_item_is_in_use = true;
-
- if (delete_items_internal(selected, check_if_item_is_in_use, command)) {
- Core::instance()->undo_stack()->push(
- command, tr("Deleted %1 Item(s)").arg(selected.size()));
- } else {
- delete command;
- }
-}
-
-bool ProjectExplorer::select_item(Node *n, bool deselect_all_first)
-{
- if (deselect_all_first) {
- deselect_all();
- }
-
- QModelIndex index = model_.create_index_from_item(n);
-
- if (index.isValid()) {
- index = sort_model_.mapFromSource(index);
-
- QModelIndex parent = index.parent();
- if (view_type() == ProjectToolbar::tree_view) {
- // Expand all folders until this index is visible
- while (parent.isValid()) {
- tree_view_->expand(parent);
- parent = parent.parent();
- }
- } else {
- browse_to_folder(parent);
- }
-
- current_view()->selectionModel()->select(
- index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
-
- return true;
- }
-
- return false;
-}
-
-}
diff --git a/.bak/projectexplorer/projectexplorer.h b/.bak/projectexplorer/projectexplorer.h
deleted file mode 100644
index 4f79ceaf2..000000000
--- a/.bak/projectexplorer/projectexplorer.h
+++ /dev/null
@@ -1,208 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_PROJECTEXPLORER_H
-#define OAK_PROJECTEXPLORER_H
-
-#include
-#include
-#include
-#include
-
-#include "node/project.h"
-#include "projectviewmodel.h"
-#include "widget/projectexplorer/projectexplorericonview.h"
-#include "widget/projectexplorer/projectexplorerlistview.h"
-#include "widget/projectexplorer/projectexplorertreeview.h"
-#include "widget/projectexplorer/projectexplorernavigation.h"
-#include "widget/projecttoolbar/projecttoolbar.h"
-
-namespace olive
-{
-
-/**
- * @brief A widget for browsing through a Project structure.
- *
- * ProjectExplorer automatically handles the view<->model system using a ProjectViewModel. Therefore, all that needs to
- * be provided is the Project structure itself.
- *
- * This widget contains three views, tree view, list view, and icon view. These can be switched at any time.
- */
-class ProjectExplorer : public QWidget {
- Q_OBJECT
-public:
- ProjectExplorer(QWidget *parent);
-
- const ProjectToolbar::ViewType &view_type() const;
-
- Project *project() const;
- void set_project(Project *p);
-
- Folder *get_root() const;
- void set_root(Folder *item);
-
- QVector selected_items() const;
-
- /**
- * @brief Use a heuristic to determine which (if any) folder is selected
- *
- * Generally for some import/adding processes, we assume that if a folder is selected, the user probably wants to
- * create the new object in it rather than in the root. If, however, more than one folder is selected, we can't
- * truly determine any folder from this and just return the root instead.
- *
- * @return
- *
- * A folder that's heuristically been determined as "selected", or the root directory if none, or nullptr if no
- * project is open.
- */
- Folder *get_selected_folder() const;
-
- /**
- * @brief Access the ViewModel model of the project
- */
- ProjectViewModel *model();
-
- void select_all();
-
- void deselect_all();
-
- void delete_selected();
-
- bool select_item(Node *n, bool deselect_all_first = true);
-
-public slots:
- void set_view_type(ProjectToolbar::ViewType type);
-
- void edit(Node *item);
-
- void rename_selected_item();
-
- void set_search_filter(const QString &s);
-
-signals:
- /**
- * @brief Emitted when an Item is double clicked
- *
- * @param item
- *
- * The Item that was double clicked, or nullptr if empty area was double clicked
- */
- void double_clicked_item(Node *item);
-
- void selection_changed(const QVector &selected);
-
-private:
- /**
- * @brief Get all the blocks that solely rely on an input node
- *
- * Ignores blocks that depend on multiple inputs
- */
- QList get_footage_blocks(QList nodes);
-
- /**
- * @brief Simple convenience function for adding a view to this stacked widget
- *
- * Mainly for use in the constructor. Adds the view, connects its signals/slots, and sets the model.
- *
- * @param view
- *
- * View to add to the stack
- */
- void add_view(QAbstractItemView *view);
-
- /**
- * @brief Browse to a specific folder index in the model
- *
- * Only affects list_view_ and icon_view_.
- *
- * @param index
- *
- * Either an invalid index to return to the project root, or an index to a valid Folder object.
- */
- void browse_to_folder(const QModelIndex &index);
-
- int confirm_item_deletion(Node *item);
-
- bool delete_items_internal(const QVector &selected,
- bool &check_if_item_is_in_use,
- MultiUndoCommand *command);
-
- static QString get_human_readable_node_name(Node *node);
-
- void update_nav_bar_text();
-
- /**
- * @brief Get the currently active QAbstractItemView
- */
- QAbstractItemView *current_view() const;
-
- QStackedWidget *stacked_widget_;
-
- ProjectExplorerNavigation *nav_bar_;
-
- ProjectExplorerIconView *icon_view_;
- ProjectExplorerListView *list_view_;
- ProjectExplorerTreeView *tree_view_;
-
- ProjectToolbar::ViewType view_type_;
-
- QSortFilterProxyModel sort_model_;
- ProjectViewModel model_;
-
- QVector context_menu_items_;
-
-private slots:
- void view_empty_area_double_clicked_slot();
-
- void item_double_clicked_slot(const QModelIndex &index);
-
- void size_changed_slot(int s);
-
- void dir_up_slot();
-
- void show_context_menu();
-
- void show_item_properties_dialog();
-
- void reveal_selected_footage();
-
- void replace_selected_footage();
-
- void open_context_menu_item_in_new_tab();
-
- void open_context_menu_item_in_new_window();
-
- void generate_proxies_for_selected_footage();
-
- void set_selected_footage_proxy_enabled(bool enabled);
-
- void reveal_proxy_for_selected_footage();
-
- void delete_proxies_for_selected_footage();
-
- void show_proxy_dialog_for_selected_footage();
-
- void view_selection_changed();
-};
-
-}
-
-#endif // OAK_PROJECTEXPLORER_H
diff --git a/.bak/projectexplorer/projectexplorericonview.cpp b/.bak/projectexplorer/projectexplorericonview.cpp
deleted file mode 100644
index 8bf040870..000000000
--- a/.bak/projectexplorer/projectexplorericonview.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "projectexplorericonview.h"
-
-namespace olive
-{
-
-ProjectExplorerIconView::ProjectExplorerIconView(QWidget *parent)
- : ProjectExplorerListViewBase(parent)
-{
- setViewMode(QListView::IconMode);
-
- setItemDelegate(&delegate_);
-}
-
-}
diff --git a/.bak/projectexplorer/projectexplorericonview.h b/.bak/projectexplorer/projectexplorericonview.h
deleted file mode 100644
index ce5871c78..000000000
--- a/.bak/projectexplorer/projectexplorericonview.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_PROJECTEXPLORERICONVIEW_H
-#define OAK_PROJECTEXPLORERICONVIEW_H
-
-#include "projectexplorerlistviewbase.h"
-#include "projectexplorericonviewitemdelegate.h"
-
-namespace olive
-{
-
-/**
- * @brief The view widget used when ProjectExplorer is in Icon View
- */
-class ProjectExplorerIconView : public ProjectExplorerListViewBase {
- Q_OBJECT
-public:
- ProjectExplorerIconView(QWidget *parent);
-
-private:
- ProjectExplorerIconViewItemDelegate delegate_;
-};
-
-}
-
-#endif // OAK_PROJECTEXPLORERICONVIEW_H
diff --git a/.bak/projectexplorer/projectexplorericonviewitemdelegate.cpp b/.bak/projectexplorer/projectexplorericonviewitemdelegate.cpp
deleted file mode 100644
index 692ddf4ce..000000000
--- a/.bak/projectexplorer/projectexplorericonviewitemdelegate.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "projectexplorericonviewitemdelegate.h"
-
-#include
-
-#include "common/qtutils.h"
-
-namespace olive
-{
-
-ProjectExplorerIconViewItemDelegate::ProjectExplorerIconViewItemDelegate(
- QObject *parent)
- : QStyledItemDelegate(parent)
-{
-}
-
-QSize ProjectExplorerIconViewItemDelegate::sizeHint(
- const QStyleOptionViewItem &option, const QModelIndex &) const
-{
- Q_UNUSED(option)
-
- return QSize(256, 256);
-}
-
-void ProjectExplorerIconViewItemDelegate::paint(
- QPainter *painter, const QStyleOptionViewItem &option,
- const QModelIndex &index) const
-{
- QFontMetrics fm = painter->fontMetrics();
- QRect img_rect = option.rect;
-
- // Draw Text
- if (fm.height() < option.rect.height() / 2) {
- img_rect.setHeight(img_rect.height() - fm.height());
-
- QRect text_rect = option.rect;
- text_rect.setTop(text_rect.top() + option.rect.height() - fm.height());
-
- QColor text_bgcolor;
- QColor text_fgcolor;
-
- if (option.state & QStyle::State_Selected) {
- text_bgcolor = option.palette.highlight().color();
- text_fgcolor = option.palette.highlightedText().color();
- } else {
- text_bgcolor = Qt::white;
- text_fgcolor = Qt::black;
- }
-
- painter->fillRect(text_rect, text_bgcolor);
- painter->setPen(text_fgcolor);
-
- QString duration_str = index.data(Qt::UserRole).toString();
-
- int timecode_width = QtUtils::q_font_metrics_width(fm, duration_str);
-
- int max_name_width = option.rect.width();
-
- if (timecode_width < option.rect.width() / 2) {
- painter->drawText(
- text_rect, static_cast(Qt::AlignBottom | Qt::AlignRight),
- index.data(Qt::UserRole).toString());
- max_name_width -= timecode_width;
- }
-
- painter->drawText(text_rect,
- static_cast(Qt::AlignBottom | Qt::AlignLeft),
- fm.elidedText(index.data(Qt::DisplayRole).toString(),
- Qt::ElideRight, max_name_width));
- }
-
- // Draw image
- QIcon ico = index.data(Qt::DecorationRole).value();
- QSize icon_size = ico.actualSize(img_rect.size());
- img_rect =
- QRect(img_rect.x() + (img_rect.width() / 2 - icon_size.width() / 2),
- img_rect.y() + (img_rect.height() / 2 - icon_size.height() / 2),
- icon_size.width(), icon_size.height());
- painter->drawPixmap(img_rect, ico.pixmap(icon_size));
-
- if (option.state & QStyle::State_Selected) {
- QColor highlight_color = option.palette.highlight().color();
- highlight_color.setAlphaF(0.5);
-
- painter->setCompositionMode(QPainter::CompositionMode_SourceAtop);
- painter->fillRect(img_rect, highlight_color);
- }
-}
-
-}
diff --git a/.bak/projectexplorer/projectexplorericonviewitemdelegate.h b/.bak/projectexplorer/projectexplorericonviewitemdelegate.h
deleted file mode 100644
index d7c2d01e3..000000000
--- a/.bak/projectexplorer/projectexplorericonviewitemdelegate.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_PROJECTEXPLORERICONVIEWITEMDELEGATE_H
-#define OAK_PROJECTEXPLORERICONVIEWITEMDELEGATE_H
-
-#include
-
-#include "common/define.h"
-
-namespace olive
-{
-
-/**
- * @brief The delegate that's used to draw items when ProjectExplorer is in Icon view
- */
-class ProjectExplorerIconViewItemDelegate : public QStyledItemDelegate {
-public:
- ProjectExplorerIconViewItemDelegate(QObject *parent = nullptr);
-
- virtual QSize sizeHint(const QStyleOptionViewItem &option,
- const QModelIndex &index) const override;
- virtual void paint(QPainter *painter, const QStyleOptionViewItem &option,
- const QModelIndex &index) const override;
-};
-
-}
-
-#endif // OAK_PROJECTEXPLORERICONVIEWITEMDELEGATE_H
diff --git a/.bak/projectexplorer/projectexplorerlistview.cpp b/.bak/projectexplorer/projectexplorerlistview.cpp
deleted file mode 100644
index b8574a1e5..000000000
--- a/.bak/projectexplorer/projectexplorerlistview.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "projectexplorerlistview.h"
-
-namespace olive
-{
-
-ProjectExplorerListView::ProjectExplorerListView(QWidget *parent)
- : ProjectExplorerListViewBase(parent)
-{
- setViewMode(QListView::ListMode);
-
- setItemDelegate(&delegate_);
-}
-
-}
diff --git a/.bak/projectexplorer/projectexplorerlistview.h b/.bak/projectexplorer/projectexplorerlistview.h
deleted file mode 100644
index 2a34429f8..000000000
--- a/.bak/projectexplorer/projectexplorerlistview.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_PROJECTEXPLORERLISTVIEW_H
-#define OAK_PROJECTEXPLORERLISTVIEW_H
-
-#include "projectexplorerlistviewbase.h"
-#include "projectexplorerlistviewitemdelegate.h"
-
-namespace olive
-{
-
-/**
- * @brief The view widget used when ProjectExplorer is in List View
- */
-class ProjectExplorerListView : public ProjectExplorerListViewBase {
- Q_OBJECT
-public:
- ProjectExplorerListView(QWidget *parent);
-
-private:
- ProjectExplorerListViewItemDelegate delegate_;
-};
-
-}
-
-#endif // OAK_PROJECTEXPLORERLISTVIEW_H
diff --git a/.bak/projectexplorer/projectexplorerlistviewbase.cpp b/.bak/projectexplorer/projectexplorerlistviewbase.cpp
deleted file mode 100644
index 848528713..000000000
--- a/.bak/projectexplorer/projectexplorerlistviewbase.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "projectexplorerlistviewbase.h"
-
-#include
-
-namespace olive
-{
-
-ProjectExplorerListViewBase::ProjectExplorerListViewBase(QWidget *parent)
- : QListView(parent)
-{
- // FIXME Is this necessary?
- setMovement(QListView::Free);
-
- // Set selection mode (allows multiple item selection)
- setSelectionMode(QAbstractItemView::ExtendedSelection);
-
- // Set resize mode
- setResizeMode(QListView::Adjust);
-
- // Set widget to emit a signal on right click
- setContextMenuPolicy(Qt::CustomContextMenu);
-}
-
-void ProjectExplorerListViewBase::mouseDoubleClickEvent(QMouseEvent *event)
-{
- // Cache here so if the index becomes invalid after the base call, we still know the truth
- bool item_at_location = indexAt(event->pos()).isValid();
-
- // Perform default double click functions
- QListView::mouseDoubleClickEvent(event);
-
- // QAbstractItemView already has a doubleClicked() signal, but we emit another here for double clicking empty space
- if (!item_at_location) {
- emit double_clicked_empty_area();
- }
-}
-
-}
diff --git a/.bak/projectexplorer/projectexplorerlistviewbase.h b/.bak/projectexplorer/projectexplorerlistviewbase.h
deleted file mode 100644
index 2169b573a..000000000
--- a/.bak/projectexplorer/projectexplorerlistviewbase.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_PROJECTEXPLORERLISTVIEWBASE_H
-#define OAK_PROJECTEXPLORERLISTVIEWBASE_H
-
-#include
-
-#include "common/define.h"
-
-namespace olive
-{
-
-/**
- * @brief A QListView derivative that contains functionality used by both List view and Icon view (which are both based
- * on QListView)
- */
-class ProjectExplorerListViewBase : public QListView {
- Q_OBJECT
-public:
- ProjectExplorerListViewBase(QWidget *parent);
-
-protected:
- /**
- * @brief Double click event override
- *
- * Function that signals DoubleClickedView().
- *
- * FIXME: This code is the same as the code in ProjectExplorerTreeView. Is there a way to merge these two through
- * subclassing?
- */
- virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
-
-signals:
- /**
- * @brief Unconditional double click signal
- *
- * Emits a signal when the view is double clicked but not on any particular item
- */
- void double_clicked_empty_area();
-};
-
-}
-
-#endif // OAK_PROJECTEXPLORERLISTVIEWBASE_H
diff --git a/.bak/projectexplorer/projectexplorerlistviewitemdelegate.cpp b/.bak/projectexplorer/projectexplorerlistviewitemdelegate.cpp
deleted file mode 100644
index fa2d243a0..000000000
--- a/.bak/projectexplorer/projectexplorerlistviewitemdelegate.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "projectexplorerlistviewitemdelegate.h"
-
-#include
-
-namespace olive
-{
-
-ProjectExplorerListViewItemDelegate::ProjectExplorerListViewItemDelegate(
- QObject *parent)
- : QStyledItemDelegate(parent)
-{
-}
-
-QSize ProjectExplorerListViewItemDelegate::sizeHint(
- const QStyleOptionViewItem &option, const QModelIndex &) const
-{
- return QSize(option.decorationSize.height(),
- option.decorationSize.height());
-}
-
-void ProjectExplorerListViewItemDelegate::paint(
- QPainter *painter, const QStyleOptionViewItem &option,
- const QModelIndex &index) const
-{
- QFontMetrics fm = painter->fontMetrics();
- QRect img_rect = option.rect;
-
- if (option.state & QStyle::State_Selected) {
- painter->fillRect(option.rect, option.palette.highlight());
- }
-
- img_rect.setWidth(qMin(img_rect.width(), img_rect.height()));
-
- QIcon ico = index.data(Qt::DecorationRole).value();
- QSize icon_size = ico.actualSize(img_rect.size());
- img_rect =
- QRect(img_rect.x() + (img_rect.width() / 2 - icon_size.width() / 2),
- img_rect.y() + (img_rect.height() / 2 - icon_size.height() / 2),
- icon_size.width(), icon_size.height());
- painter->drawPixmap(img_rect, ico.pixmap(icon_size));
-
- QRect text_rect = option.rect;
- text_rect.setLeft(text_rect.left() + option.rect.height());
-
- int maximum_line_count = qMax(1, option.rect.height() / fm.height() - 1);
- QString text;
- if (maximum_line_count == 1) {
- text = index.data(Qt::DisplayRole).toString();
- } else {
- text = index.data(Qt::ToolTipRole).toString();
- if (text.isEmpty()) {
- text = index.data(Qt::DisplayRole).toString();
- } else {
- QStringList strings = text.split("\n");
- while (strings.size() > maximum_line_count) {
- strings.removeLast();
- }
- text = strings.join("\n");
- }
- }
-
- painter->setPen(option.state & QStyle::State_Selected ?
- option.palette.highlightedText().color() :
- option.palette.text().color());
-
- painter->drawText(text_rect,
- static_cast(Qt::AlignLeft | Qt::AlignVCenter), text);
-}
-
-}
diff --git a/.bak/projectexplorer/projectexplorerlistviewitemdelegate.h b/.bak/projectexplorer/projectexplorerlistviewitemdelegate.h
deleted file mode 100644
index 7297fdf33..000000000
--- a/.bak/projectexplorer/projectexplorerlistviewitemdelegate.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_PROJECTEXPLORERLISTVIEWITEMDELEGATE_H
-#define OAK_PROJECTEXPLORERLISTVIEWITEMDELEGATE_H
-
-#include
-
-#include "common/define.h"
-
-namespace olive
-{
-
-/**
- * @brief The delegate that's used to draw items when ProjectExplorer is in List view
- */
-class ProjectExplorerListViewItemDelegate : public QStyledItemDelegate {
-public:
- ProjectExplorerListViewItemDelegate(QObject *parent = nullptr);
-
- virtual QSize sizeHint(const QStyleOptionViewItem &option,
- const QModelIndex &index) const override;
- virtual void paint(QPainter *painter, const QStyleOptionViewItem &option,
- const QModelIndex &index) const override;
-};
-
-}
-
-#endif // OAK_PROJECTEXPLORERLISTVIEWITEMDELEGATE_H
diff --git a/.bak/projectexplorer/projectexplorernavigation.cpp b/.bak/projectexplorer/projectexplorernavigation.cpp
deleted file mode 100644
index ea7b90d7f..000000000
--- a/.bak/projectexplorer/projectexplorernavigation.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "projectexplorernavigation.h"
-
-#include
-#include
-
-#include "common/define.h"
-#include "ui/icons/icons.h"
-
-namespace olive
-{
-
-ProjectExplorerNavigation::ProjectExplorerNavigation(QWidget *parent)
- : QWidget(parent)
-{
- // Create widget layout
- QHBoxLayout *layout = new QHBoxLayout(this);
- layout->setContentsMargins(0, 0, 0, 0);
-
- // Create "directory up" button
- dir_up_btn_ = new QPushButton(this);
- dir_up_btn_->setEnabled(false);
- dir_up_btn_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
- layout->addWidget(dir_up_btn_);
- connect(dir_up_btn_, SIGNAL(clicked(bool)), this,
- SIGNAL(directory_up_clicked()));
-
- // Create directory tree label
- dir_lbl_ = new QLabel(this);
- dir_lbl_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
- layout->addWidget(dir_lbl_);
-
- // Create size slider
- size_slider_ = new QSlider(this);
- size_slider_->setOrientation(Qt::Horizontal);
- size_slider_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
- layout->addWidget(size_slider_);
- connect(size_slider_, SIGNAL(valueChanged(int)), this,
- SIGNAL(size_changed(int)));
-
- retranslate();
- update_icons();
-}
-
-void ProjectExplorerNavigation::set_text(const QString &s)
-{
- dir_lbl_->setText(s);
-}
-
-void ProjectExplorerNavigation::set_dir_up_enabled(bool e)
-{
- dir_up_btn_->setEnabled(e);
-}
-
-void ProjectExplorerNavigation::set_size_value(int s)
-{
- size_slider_->setValue(s);
-}
-
-void ProjectExplorerNavigation::changeEvent(QEvent *e)
-{
- if (e->type() == QEvent::LanguageChange) {
- retranslate();
- } else if (e->type() == QEvent::StyleChange) {
- update_icons();
- }
- QWidget::changeEvent(e);
-}
-
-void ProjectExplorerNavigation::retranslate()
-{
- dir_up_btn_->setToolTip(tr("Go to parent folder"));
-}
-
-void ProjectExplorerNavigation::update_icons()
-{
- dir_up_btn_->setIcon(icon::dir_up);
- size_slider_->setMinimum(k_project_icon_size_minimum);
- size_slider_->setMaximum(k_project_icon_size_maximum);
- size_slider_->setValue(k_project_icon_size_default);
-}
-
-}
diff --git a/.bak/projectexplorer/projectexplorernavigation.h b/.bak/projectexplorer/projectexplorernavigation.h
deleted file mode 100644
index 4bf12fa30..000000000
--- a/.bak/projectexplorer/projectexplorernavigation.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_PROJECTEXPLORERLISTVIEWTOOLBAR_H
-#define OAK_PROJECTEXPLORERLISTVIEWTOOLBAR_H
-
-#include
-#include
-#include
-#include
-
-#include "common/define.h"
-
-namespace olive
-{
-
-/**
- * @brief A navigation bar widget for ProjectExplorer's Icon and List views
- *
- * Unlike the Tree view, Icon and List don't follow a hierarchical view of information. This means there is no direct
- * way of navigating in and out of folders in those view types. We solve this in two ways:
- *
- * * Double clicking a Folder in those views will enter that folder
- * * This navigation bar offers a "directory up" button for leaving a folder
- *
- * This navbar also provides an icon size slider for those views (between kProjectIconSizeMinimum and
- * kProjectIconSizeMaximum) as well as text that's intended to be set to the current Folder's name (or
- * empty for the root folder).
- *
- * This widget does not actually communicate to Project or ProjectExplorer classes. It is simply UI widgets that are
- * intended to be connected in ways that do. This is the primarily responsibility of ProjectExplorer.
- *
- * By default, the directory up button is disabled (assuming root folder), the text is empty, and the icon size slider
- * is set to kProjectIconSizeDefault.
- */
-class ProjectExplorerNavigation : public QWidget {
- Q_OBJECT
-public:
- ProjectExplorerNavigation(QWidget *parent);
-
- /**
- * @brief Sets the text string
- *
- * This text is intended to be set to the current Folder's name
- *
- * @param s
- */
- void set_text(const QString &s);
-
- /**
- * @brief Set whether the "directory up" button is enabled or not
- *
- * @param e
- */
- void set_dir_up_enabled(bool e);
-
- /**
- * @brief Set the current value of the size slider
- *
- * NOTE: Does NOT emit SizeChanged().
- *
- * @param s
- *
- * New size value to set to
- */
- void set_size_value(int s);
-
-signals:
- /**
- * @brief Signal emitted when the directory up button is clicked
- */
- void directory_up_clicked();
-
- /**
- * @brief Signal emitted when the icon size slider changes value
- *
- * @param size
- *
- * New size set in the slider
- */
- void size_changed(int size);
-
-protected:
- virtual void changeEvent(QEvent *) override;
-
-private:
- void retranslate();
-
- void update_icons();
-
- QPushButton *dir_up_btn_;
-
- QLabel *dir_lbl_;
-
- QSlider *size_slider_;
-};
-
-}
-
-#endif // OAK_PROJECTEXPLORERLISTVIEWTOOLBAR_H
diff --git a/.bak/projectexplorer/projectexplorertreeview.cpp b/.bak/projectexplorer/projectexplorertreeview.cpp
deleted file mode 100644
index 76ae834dc..000000000
--- a/.bak/projectexplorer/projectexplorertreeview.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "projectexplorertreeview.h"
-
-#include
-
-namespace olive
-{
-
-ProjectExplorerTreeView::ProjectExplorerTreeView(QWidget *parent)
- : QTreeView(parent)
-{
- // Set selection mode (allows multiple item selection)
- setSelectionMode(QAbstractItemView::ExtendedSelection);
-
- // Allow dragging and dropping
- setDragDropMode(QAbstractItemView::DragDrop);
-
- // Enable dragging
- setDragEnabled(true);
-
- // Allow dropping from external sources
- setAcceptDrops(true);
-
- // Set context menu to emit a signal
- setContextMenuPolicy(Qt::CustomContextMenu);
-}
-
-void ProjectExplorerTreeView::mouseDoubleClickEvent(QMouseEvent *event)
-{
- // Perform default double click functions
- QTreeView::mouseDoubleClickEvent(event);
-
- // QAbstractItemView already has a doubleClicked() signal, but we emit another here for double clicking empty space
- if (!indexAt(event->pos()).isValid()) {
- emit double_clicked_empty_area();
- }
-}
-
-}
diff --git a/.bak/projectexplorer/projectexplorertreeview.h b/.bak/projectexplorer/projectexplorertreeview.h
deleted file mode 100644
index a334c36d5..000000000
--- a/.bak/projectexplorer/projectexplorertreeview.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_PROJECTEXPLORERTREEVIEW_H
-#define OAK_PROJECTEXPLORERTREEVIEW_H
-
-#include
-
-#include "common/define.h"
-
-namespace olive
-{
-
-/**
- * @brief The view widget used when ProjectExplorer is in Tree View
- *
- * A fairly simple subclass of QTreeView that provides a double clicked signal whether the index is valid or not
- * (QAbstractItemView has a doubleClicked() signal but it's only emitted with a valid index).
- */
-class ProjectExplorerTreeView : public QTreeView {
- Q_OBJECT
-public:
- ProjectExplorerTreeView(QWidget *parent);
-
-protected:
- /**
- * @brief Double click event override
- *
- * Function that signals DoubleClickedView().
- *
- * FIXME: This code is the same as the code in ProjectExplorerListViewBase. Is there a way to merge these two through
- *
- */
- virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
-
-signals:
- /**
- * @brief Unconditional double click signal
- *
- * Emits a signal when the view is double clicked but not on any particular item
- */
- void double_clicked_empty_area();
-};
-
-}
-
-#endif // OAK_PROJECTEXPLORERTREEVIEW_H
diff --git a/.bak/projectexplorer/projectexplorerundo.h b/.bak/projectexplorer/projectexplorerundo.h
deleted file mode 100644
index 0dc337a2d..000000000
--- a/.bak/projectexplorer/projectexplorerundo.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 OAK_PROJECTEXPLORERUNDO_H
-#define OAK_PROJECTEXPLORERUNDO_H
-
-#include "undo/undocommand.h"
-
-namespace olive
-{
-
-}
-
-#endif // OAK_PROJECTEXPLORERUNDO_H
diff --git a/.bak/projectexplorer/projectviewmodel.cpp b/.bak/projectexplorer/projectviewmodel.cpp
deleted file mode 100644
index 3b6e6c19e..000000000
--- a/.bak/projectexplorer/projectviewmodel.cpp
+++ /dev/null
@@ -1,573 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2022 Olive Team
- Modifications Copyright (C) 2025 mikesolar
-
- 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 "projectviewmodel.h"
-#include "ui/icons/icons.h"
-
-#include
-#include
-#include
-
-#include "common/qtutils.h"
-#include "core.h"
-#include "node/nodeundo.h"
-
-namespace olive
-{
-
-ProjectViewModel::ProjectViewModel(QObject *parent)
- : QAbstractItemModel(parent)
- , project_(nullptr)
-{
-}
-
-Project *ProjectViewModel::project() const
-{
- return project_;
-}
-
-void ProjectViewModel::set_project(Project *p)
-{
- beginResetModel();
-
- if (project_) {
- disconnect_item(project_->root());
- }
-
- project_ = p;
-
- if (project_) {
- connect_item(project_->root());
- }
-
- endResetModel();
-}
-
-QModelIndex ProjectViewModel::index(int row, int column,
- const QModelIndex &parent) const
-{
- // I'm actually not 100% sure what this does, but it seems logical and was in the earlier code
- if (!hasIndex(row, column, parent)) {
- return QModelIndex();
- }
-
- // Get the parent object, we assume it's a folder since only folders can have children
- Folder *item_parent = static_cast(get_item_object_from_index(parent));
-
- // Return an index to this object
- return createIndex(row, column, item_parent->item_child(row));
-}
-
-QModelIndex ProjectViewModel::parent(const QModelIndex &child) const
-{
- // Get the Item object from the index
- Node *item = get_item_object_from_index(child);
-
- // Get Item's parent object
- Folder *par = item->folder();
-
- // If the parent is the root, return an empty index
- if (par == project_->root()) {
- return QModelIndex();
- }
-
- // Otherwise return a true index to its parent
- int parent_index = index_of_child(par);
-
- // Make sure the index is valid (there's no reason it shouldn't be)
- Q_ASSERT(parent_index > -1);
-
- // Return an index to the parent
- return createIndex(parent_index, 0, par);
-}
-
-int ProjectViewModel::rowCount(const QModelIndex &parent) const
-{
- // If there's no project, there are obviously no items to show
- if (project_ == nullptr) {
- return 0;
- }
-
- // If the index is the root, return the root child count
- if (parent == QModelIndex()) {
- return project_->root()->item_child_count();
- }
-
- // Otherwise, the index must contain a valid pointer, so we just return its child count
- return static_cast(get_item_object_from_index(parent))
- ->item_child_count();
-}
-
-int ProjectViewModel::columnCount(const QModelIndex &parent) const
-{
- Q_UNUSED(parent)
-
- // Not strictly necessary, but a decent visual cue that there's no project currently active
- if (project_ == nullptr) {
- return 0;
- }
-
- return k_column_count;
-}
-
-QVariant ProjectViewModel::data(const QModelIndex &index, int role) const
-{
- Node *internal_item = get_item_object_from_index(index);
-
- ColumnType column_type = static_cast(index.column());
-
- switch (role) {
- case Qt::DisplayRole:
- case k_inner_text_role: {
- // Standard text role
-
- switch (column_type) {
- case k_name:
- return internal_item->get_label();
- case k_duration:
- return internal_item->data(Node::duration);
- case k_rate:
- return internal_item->data(Node::frequency_rate);
- case k_last_modified:
- case k_created_time: {
- qint64 using_time =
- (column_type == k_last_modified) ?
- internal_item->data(Node::modified_time).toLongLong() :
- internal_item->data(Node::created_time).toLongLong();
-
- if (using_time == 0) {
- // 0 is the null value, return nothing
- break;
- }
-
- QVariant ret;
-
- if (role == k_inner_text_role) {
- // Use time value directly for correct sorting
- ret = using_time;
- } else {
- // Display role, format to a human readable string
- ret = QtUtils::get_formatted_date_time(
- QDateTime::fromSecsSinceEpoch(using_time));
- }
-
- return ret;
- }
- case k_column_count:
- break;
- }
- } break;
- case Qt::EditRole:
- if (column_type == k_name) {
- return internal_item->get_label();
- }
- break;
- case Qt::DecorationRole:
- // If this is the first column, return the Item's icon
- if (column_type == k_name) {
- return icon::from_name(internal_item->data(Node::icon).toString());
- }
- break;
- case Qt::ToolTipRole:
- return internal_item->data(Node::tooltip);
- }
-
- return QVariant();
-}
-
-QVariant ProjectViewModel::headerData(int section, Qt::Orientation orientation,
- int role) const
-{
- // Check if we need text data (DisplayRole) and orientation is horizontal
- // FIXME I'm not 100% sure what happens if the orientation is vertical/if that check is necessary
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
- ColumnType column_type = static_cast(section);
-
- // Return the name based on the column's current type
- switch (column_type) {
- case k_name:
- return tr("Name");
- case k_duration:
- return tr("Duration");
- case k_rate:
- return tr("Rate");
- case k_last_modified:
- return tr("Modified");
- case k_created_time:
- return tr("Created");
- case k_column_count:
- break;
- }
- }
-
- return QAbstractItemModel::headerData(section, orientation, role);
-}
-
-bool ProjectViewModel::hasChildren(const QModelIndex &parent) const
-{
- // If it's a folder, we always return TRUE in order to always show the "expand triangle" icon,
- // even when there are no "physical" children
- Node *item = get_item_object_from_index(parent);
-
- return dynamic_cast