/*** 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 "nodetreeview.h" #include namespace olive { NodeTreeView::NodeTreeView(QWidget *parent) : QTreeWidget(parent) , only_show_keyframable_(false) , show_keyframe_tracks_as_rows_(false) , checkboxes_enabled_(false) { connect(this, &NodeTreeView::itemChanged, this, &NodeTreeView::ItemCheckStateChanged); connect(this, &NodeTreeView::itemSelectionChanged, this, &NodeTreeView::SelectionChanged); Retranslate(); } bool NodeTreeView::IsNodeEnabled(Node *n) const { return !disabled_nodes_.contains(n); } bool NodeTreeView::IsInputEnabled(const NodeKeyframeTrackReference &ref) const { return !disabled_inputs_.contains(ref); } void NodeTreeView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, const QColor &color) { // Insert into hashmap keyframe_colors_.insert(ref, color); // If we currently have an item for this, set it QTreeWidgetItem *item = item_map_.value(ref); if (item) { item->setForeground(0, color); } } void NodeTreeView::SetNodes(const QVector &nodes) { nodes_ = nodes; this->clear(); item_map_.clear(); foreach (Node *n, nodes_) { QTreeWidgetItem *node_item = new QTreeWidgetItem(); node_item->setText(0, n->Name()); if (checkboxes_enabled_) { node_item->setCheckState( 0, disabled_nodes_.contains(n) ? Qt::Unchecked : Qt::Checked); } node_item->setData(0, kItemType, kItemTypeNode); node_item->setData(0, kItemNodePointer, QtUtils::PtrToValue(n)); foreach (const QString &input, n->inputs()) { if (n->IsInputHidden(input) || (only_show_keyframable_ && !n->IsInputKeyframable(input))) { continue; } QTreeWidgetItem *input_item = nullptr; int arr_sz = n->InputArraySize(input); for (int i = -1; i < arr_sz; i++) { NodeInput input_ref(n, input, i); const QVector &key_tracks = n->GetKeyframeTracks(input_ref); int this_element_track; if (show_keyframe_tracks_as_rows_ && (key_tracks.size() == 1 || (i == -1 && n->InputIsArray(input)))) { this_element_track = 0; } else { this_element_track = -1; } QTreeWidgetItem *element_item; if (input_item) { element_item = CreateItem( input_item, NodeKeyframeTrackReference( input_ref, this_element_track)); } else { input_item = CreateItem(node_item, NodeKeyframeTrackReference( input_ref, this_element_track)); element_item = input_item; } if (show_keyframe_tracks_as_rows_ && key_tracks.size() > 1 && (!n->InputIsArray(input) || i >= 0)) { CreateItemsForTracks(element_item, input_ref, key_tracks.size()); } } } // Add at the end to prevent unnecessary signalling while we're setting these objects up if (node_item->childCount() > 0) { this->addTopLevelItem(node_item); } else { delete node_item; } } expandAll(); } void NodeTreeView::changeEvent(QEvent *e) { QTreeWidget::changeEvent(e); if (e->type() == QEvent::LanguageChange) { Retranslate(); } } void NodeTreeView::mouseDoubleClickEvent(QMouseEvent *e) { QTreeWidget::mouseDoubleClickEvent(e); NodeKeyframeTrackReference ref = GetSelectedInput(); if (ref.input().IsValid()) { emit InputDoubleClicked(ref); } } void NodeTreeView::Retranslate() { setHeaderLabel(tr("Nodes")); } NodeKeyframeTrackReference NodeTreeView::GetSelectedInput() { QList sel = selectedItems(); NodeKeyframeTrackReference selected_ref; if (!sel.isEmpty()) { QTreeWidgetItem *item = sel.first(); if (item->data(0, kItemType).toInt() == kItemTypeInput) { selected_ref = item->data(0, kItemInputReference) .value(); } else { selected_ref = NodeKeyframeTrackReference(NodeInput( QtUtils::ValueToPtr(item->data(0, kItemNodePointer)), QString())); } } return selected_ref; } QTreeWidgetItem *NodeTreeView::CreateItem(QTreeWidgetItem *parent, const NodeKeyframeTrackReference &ref) { QTreeWidgetItem *input_item = new QTreeWidgetItem(parent); QString item_name; if (ref.track() == -1 || NodeValue::get_number_of_keyframe_tracks(ref.input().GetDataType()) == 1 || (ref.input().IsArray() && ref.input().element() == -1)) { if (ref.input().element() == -1) { item_name = ref.input().name(); } else { item_name = QString::number(ref.input().element()); } } else { switch (ref.track()) { case 0: item_name = UseRGBAOverXYZW(ref) ? tr("R") : tr("X"); break; case 1: item_name = UseRGBAOverXYZW(ref) ? tr("G") : tr("Y"); break; case 2: item_name = UseRGBAOverXYZW(ref) ? tr("B") : tr("Z"); break; case 3: item_name = UseRGBAOverXYZW(ref) ? tr("A") : tr("W"); break; default: item_name = QString::number(ref.track()); } } input_item->setText(0, item_name); if (checkboxes_enabled_) { input_item->setCheckState( 0, disabled_inputs_.contains(ref) ? Qt::Unchecked : Qt::Checked); } input_item->setData(0, kItemType, kItemTypeInput); input_item->setData(0, kItemInputReference, QVariant::fromValue(ref)); if (keyframe_colors_.contains(ref)) { input_item->setForeground(0, keyframe_colors_.value(ref)); } item_map_.insert(ref, input_item); return input_item; } void NodeTreeView::CreateItemsForTracks(QTreeWidgetItem *parent, const NodeInput &input, int track_count) { for (int j = 0; j < track_count; j++) { CreateItem(parent, NodeKeyframeTrackReference(input, j)); } } bool NodeTreeView::UseRGBAOverXYZW(const NodeKeyframeTrackReference &ref) { return ref.input().GetDataType() == NodeValue::kColor; } void NodeTreeView::ItemCheckStateChanged(QTreeWidgetItem *item, int column) { Q_UNUSED(column) switch (item->data(0, kItemType).toInt()) { case kItemTypeNode: { Node *n = QtUtils::ValueToPtr(item->data(0, kItemNodePointer)); if (item->checkState(0) == Qt::Checked) { if (disabled_nodes_.contains(n)) { disabled_nodes_.removeOne(n); emit NodeEnableChanged(n, true); } } else if (!disabled_nodes_.contains(n)) { disabled_nodes_.append(n); emit NodeEnableChanged(n, false); } break; } case kItemTypeInput: { NodeKeyframeTrackReference i = item->data(0, kItemInputReference) .value(); if (item->checkState(0) == Qt::Checked) { if (disabled_inputs_.contains(i)) { disabled_inputs_.removeOne(i); emit InputEnableChanged(i, true); } } else if (!disabled_inputs_.contains(i)) { disabled_inputs_.append(i); emit InputEnableChanged(i, false); } break; } } } void NodeTreeView::SelectionChanged() { emit InputSelectionChanged(GetSelectedInput()); } }