From 179e8d9014c23e518ef736d3d21837e6e07a1d08 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 15:47:25 -0700 Subject: [PATCH 01/12] audio: added null check Fixes common crash. --- app/render/audioplaybackcache.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/render/audioplaybackcache.cpp b/app/render/audioplaybackcache.cpp index 0f9a1183a..ed507581b 100644 --- a/app/render/audioplaybackcache.cpp +++ b/app/render/audioplaybackcache.cpp @@ -71,7 +71,7 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &v TimeRangeList ranges_we_validated; // Calculate buffer size per channel - qint64 buffer_size_per_channel = samples->sample_count() * params_.bytes_per_sample_per_channel(); + qint64 buffer_size_per_channel = samples ? samples->sample_count() * params_.bytes_per_sample_per_channel() : 0; // Write each valid range to the segments foreach (const TimeRange& r, valid_ranges) { @@ -103,9 +103,6 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &v // Determine how many bytes need to be written qint64 total_write_length = params_.time_to_bytes_per_channel(this_write_out_point - this_write_in_point); - // Retrieve data buffer - const char *a = reinterpret_cast(samples->data(i)); - // Determine how many bytes we actually have in the source buffer qint64 possible_write_length = qMin(qMax(qint64(0), buffer_size_per_channel - src_offset), total_write_length); @@ -114,7 +111,9 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &v // If we have source bytes to write, write them here if (possible_write_length > 0) { - seg_file.write(a + src_offset, possible_write_length); + // Assume `samples` is valid if we're here, or else `buffer_size_per_channel` and + // therefore `possible_write_length` will be 0. + seg_file.write(reinterpret_cast(samples->data(i)) + src_offset, possible_write_length); } if (possible_write_length < total_write_length) { From 9e183779f353855bcb05178f1595d2473dcd9075 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 15:50:25 -0700 Subject: [PATCH 02/12] timeline: null check on setting beam cursor Fixes crash --- app/widget/timelinewidget/view/timelineview.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index b1a3c364b..e07c981f6 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -589,6 +589,10 @@ void TimelineView::ConnectTrackList(TrackList *list) void TimelineView::SetBeamCursor(const TimelineCoordinate &coord) { + if (!connected_track_list_) { + return; + } + bool update_required = coord.GetTrack().type() == connected_track_list_->type() || cursor_coord_.GetTrack().type() == connected_track_list_->type(); From c127bebae4661b15ccfb3b3f20cfa122c5d18eff Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 17:08:42 -0700 Subject: [PATCH 03/12] renderer/viewer: more flexibility regarding alpha association --- app/render/renderer.cpp | 26 ++++++------------------ app/render/renderer.h | 18 ++++++++-------- app/render/renderprocessor.cpp | 14 +++++++++++-- app/widget/scope/scopebase/scopebase.cpp | 2 +- app/widget/viewer/viewerdisplay.cpp | 2 +- 5 files changed, 29 insertions(+), 33 deletions(-) diff --git a/app/render/renderer.cpp b/app/render/renderer.cpp index 3cc41aa2e..a27553fae 100644 --- a/app/render/renderer.cpp +++ b/app/render/renderer.cpp @@ -52,14 +52,14 @@ TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, const void *data, return CreateTexture(params, Texture::k2D, data, linesize); } -void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, bool source_is_premultiplied, Texture *destination, bool clear_destination, const QMatrix4x4 &matrix, const QMatrix4x4 &crop_matrix) +void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, Texture *destination, bool clear_destination, const QMatrix4x4 &matrix, const QMatrix4x4 &crop_matrix) { - BlitColorManagedInternal(color_processor, source, source_is_premultiplied, destination, destination->params(), clear_destination, matrix, crop_matrix); + BlitColorManagedInternal(color_processor, source, source_alpha_association, destination, destination->params(), clear_destination, matrix, crop_matrix); } -void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, bool source_is_premultiplied, VideoParams params, bool clear_destination, const QMatrix4x4& matrix, const QMatrix4x4 &crop_matrix) +void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, VideoParams params, bool clear_destination, const QMatrix4x4& matrix, const QMatrix4x4 &crop_matrix) { - BlitColorManagedInternal(color_processor, source, source_is_premultiplied, nullptr, params, clear_destination, matrix, crop_matrix); + BlitColorManagedInternal(color_processor, source, source_alpha_association, nullptr, params, clear_destination, matrix, crop_matrix); } TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms) @@ -257,7 +257,7 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo } void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, TexturePtr source, - bool source_is_premultiplied, Texture *destination, + AlphaAssociated source_alpha_association, Texture *destination, VideoParams params, bool clear_destination, const QMatrix4x4& matrix, const QMatrix4x4& crop_matrix) { @@ -271,21 +271,7 @@ void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, Textu job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(source))); job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix)); job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix.inverted())); - - AlphaAssociated associated; - if (source->channel_count() == VideoParams::kRGBAChannelCount) { - if (source_is_premultiplied) { - // De-assoc/re-assoc required for color management - associated = kAlphaAssociated; - } else { - // Just assoc at the end - associated = kAlphaUnassociated; - } - } else { - // No assoc/deassoc required - associated = kAlphaNone; - } - job.InsertValue(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, associated)); + job.InsertValue(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, source_alpha_association)); foreach (const ColorContext::LUT& l, color_ctx.lut3d_textures) { job.InsertValue(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture))); diff --git a/app/render/renderer.h b/app/render/renderer.h index 628212c13..977fc98ce 100644 --- a/app/render/renderer.h +++ b/app/render/renderer.h @@ -63,8 +63,14 @@ public: Blit(shader, job, nullptr, params, clear_destination); } - void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, bool source_is_premultiplied, Texture* destination, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4()); - void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, bool source_is_premultiplied, VideoParams params, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4()); + enum AlphaAssociated { + kAlphaNone, + kAlphaUnassociated, + kAlphaAssociated + }; + + void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, Texture* destination, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4()); + void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, VideoParams params, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4()); TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms); @@ -120,16 +126,10 @@ private: }; - enum AlphaAssociated { - kAlphaNone, - kAlphaUnassociated, - kAlphaAssociated - }; - bool GetColorContext(ColorProcessorPtr color_processor, ColorContext* ctx); void BlitColorManagedInternal(ColorProcessorPtr color_processor, TexturePtr source, - bool source_is_premultiplied, + AlphaAssociated source_alpha_association, Texture* destination, VideoParams params, bool clear_destination, const QMatrix4x4 &matrix, const QMatrix4x4 &crop_matrix); diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 47c981263..34a1b8760 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -95,7 +95,7 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time if (output_color_transform) { // Yes color transform, blit color managed - render_ctx_->BlitColorManaged(output_color_transform, texture, true, blit_tex.get(), true, matrix); + render_ctx_->BlitColorManaged(output_color_transform, texture, Renderer::kAlphaAssociated, blit_tex.get(), true, matrix); } else { // No color transform, just blit ShaderJob job; @@ -439,8 +439,18 @@ QVariant RenderProcessor::ProcessVideoFootage(const FootageJob &stream, const ra using_colorspace, color_manager->GetReferenceColorSpace()); + Renderer::AlphaAssociated alpha_assoc; + if (stream_data.channel_count() != VideoParams::kRGBAChannelCount + || stream_data.colorspace() == color_manager->GetReferenceColorSpace()) { + alpha_assoc = Renderer::kAlphaNone; + } else if (stream_data.premultiplied_alpha()) { + alpha_assoc = Renderer::kAlphaAssociated; + } else { + alpha_assoc = Renderer::kAlphaUnassociated; + } + render_ctx_->BlitColorManaged(processor, unmanaged_texture, - stream_data.premultiplied_alpha(), + alpha_assoc, value.get()); still_image_cache_->mutex()->lock(); diff --git a/app/widget/scope/scopebase/scopebase.cpp b/app/widget/scope/scopebase/scopebase.cpp index 2e6875872..8e6074e74 100644 --- a/app/widget/scope/scopebase/scopebase.cpp +++ b/app/widget/scope/scopebase/scopebase.cpp @@ -74,7 +74,7 @@ void ScopeBase::OnPaint() if (!managed_tex_ || !managed_tex_up_to_date_ || managed_tex_->params() != texture_->params()) { managed_tex_ = renderer()->CreateTexture(texture_->params()); - renderer()->BlitColorManaged(color_service(), texture_, true, managed_tex_.get()); + renderer()->BlitColorManaged(color_service(), texture_, Renderer::kAlphaNone, managed_tex_.get()); } DrawScope(managed_tex_, pipeline_); diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 46ab98c55..3bae651b8 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -390,7 +390,7 @@ void ViewerDisplayWidget::OnPaint() texture_to_draw = deinterlace_texture_; } - renderer()->BlitColorManaged(color_service(), texture_to_draw, true, device_params, false, + renderer()->BlitColorManaged(color_service(), texture_to_draw, Renderer::kAlphaNone, device_params, false, combined_matrix_flipped_, crop_matrix_); } } From d0e72583016639b8abf25994a109b8d27f03482c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 17:46:42 -0700 Subject: [PATCH 04/12] added welcome dialog --- app/config/config.cpp | 1 + app/core.cpp | 2 +- app/dialog/about/about.cpp | 69 ++++++++++++++++++++++++---- app/dialog/about/about.h | 10 +++- app/dialog/about/scrollinglabel.cpp | 2 +- app/window/mainwindow/mainwindow.cpp | 11 +++++ app/window/mainwindow/mainwindow.h | 2 + 7 files changed, 84 insertions(+), 13 deletions(-) diff --git a/app/config/config.cpp b/app/config/config.cpp index d5803a32d..688754693 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -95,6 +95,7 @@ void Config::SetDefaults() SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean, true); + SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("AutoCacheEnabled"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000); diff --git a/app/core.cpp b/app/core.cpp index 0938330af..dc41884a8 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -316,7 +316,7 @@ void Core::SetSnapping(const bool &b) void Core::DialogAboutShow() { - AboutDialog a(main_window_); + AboutDialog a(false, main_window_); a.exec(); } diff --git a/app/dialog/about/about.cpp b/app/dialog/about/about.cpp index 1f80589b8..3c13440fa 100644 --- a/app/dialog/about/about.cpp +++ b/app/dialog/about/about.cpp @@ -26,23 +26,33 @@ #include #include "common/qtutils.h" +#include "config/config.h" #include "patreon.h" #include "scrollinglabel.h" namespace olive { -AboutDialog::AboutDialog(QWidget *parent) : +AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) : QDialog(parent) { - setWindowTitle(tr("About %1").arg(QApplication::applicationName())); + if (welcome_dialog) { + setWindowTitle(tr("Welcome to %1").arg(QApplication::applicationName())); + } else { + setWindowTitle(tr("About %1").arg(QApplication::applicationName())); + } + + QFontMetrics fm = fontMetrics(); QVBoxLayout* layout = new QVBoxLayout(this); + layout->setMargin(fm.height()); - layout->addWidget(new QLabel()); + QHBoxLayout *horiz_layout = new QHBoxLayout(); + horiz_layout->setMargin(fm.height()); + horiz_layout->setSpacing(fm.height()*2); QLabel* icon = new QLabel(QStringLiteral("")); icon->setAlignment(Qt::AlignCenter); - layout->addWidget(icon); + horiz_layout->addWidget(icon); // Construct About text QLabel* label = @@ -58,18 +68,33 @@ AboutDialog::AboutDialog(QWidget *parent) : "This software is licensed under the GNU GPL Version 3."))); // Set text formatting - label->setAlignment(Qt::AlignCenter); + label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); label->setWordWrap(true); label->setOpenExternalLinks(true); label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - layout->addWidget(label); + horiz_layout->addWidget(label); + + layout->addLayout(horiz_layout); // Patrons where necessary if (!patrons.isEmpty()) { layout->addWidget(new QLabel()); + layout->addWidget(QtUtils::CreateHorizontalLine()); + layout->addWidget(new QLabel()); - QLabel* support_lbl = new QLabel(tr("Olive wouldn't be possible without the support of gracious " - "donations from Patreon:")); + QString opening_statement; + + if (welcome_dialog) { + opening_statement = tr("Olive relies on support from the community to continue its development."); + } else { + opening_statement = tr("Olive wouldn't be possible without the support of gracious donations from the following people."); + } + + QLabel* support_lbl = new QLabel(tr("%1 " + "If you like this project, please consider " + "donating or " + "pledging to " + "support its development.").arg(opening_statement)); support_lbl->setWordWrap(true); support_lbl->setAlignment(Qt::AlignCenter); support_lbl->setOpenExternalLinks(true); @@ -82,13 +107,37 @@ AboutDialog::AboutDialog(QWidget *parent) : layout->addWidget(new QLabel()); + QHBoxLayout *btn_layout = new QHBoxLayout(); + btn_layout->setMargin(0); + btn_layout->setSpacing(0); + + if (welcome_dialog) { + dont_show_again_checkbox_ = new QCheckBox(tr("Don't show this message again")); + btn_layout->addWidget(dont_show_again_checkbox_); + } else { + dont_show_again_checkbox_ = nullptr; + } + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this); - buttons->setCenterButtons(true); - layout->addWidget(buttons); + if (!welcome_dialog) { + buttons->setCenterButtons(true); + } + btn_layout->addWidget(buttons); + + layout->addLayout(btn_layout); connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); setFixedSize(sizeHint()); } +void AboutDialog::accept() +{ + if (dont_show_again_checkbox_ && dont_show_again_checkbox_->isChecked()) { + Config::Current()[QStringLiteral("ShowWelcomeDialog")] = false; + } + + QDialog::accept(); +} + } diff --git a/app/dialog/about/about.h b/app/dialog/about/about.h index 4fe89a6c2..cc6197f15 100644 --- a/app/dialog/about/about.h +++ b/app/dialog/about/about.h @@ -21,6 +21,7 @@ #ifndef ABOUTDIALOG_H #define ABOUTDIALOG_H +#include #include #include "common/define.h" @@ -46,7 +47,14 @@ public: * * QWidget parent object. Usually this will be MainWindow. */ - explicit AboutDialog(QWidget *parent = nullptr); + explicit AboutDialog(bool welcome_dialog, QWidget *parent = nullptr); + +public slots: + virtual void accept() override; + +private: + QCheckBox *dont_show_again_checkbox_; + }; } diff --git a/app/dialog/about/scrollinglabel.cpp b/app/dialog/about/scrollinglabel.cpp index 2fbc5d027..c7fb59b9d 100644 --- a/app/dialog/about/scrollinglabel.cpp +++ b/app/dialog/about/scrollinglabel.cpp @@ -26,7 +26,7 @@ namespace olive { -const int ScrollingLabel::kMinLineHeight = 5; +const int ScrollingLabel::kMinLineHeight = 10; ScrollingLabel::ScrollingLabel(QWidget *parent) : QWidget(parent), diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 7db76d1ba..7f49a1e89 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -29,6 +29,7 @@ #include #endif +#include "dialog/about/about.h" #include "mainmenu.h" #include "mainstatusbar.h" @@ -486,6 +487,14 @@ void MainWindow::ProjectPanelSelectionChanged(const QVector &nodes) } } +void MainWindow::ShowWelcomeDialog() +{ + if (Config::Current()[QStringLiteral("ShowWelcomeDialog")].toBool()) { + AboutDialog ad(true, this); + ad.exec(); + } +} + #ifdef Q_OS_LINUX void MainWindow::ShowNouveauWarning() { @@ -844,6 +853,8 @@ void MainWindow::showEvent(QShowEvent *e) } #endif + QMetaObject::invokeMethod(this, "ShowWelcomeDialog", Qt::QueuedConnection); + first_show_ = false; } } diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h index 0e292571c..01f2fc432 100644 --- a/app/window/mainwindow/mainwindow.h +++ b/app/window/mainwindow/mainwindow.h @@ -196,6 +196,8 @@ private slots: void ProjectPanelSelectionChanged(const QVector &nodes); + void ShowWelcomeDialog(); + }; } From a83be3a25305edb7d2c7cc792d58360c9e69137c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 17:47:08 -0700 Subject: [PATCH 05/12] nodeview: combined paste/duplicate code Code cleanup. Doesn't fix bug involved with pasting or duplicating. --- app/widget/nodeview/nodeview.cpp | 62 ++++++++++++++++---------------- app/widget/nodeview/nodeview.h | 2 ++ 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 6f277e5a9..b67076a8f 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -353,42 +353,12 @@ void NodeView::CopySelected(bool cut) void NodeView::Paste() { - if (!graph_) { - return; - } - - paste_command_ = new MultiUndoCommand(); - - QVector pasted_nodes = PasteNodesFromClipboard(graph_, paste_command_); - - if (!pasted_nodes.isEmpty()) { - paste_command_->add_child(new NodeViewAttachNodesToCursor(this, pasted_nodes)); - } - - paste_command_->redo(); + PasteNodesInternal(); } void NodeView::Duplicate() { - if (!graph_) { - return; - } - - QVector selected = scene_.GetSelectedNodes(); - - if (selected.isEmpty()) { - return; - } - - paste_command_ = new MultiUndoCommand(); - - QVector duplicated_nodes = Node::CopyDependencyGraph(selected, paste_command_); - - if (!duplicated_nodes.isEmpty()) { - paste_command_->add_child(new NodeViewAttachNodesToCursor(this, duplicated_nodes)); - } - - paste_command_->redo(); + PasteNodesInternal(scene_.GetSelectedNodes()); } void NodeView::SetColorLabel(int index) @@ -1596,6 +1566,34 @@ NodeViewItem *NodeView::UpdateNodeItem(Node *node, bool ignore_own_context) return item; } +void NodeView::PasteNodesInternal(const QVector &duplicate_nodes) +{ + // If no graph, do nothing + if (!graph_) { + return; + } + + paste_command_ = new MultiUndoCommand(); + + // If duplicating nodes, duplicate, otherwise paste + QVector new_nodes; + if (duplicate_nodes.isEmpty()) { + new_nodes = PasteNodesFromClipboard(graph_, paste_command_); + } else { + new_nodes = Node::CopyDependencyGraph(duplicate_nodes, paste_command_); + } + + // If no nodes were retrieved, do nothing + if (new_nodes.isEmpty()) { + return; + } + + // Attach nodes to cursor + paste_command_->add_child(new NodeViewAttachNodesToCursor(this, new_nodes)); + + paste_command_->redo(); +} + NodeView::NodeViewAttachNodesToCursor::NodeViewAttachNodesToCursor(NodeView *view, const QVector &nodes) : view_(view), nodes_(nodes) diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index 402c643d3..24e9d452c 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -142,6 +142,8 @@ private: NodeViewItem *UpdateNodeItem(Node *node, bool ignore_own_context = false); + void PasteNodesInternal(const QVector &duplicate_nodes = QVector()); + class NodeViewAttachNodesToCursor : public UndoCommand { public: From b630aa88a4ff3d3df30a90ed819d000d082005ab Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 17:58:06 -0700 Subject: [PATCH 06/12] aboutdialog: revised layout --- app/dialog/about/about.cpp | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/app/dialog/about/about.cpp b/app/dialog/about/about.cpp index 3c13440fa..e3500e217 100644 --- a/app/dialog/about/about.cpp +++ b/app/dialog/about/about.cpp @@ -76,30 +76,28 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) : layout->addLayout(horiz_layout); - // Patrons where necessary - if (!patrons.isEmpty()) { - layout->addWidget(new QLabel()); - layout->addWidget(QtUtils::CreateHorizontalLine()); - layout->addWidget(new QLabel()); + // Patrons where possible + layout->addWidget(new QLabel()); - QString opening_statement; + QString opening_statement; - if (welcome_dialog) { - opening_statement = tr("Olive relies on support from the community to continue its development."); - } else { - opening_statement = tr("Olive wouldn't be possible without the support of gracious donations from the following people."); - } + if (welcome_dialog || patrons.isEmpty()) { + opening_statement = tr("Olive relies on support from the community to continue its development."); + } else { + opening_statement = tr("Olive wouldn't be possible without the support of gracious donations from the following people."); + } - QLabel* support_lbl = new QLabel(tr("%1 " - "If you like this project, please consider " - "donating or " - "pledging to " + QLabel* support_lbl = new QLabel(tr("%1 " + "If you like this project, please consider making a " + "one-time donation or " + "pledging monthly to " "support its development.").arg(opening_statement)); - support_lbl->setWordWrap(true); - support_lbl->setAlignment(Qt::AlignCenter); - support_lbl->setOpenExternalLinks(true); - layout->addWidget(support_lbl); + support_lbl->setWordWrap(true); + support_lbl->setAlignment(Qt::AlignCenter); + support_lbl->setOpenExternalLinks(true); + layout->addWidget(support_lbl); + if (!patrons.isEmpty()) { ScrollingLabel* scroll = new ScrollingLabel(patrons); scroll->StartAnimating(); layout->addWidget(scroll); From cdc8df1dc590dda44ecc36f90972023b306d1ded Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 18:22:02 -0700 Subject: [PATCH 07/12] added node properties dialog Incomplete, but probably useful --- app/dialog/CMakeLists.txt | 1 + app/dialog/nodeproperties/CMakeLists.txt | 22 ++++++ .../nodeproperties/nodepropertiesdialog.cpp | 74 +++++++++++++++++++ .../nodeproperties/nodepropertiesdialog.h | 52 +++++++++++++ .../projectexplorer/projectexplorer.cpp | 6 +- app/widget/timelinewidget/timelinewidget.cpp | 4 +- 6 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 app/dialog/nodeproperties/CMakeLists.txt create mode 100644 app/dialog/nodeproperties/nodepropertiesdialog.cpp create mode 100644 app/dialog/nodeproperties/nodepropertiesdialog.h diff --git a/app/dialog/CMakeLists.txt b/app/dialog/CMakeLists.txt index 48c9884d1..b1a19fe5a 100644 --- a/app/dialog/CMakeLists.txt +++ b/app/dialog/CMakeLists.txt @@ -23,6 +23,7 @@ add_subdirectory(diskcache) add_subdirectory(export) add_subdirectory(footagerelink) add_subdirectory(keyframeproperties) +add_subdirectory(nodeproperties) add_subdirectory(preferences) add_subdirectory(progress) add_subdirectory(rendercancel) diff --git a/app/dialog/nodeproperties/CMakeLists.txt b/app/dialog/nodeproperties/CMakeLists.txt new file mode 100644 index 000000000..f1c44b5af --- /dev/null +++ b/app/dialog/nodeproperties/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2021 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + dialog/nodeproperties/nodepropertiesdialog.cpp + dialog/nodeproperties/nodepropertiesdialog.h + PARENT_SCOPE +) diff --git a/app/dialog/nodeproperties/nodepropertiesdialog.cpp b/app/dialog/nodeproperties/nodepropertiesdialog.cpp new file mode 100644 index 000000000..123b6c69b --- /dev/null +++ b/app/dialog/nodeproperties/nodepropertiesdialog.cpp @@ -0,0 +1,74 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "nodepropertiesdialog.h" + +#include +#include + +#include "core.h" +#include "widget/nodeview/nodeviewundo.h" + +namespace olive { + +NodePropertiesDialog::NodePropertiesDialog(Node *node, const rational &timebase, QWidget *parent) : + QDialog(parent), + node_(node) +{ + setWindowTitle(tr("Node Properties")); + + QVBoxLayout *layout = new QVBoxLayout(this); + + QHBoxLayout *label_layout = new QHBoxLayout(); + label_layout->setMargin(0); + layout->addLayout(label_layout); + + label_layout->addWidget(new QLabel(tr("Name:"))); + + label_edit_ = new QLineEdit(); + label_edit_->setText(node->GetLabel()); + label_layout->addWidget(label_edit_); + + NodeParamViewItem *item = new NodeParamViewItem(node); + item->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + item->SetTimebase(timebase); + item->setTitleBarWidget(new QWidget()); + layout->addWidget(item); + + layout->addStretch(); + + QDialogButtonBox *btns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(btns, &QDialogButtonBox::accepted, this, &NodePropertiesDialog::accept); + connect(btns, &QDialogButtonBox::rejected, this, &NodePropertiesDialog::reject); + layout->addWidget(btns); +} + +void NodePropertiesDialog::accept() +{ + if (label_edit_->text() != node_->GetLabel()) { + NodeRenameCommand* rename_command = new NodeRenameCommand(); + rename_command->AddNode(node_, label_edit_->text()); + Core::instance()->undo_stack()->push(rename_command); + } + + QDialog::accept(); +} + +} diff --git a/app/dialog/nodeproperties/nodepropertiesdialog.h b/app/dialog/nodeproperties/nodepropertiesdialog.h new file mode 100644 index 000000000..5375a1313 --- /dev/null +++ b/app/dialog/nodeproperties/nodepropertiesdialog.h @@ -0,0 +1,52 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef NODEPROPERTIESDIALOG_H +#define NODEPROPERTIESDIALOG_H + +#include + +#include "widget/nodeparamview/nodeparamviewitem.h" + +namespace olive { + +class NodePropertiesDialog : public QDialog +{ + Q_OBJECT +public: + NodePropertiesDialog(Node *node, const rational &timebase, QWidget *parent = nullptr); + NodePropertiesDialog(const QVector &node, const rational &timebase, QWidget *parent = nullptr) : + NodePropertiesDialog(node.first(), timebase, parent) + { + } + +public slots: + virtual void accept() override; + +private: + Node *node_; + + QLineEdit *label_edit_; + +}; + +} + +#endif // NODEPROPERTIESDIALOG_H diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index d653384f5..d229cf112 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -30,6 +30,7 @@ #include "common/define.h" #include "core.h" +#include "dialog/nodeproperties/nodepropertiesdialog.h" #include "dialog/sequence/sequence.h" #include "projectexplorerundo.h" #include "task/precache/precachetask.h" @@ -439,11 +440,12 @@ void ProjectExplorer::ShowItemPropertiesDialog() // FIXME: Support for multiple items if (dynamic_cast(sel)) { - Core::instance()->LabelNodes({static_cast(sel)}); + NodePropertiesDialog npd(sel, static_cast(sel)->GetVideoParams().time_base(), this); + npd.exec(); } else if (dynamic_cast(sel)) { - Core::instance()->LabelNodes({static_cast(sel)}); + Core::instance()->LabelNodes(context_menu_items_); } else if (dynamic_cast(sel)) { diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 1d3fbea4a..5fa05ab70 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -28,6 +28,7 @@ #include "core.h" #include "common/range.h" #include "common/timecodefunctions.h" +#include "dialog/nodeproperties/nodepropertiesdialog.h" #include "dialog/sequence/sequence.h" #include "node/block/transition/transition.h" #include "tool/add.h" @@ -992,7 +993,8 @@ void TimelineWidget::ShowContextMenu() nodes.append(i); } - Core::instance()->LabelNodes(nodes); + NodePropertiesDialog npd(nodes, timebase(), this); + npd.exec(); }); } From 7c21cf2339316024b3ed25f05b547ebab6f37268 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 22:08:44 -0700 Subject: [PATCH 08/12] audio: fixed reversing issue --- app/audio/outputdeviceproxy.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/audio/outputdeviceproxy.cpp b/app/audio/outputdeviceproxy.cpp index 12b023a2e..f8da3cb05 100644 --- a/app/audio/outputdeviceproxy.cpp +++ b/app/audio/outputdeviceproxy.cpp @@ -114,10 +114,12 @@ qint64 AudioOutputDeviceProxy::ReverseAwareRead(char *data, qint64 maxlen) if (playback_speed_ < 0) { // If we're reversing, we'll seek back by maxlen bytes before we read - new_pos = device_->pos() - maxlen; + qint64 len_adjusted_by_channels = maxlen / params_.channel_count(); + + new_pos = device_->pos() - len_adjusted_by_channels; if (new_pos < 0) { - maxlen = device_->pos(); + maxlen = device_->pos() * params_.channel_count(); new_pos = 0; } From fac8dbbc196736c32f61c16cd17f67d98144b4c7 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 22:09:00 -0700 Subject: [PATCH 09/12] nodegraph: use qhash instead of qmap Qt says this is better --- app/node/graph.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/node/graph.h b/app/node/graph.h index dbdfb990b..8935db662 100644 --- a/app/node/graph.h +++ b/app/node/graph.h @@ -96,7 +96,7 @@ public: qreal GetNodeContextHeight(Node *context); - using PositionMap = QMap; + using PositionMap = QHash; const PositionMap &GetNodesForContext(Node *context) { From 1c8b1b600692cfee839da9f9c11e8985a7976784 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 22:09:28 -0700 Subject: [PATCH 10/12] nodeview: fixed pasting/duplicating crash --- app/node/nodecopypaste.cpp | 4 +- app/node/nodecopypaste.h | 2 +- app/widget/nodeview/nodeview.cpp | 80 +++++++++++++++++++- app/widget/nodeview/nodeview.h | 3 + app/widget/timelinewidget/timelinewidget.cpp | 2 +- app/widget/timelinewidget/timelinewidget.h | 2 +- 6 files changed, 87 insertions(+), 6 deletions(-) diff --git a/app/node/nodecopypaste.cpp b/app/node/nodecopypaste.cpp index 61799b0f8..21eee76d9 100644 --- a/app/node/nodecopypaste.cpp +++ b/app/node/nodecopypaste.cpp @@ -70,7 +70,7 @@ void NodeCopyPasteService::CopyNodesToClipboard(const QVector &nodes, vo writer.writeEndElement(); // contexts writer.writeStartElement(QStringLiteral("custom")); - CopyNodesToClipboardInternal(&writer, userdata); + CopyNodesToClipboardInternal(&writer, nodes, userdata); writer.writeEndElement(); // custom writer.writeEndElement(); // olive @@ -229,7 +229,7 @@ QVector NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph, return pasted_nodes; } -void NodeCopyPasteService::CopyNodesToClipboardInternal(QXmlStreamWriter*, void*) +void NodeCopyPasteService::CopyNodesToClipboardInternal(QXmlStreamWriter*, const QVector &, void*) { } diff --git a/app/node/nodecopypaste.h b/app/node/nodecopypaste.h index a44e4240b..cc29d281a 100644 --- a/app/node/nodecopypaste.h +++ b/app/node/nodecopypaste.h @@ -39,7 +39,7 @@ protected: QVector PasteNodesFromClipboard(NodeGraph *graph, MultiUndoCommand *command, void* userdata = nullptr); - virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, void* userdata); + virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector &nodes, void* userdata); virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata); diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index b67076a8f..1bfefd9dc 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -1264,6 +1264,65 @@ bool NodeView::eventFilter(QObject *object, QEvent *event) return super::eventFilter(object, event); } +void NodeView::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector &nodes, void *userdata) +{ + writer->writeStartElement(QStringLiteral("pos")); + + for (Node *n : nodes) { + NodeViewItem *item = scene_.item_map().value(n); + QPointF pos = item->GetNodePosition(); + + writer->writeStartElement(QStringLiteral("node")); + writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(n))); + writer->writeTextElement(QStringLiteral("x"), QString::number(pos.x())); + writer->writeTextElement(QStringLiteral("y"), QString::number(pos.y())); + writer->writeEndElement(); // node + } + + writer->writeEndElement(); // pos +} + +void NodeView::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void *userdata) +{ + NodeGraph::PositionMap *map = static_cast(userdata); + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("pos")) { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + Node *n = nullptr; + QPointF pos; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("ptr")) { + n = xml_node_data.node_ptrs.value(attr.value().toULongLong()); + break; + } + } + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("x")) { + pos.setX(reader->readElementText().toDouble()); + } else if (reader->name() == QStringLiteral("y")) { + pos.setY(reader->readElementText().toDouble()); + } else { + reader->skipCurrentElement(); + } + } + + if (n) { + map->insert(n, pos); + } + } else { + reader->skipCurrentElement(); + } + } + } else { + reader->skipCurrentElement(); + } + } +} + void NodeView::ZoomFromKeyboard(double multiplier) { QPoint cursor_pos = mapFromGlobal(QCursor::pos()); @@ -1577,14 +1636,33 @@ void NodeView::PasteNodesInternal(const QVector &duplicate_nodes) // If duplicating nodes, duplicate, otherwise paste QVector new_nodes; + NodeGraph::PositionMap map; if (duplicate_nodes.isEmpty()) { - new_nodes = PasteNodesFromClipboard(graph_, paste_command_); + new_nodes = PasteNodesFromClipboard(graph_, paste_command_, &map); + + for (auto it=new_nodes.cbegin(); it!=new_nodes.cend(); it++) { + for (Node *context : qAsConst(filter_nodes_)) { + paste_command_->add_child(new NodeSetPositionCommand(*it, context, map.value(*it), false)); + } + } } else { new_nodes = Node::CopyDependencyGraph(duplicate_nodes, paste_command_); + + for (int i=0; iGetNodePosition(); + paste_command_->add_child(new NodeSetPositionCommand(copy, context, p, false)); + } + } } // If no nodes were retrieved, do nothing if (new_nodes.isEmpty()) { + delete paste_command_; + paste_command_ = nullptr; return; } diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index 24e9d452c..65d1b0438 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -112,6 +112,9 @@ protected: virtual bool eventFilter(QObject *object, QEvent *event) override; + virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector &nodes, void* userdata) override; + virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override; + private: void AttachNodesToCursor(const QVector &nodes); diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 5fa05ab70..6d5d10014 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -296,7 +296,7 @@ void TimelineWidget::DisconnectNodeEvent(ViewerOutput *n) } } -void TimelineWidget::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, void* userdata) +void TimelineWidget::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector &nodes, void* userdata) { // Cache the earliest in point so all copied clips have a "relative" in point that can be pasted anywhere QVector& selected = *static_cast*>(userdata); diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 502243266..176486f8e 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -248,7 +248,7 @@ protected: virtual void ConnectNodeEvent(ViewerOutput* n) override; virtual void DisconnectNodeEvent(ViewerOutput* n) override; - virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, void* userdata) override; + virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector &nodes, void* userdata) override; virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override; struct BlockPasteData { From c356c241b94cedd4f014160e73a0fb4251b0f818 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 23:35:08 -0700 Subject: [PATCH 11/12] nodeview: disable toolbar when null graph set --- app/panel/node/node.cpp | 10 +++++----- app/panel/node/node.h | 3 +++ app/widget/nodeview/nodeview.cpp | 4 ++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/panel/node/node.cpp b/app/panel/node/node.cpp index 4b7334378..591f7a867 100644 --- a/app/panel/node/node.cpp +++ b/app/panel/node/node.cpp @@ -32,19 +32,19 @@ NodePanel::NodePanel(QWidget *parent) : QVBoxLayout *outer_layout = new QVBoxLayout(outer_widget); outer_layout->setMargin(0); - NodeViewToolBar *toolbar = new NodeViewToolBar(); - outer_layout->addWidget(toolbar); + 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::MiniMapEnabledToggled, node_view_, &NodeView::SetMiniMapEnabled); - connect(toolbar, &NodeViewToolBar::AddNodeClicked, node_view_, &NodeView::ShowAddMenu); + connect(toolbar_, &NodeViewToolBar::MiniMapEnabledToggled, node_view_, &NodeView::SetMiniMapEnabled); + connect(toolbar_, &NodeViewToolBar::AddNodeClicked, node_view_, &NodeView::ShowAddMenu); // Set defaults - toolbar->SetMiniMapEnabled(true); + toolbar_->SetMiniMapEnabled(true); node_view_->SetMiniMapEnabled(true); // Connect node view signals to this panel diff --git a/app/panel/node/node.h b/app/panel/node/node.h index 607671055..910a48f5f 100644 --- a/app/panel/node/node.h +++ b/app/panel/node/node.h @@ -44,6 +44,7 @@ public: void SetGraph(NodeGraph *graph, const QVector &nodes) { node_view_->SetGraph(graph, nodes); + toolbar_->setEnabled(graph); } void ClearGraph() @@ -125,6 +126,8 @@ private: NodeView* node_view_; + NodeViewToolBar *toolbar_; + }; } diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 1bfefd9dc..5369fd453 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -975,6 +975,10 @@ void NodeView::ShowContextMenu(const QPoint &pos) void NodeView::CreateNodeSlot(QAction *action) { + if (!graph_) { + return; + } + Node* new_node = NodeFactory::CreateFromMenuAction(action); if (new_node) { From 3f785f30fdb976993a52aa7390ec08679313d946 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 23 Jul 2021 23:38:24 -0700 Subject: [PATCH 12/12] nodeview: ensure node is cleanly removed at all times --- app/widget/nodeview/nodeview.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 5369fd453..6b5d740e6 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -1041,6 +1041,13 @@ void NodeView::OpenSelectedNodeInViewer() void NodeView::RemoveNode(Node *node) { + for (const Node::OutputConnection &oc : node->output_connections()) { + scene_.RemoveEdge(oc.first, oc.second); + } + for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { + scene_.RemoveEdge(it->second, it->first); + } + positions_.remove(scene_.item_map().value(node)); scene_.RemoveNode(node); } @@ -1100,14 +1107,7 @@ void NodeView::RemoveNodePosition(Node *node, Node *relative) } if (!found) { - for (const Node::OutputConnection &oc : node->output_connections()) { - scene_.RemoveEdge(oc.first, oc.second); - } - for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { - scene_.RemoveEdge(it->second, it->first); - } - positions_.remove(item); - scene_.RemoveNode(node); + RemoveNode(node); } }