/*** Olive - Non-Linear Video Editor Copyright (C) 2019 Olive Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ***/ #include "nodeio.h" #include #include #include #include "undo/undo.h" #include "undo/undostack.h" #include "timeline/clip.h" #include "timeline/sequence.h" #include "panels/panels.h" #include "panels/effectcontrols.h" #include "panels/viewer.h" #include "panels/grapheditor.h" #include "nodes/oldeffectnode.h" #include "ui/viewerwidget.h" #include "ui/keyframenavigator.h" #include "ui/clickablelabel.h" NodeIO::NodeIO(Node *parent, const QString &id, const QString &name, bool savable, bool keyframable) : QObject(parent), id_(id), name_(name), keyframable_(keyframable), keyframing_(false), savable_(savable), output_type_(olive::nodes::kInvalid) { Q_ASSERT(parent != nullptr); parent->AddParameter(this); } void NodeIO::AddField(EffectField *field) { field->setParent(this); connect(field, SIGNAL(Clicked()), this, SIGNAL(Clicked())); connect(field, SIGNAL(Changed()), this, SIGNAL(Changed())); fields_.append(field); } void NodeIO::AddAcceptedNodeInput(olive::nodes::DataType type) { Q_ASSERT(output_type_ == olive::nodes::kInvalid); accepted_inputs_.append(type); } void NodeIO::ConnectEdge(NodeIO *output, NodeIO *input) { // Make sure one is an output and one is an input Q_ASSERT(output->IsNodeInput() != input->IsNodeInput()); // Swap them if necessary if (input->IsNodeOutput()) { NodeIO* temp = output; output = input; input = temp; } // Inputs can only have one edge, so we disconnect it here if there is one if (!input->node_edges_.isEmpty()) { DisconnectEdge(input->node_edges_.first()); } NodeEdgePtr edge = std::make_shared(output, input); output->node_edges_.append(edge); input->node_edges_.append(edge); emit output->EdgesChanged(); } void NodeIO::DisconnectEdge(NodeEdgePtr edge) { NodeIO* output = edge->output(); NodeIO* input = edge->input(); output->node_edges_.removeAll(edge); input->node_edges_.removeAll(edge); emit output->EdgesChanged(); } QVector NodeIO::edges() { return node_edges_; } bool NodeIO::IsKeyframing() { return keyframing_; } void NodeIO::SetKeyframingInternal(bool b) { /* FIXME if (GetParentEffect()->type() != EFFECT_TYPE_TRANSITION) { keyframing_ = b; emit KeyframingSetChanged(keyframing_); } */ } bool NodeIO::IsSavable() { return savable_; } bool NodeIO::IsKeyframable() { return keyframable_; } QVariant NodeIO::GetValue() { return GetValueAt(0); } QVariant NodeIO::GetValueAt(double timecode) { if (FieldCount() == 0) { return data_; } Q_ASSERT(FieldCount() == 1); return Field(0)->GetValueAt(timecode); } void NodeIO::SetValue(const QVariant &value) { SetValueAt(0, value); } void NodeIO::SetValueAt(double timecode, const QVariant &value) { if (FieldCount() == 0) { data_ = value; return; } Q_ASSERT(FieldCount() == 1); Field(0)->SetValueAt(timecode, value); } void NodeIO::SetEnabled(bool enabled) { for (int i=0;iSetEnabled(enabled); } } void NodeIO::SetOutputDataType(olive::nodes::DataType type) { Q_ASSERT(accepted_inputs_.isEmpty()); output_type_ = type; } bool NodeIO::CanAcceptDataType(olive::nodes::DataType type) { if (!IsNodeInput()) { return false; } return accepted_inputs_.contains(type); } olive::nodes::DataType NodeIO::OutputDataType() { return output_type_; } bool NodeIO::IsNodeInput() { return !accepted_inputs_.isEmpty(); } bool NodeIO::IsNodeOutput() { return output_type_ != olive::nodes::kInvalid; } void NodeIO::SetKeyframingEnabled(bool enabled) { if (enabled == keyframing_) { return; } if (enabled) { ComboAction* ca = new ComboAction(); // Enable keyframing setting on this row ca->append(new SetIsKeyframing(this, true)); // Prepare each field's data to start keyframing for (int i=0;iPrepareDataForKeyframing(true, ca); } olive::undo_stack.push(ca); update_ui(false); } else { // Confirm with the user whether they really want to disable keyframing if (QMessageBox::question(panel_effect_controls, tr("Disable Keyframes"), tr("Disabling keyframes will delete all current keyframes. " "Are you sure you want to do this?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { ComboAction* ca = new ComboAction(); // Prepare each field's data to stop keyframing for (int i=0;iPrepareDataForKeyframing(false, ca); } // Disable keyframing setting on this row ca->append(new SetIsKeyframing(this, false)); olive::undo_stack.push(ca); update_ui(false); } else { SetKeyframingInternal(true); } } } void NodeIO::GoToPreviousKeyframe() { /* FIXME long key = LONG_MIN; Clip* c = GetParentEffect()->parent_clip; long sequence_playhead = c->track()->sequence()->playhead; // Used to convert clip frame number to sequence frame number long time_adjustment = c->timeline_in() - c->clip_in(); // Loop through all of this row's fields for (int i=0;ikeyframes.size();j++) { long comp = f->keyframes.at(j).time + time_adjustment; // Get the closest keyframe if (comp < sequence_playhead) { key = qMax(comp, key); } } } // If we found a keyframe less than the playhead, jump to it if (key != LONG_MIN) panel_sequence_viewer->seek(key); */ } void NodeIO::ToggleKeyframe() { /* FIXME Clip* c = GetParentEffect()->parent_clip; long sequence_playhead = c->track()->sequence()->playhead; // Used to convert clip frame number to sequence frame number long time_adjustment = c->timeline_in() - c->clip_in(); QVector key_fields; QVector key_field_index; // See if any keyframes on any fields are at the current time for (int j=0;jkeyframes.size();i++) { long comp = f->keyframes.at(i).time + time_adjustment; if (comp == sequence_playhead) { // Cache the keyframes if they are at the current time key_fields.append(f); key_field_index.append(i); } } } ComboAction* ca = new ComboAction(); if (key_fields.isEmpty()) { // If we didn't find any current keyframes, create one for each field SetKeyframeOnAllFields(ca); } else { // If we DID find keyframes at this time, delete them QVector sorted_key_fields; QVector sorted_key_field_index; // Since QVectors shift themselves when removing items, we need to sort these in reverse order for (int i=0;iappend(new KeyframeDelete(sorted_key_fields.at(i), sorted_key_field_index.at(i))); } } olive::undo_stack.push(ca); update_ui(false); */ } void NodeIO::GoToNextKeyframe() { /* FIXME long key = LONG_MAX; Clip* c = GetParentEffect()->parent_clip; for (int i=0;ikeyframes.size();j++) { long comp = f->keyframes.at(j).time - c->clip_in() + c->timeline_in(); if (comp > c->track()->sequence()->playhead) { key = qMin(comp, key); } } } if (key != LONG_MAX) panel_sequence_viewer->seek(key); */ } void NodeIO::FocusRow() { panel_graph_editor->set_row(this); } Node* NodeIO::ParentNode() { return static_cast(parent()); } void NodeIO::SetKeyframeOnAllFields(ComboAction* ca) { for (int i=0;iSetValueAt(ParentNode()->Time(), field->GetValueAt(ParentNode()->Time())); kdc->SetNewKeyframes(); ca->append(kdc); } panel_effect_controls->update_keyframes(); } const QString &NodeIO::name() { return name_; } EffectField* NodeIO::Field(int i) { return fields_.at(i); } int NodeIO::FieldCount() { return fields_.size(); }