Files
oak-editor/app/timeline/timelineundopointer.cpp
T
Mike-Solar bb40b4923e style: unify identifier naming per updated conventions
Automated with clang-tidy readability-identifier-naming (config added to
.clang-tidy) plus scripted passes, per the updated rules now documented
in CONTRIBUTING.md:

- types (class/struct/enum/alias/template params): PascalCase
- functions, variables, members: snake_case (incl. rational -> Rational)
- private/protected members: trailing underscore; static member
  variables likewise (instance_, available_themes_)
- constants and enum values: snake_case (kLinear -> k_linear,
  F32P -> f32p); ALL_CAPS reserved for macros
- macros: OAK_ prefix (OLIVE_ADD_TEST/OLIVE_ASSERT/OLIVE_CONFIG ->
  OAK_ADD_TEST/OAK_ASSERT/OAK_CONFIG, GL_PREAMBLE -> OAK_GL_PREAMBLE,
  include guards -> OAK_*)
- file names: all lowercase (Current/Plugin/OliveHost/OliveClip/
  OlivePluginInstance -> current/plugin/olivehost/oliveclip/
  oliveplugininstance)
- getters share the member name sans underscore, setters set_foo()
- Qt and third-party (OpenFX) virtual overrides and framework callbacks
  keep their original names (exempt in .clang-tidy)

Manual follow-ups required where automation could not reach:
- string-based QMetaObject/SIGNAL/SLOT references updated to renamed
  methods (AddTask, CreatedFile, DeleteSpecificFile, moveSelectionUp, ...)
- macro bodies referencing renamed methods (OLIVE_CONFIG,
  NODE_DEFAULT_DESTRUCTOR, MANAGEDDISPLAYWIDGET_*)
- self-shadowing locals renamed where signals/methods became same-named
  (size_changed, worker_count, selected_items, import param, filters)
- third_party OFX member/namespace usages restored (OFX::Host::*,
  _created, _clipPrefsDirty, createInstance, clearPersistentMessage)
- STL protocol aliases restored (const_iterator) with .clang-tidy
  ignore rules; qHash overloads restored

Full build and test suite pass: ctest 4/4, ~1960 gtest cases green.
2026-07-19 16:10:54 +08:00

402 lines
11 KiB
C++

/***
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 <http://www.gnu.org/licenses/>.
***/
#include "timelineundopointer.h"
#include "node/block/gap/gap.h"
#include "node/block/transition/transition.h"
#include "node/project.h"
#include "timelineundocommon.h"
namespace olive
{
//
// BlockTrimCommand
//
void BlockTrimCommand::redo()
{
if (doing_nothing_) {
return;
}
// Determine how much time to invalidate
TimeRange invalidate_range;
if (mode_ == Timeline::k_trim_in) {
invalidate_range = TimeRange(block_->in(), block_->in() + trim_diff_);
block_->set_length_and_media_in(new_length_);
} else {
invalidate_range = TimeRange(block_->out(), block_->out() - trim_diff_);
block_->set_length_and_media_out(new_length_);
}
if (needs_adjacent_) {
if (we_created_adjacent_) {
// Add adjacent and insert it
adjacent_->setParent(track_->parent());
if (mode_ == Timeline::k_trim_in) {
track_->insert_block_before(adjacent_, block_);
} else {
track_->insert_block_after(adjacent_, block_);
}
} else if (we_removed_adjacent_) {
track_->ripple_remove_block(adjacent_);
// It no longer inputs/outputs anything, remove it
if (remove_block_from_graph_ && node_can_be_removed(adjacent_)) {
if (!deleted_adjacent_command_) {
deleted_adjacent_command_ =
create_and_run_remove_command(adjacent_);
} else {
deleted_adjacent_command_->redo_now();
}
}
} else {
Rational adjacent_length = adjacent_->length() + trim_diff_;
if (mode_ == Timeline::k_trim_in) {
adjacent_->set_length_and_media_out(adjacent_length);
} else {
adjacent_->set_length_and_media_in(adjacent_length);
}
}
}
if (dynamic_cast<TransitionBlock *>(block_)) {
// Whole transition needs to be invalidated
invalidate_range = block_->range();
}
}
void BlockTrimCommand::undo()
{
if (doing_nothing_) {
return;
}
// Will be POSITIVE if trimming shorter and NEGATIVE if trimming longer
if (needs_adjacent_) {
if (we_created_adjacent_) {
// Adjacent is ours, just delete it
track_->ripple_remove_block(adjacent_);
adjacent_->setParent(&memory_manager_);
} else {
if (we_removed_adjacent_) {
if (deleted_adjacent_command_) {
// We deleted adjacent, restore it now
deleted_adjacent_command_->undo_now();
}
if (mode_ == Timeline::k_trim_in) {
track_->insert_block_before(adjacent_, block_);
} else {
track_->insert_block_after(adjacent_, block_);
}
} else {
Rational adjacent_length = adjacent_->length() - trim_diff_;
if (mode_ == Timeline::k_trim_in) {
adjacent_->set_length_and_media_out(adjacent_length);
} else {
adjacent_->set_length_and_media_in(adjacent_length);
}
}
}
}
TimeRange invalidate_range;
if (mode_ == Timeline::k_trim_in) {
block_->set_length_and_media_in(old_length_);
invalidate_range = TimeRange(block_->in(), block_->in() + trim_diff_);
} else {
block_->set_length_and_media_out(old_length_);
invalidate_range = TimeRange(block_->out(), block_->out() - trim_diff_);
}
if (dynamic_cast<TransitionBlock *>(block_)) {
// Whole transition needs to be invalidated
invalidate_range = block_->range();
}
}
void BlockTrimCommand::prepare()
{
// Store old length
old_length_ = block_->length();
// Determine if the length isn't changing, in which case we set a flag to do nothing
if ((doing_nothing_ = (old_length_ == new_length_))) {
return;
}
// Will be POSITIVE if trimming shorter and NEGATIVE if trimming longer
trim_diff_ = old_length_ - new_length_;
// Retrieve our adjacent block (or nullptr if none)
if (mode_ == Timeline::k_trim_in) {
adjacent_ = block_->previous();
} else {
adjacent_ = block_->next();
}
// Ignore when trimming the out with no adjacent, because the user must have trimmed the end
// of the last block in the track, so we don't need to do anything elses
needs_adjacent_ = (mode_ == Timeline::k_trim_in || adjacent_);
if (needs_adjacent_) {
// If we're trimming shorter, we need an adjacent, so check if we have a viable one.
we_created_adjacent_ =
(trim_diff_ > 0 &&
(!adjacent_ ||
(!dynamic_cast<GapBlock *>(adjacent_) && !trim_is_a_roll_edit_)));
if (we_created_adjacent_) {
// We shortened but don't have a viable adjacent to lengthen, so we create one
adjacent_ = new GapBlock();
adjacent_->set_length_and_media_out(trim_diff_);
} else {
// Determine if we're removing the adjacent
Rational adjacent_length = adjacent_->length() + trim_diff_;
we_removed_adjacent_ = adjacent_length.isNull();
}
}
}
//
// TrackSlideCommand
//
void TrackSlideCommand::redo()
{
// Make sure all movement blocks' old positions are invalidated
TimeRange invalidate_range(blocks_.first()->in(), blocks_.last()->out());
// We will always have an in adjacent if there was a valid slide
if (we_created_in_adjacent_) {
// We created in adjacent, so all we have to do is insert it
in_adjacent_->setParent(track_->parent());
track_->insert_block_before(in_adjacent_, blocks_.first());
} else if (-movement_ == in_adjacent_->length()) {
// Movement will remove in adjacent
track_->ripple_remove_block(in_adjacent_);
if (node_can_be_removed(in_adjacent_)) {
if (!in_adjacent_remove_command_) {
in_adjacent_remove_command_ = create_remove_command(in_adjacent_);
}
in_adjacent_remove_command_->redo_now();
}
we_removed_in_adjacent_ = true;
} else {
// Simply resize adjacent
in_adjacent_->set_length_and_media_out(in_adjacent_->length() +
movement_);
}
// We may not have an out adjacent if the slide was at the end of the track
if (out_adjacent_) {
if (we_created_out_adjacent_) {
// We created out adjacent, so we just have to insert it
out_adjacent_->setParent(track_->parent());
track_->insert_block_after(out_adjacent_, blocks_.last());
} else if (movement_ == out_adjacent_->length()) {
// Movement will remove out adjacent
track_->ripple_remove_block(out_adjacent_);
if (node_can_be_removed(out_adjacent_)) {
if (!out_adjacent_remove_command_) {
out_adjacent_remove_command_ =
create_remove_command(out_adjacent_);
}
out_adjacent_remove_command_->redo_now();
}
we_removed_out_adjacent_ = true;
} else {
// Simply resize adjacent
out_adjacent_->set_length_and_media_in(out_adjacent_->length() -
movement_);
}
}
// Make sure all movement blocks' new positions are invalidated
invalidate_range.set_range(
qMin(invalidate_range.in(), blocks_.first()->in()),
qMax(invalidate_range.out(), blocks_.last()->out()));
}
void TrackSlideCommand::undo()
{
// Make sure all movement blocks' old positions are invalidated
TimeRange invalidate_range(blocks_.first()->in(), blocks_.last()->out());
if (we_created_in_adjacent_) {
// We created this, so we can remove it now
track_->ripple_remove_block(in_adjacent_);
in_adjacent_->setParent(&memory_manager_);
} else if (we_removed_in_adjacent_) {
if (in_adjacent_remove_command_) {
// We removed this, so we can restore it now
in_adjacent_remove_command_->undo_now();
}
track_->insert_block_before(in_adjacent_, blocks_.first());
} else {
// Simply resize adjacent
in_adjacent_->set_length_and_media_out(in_adjacent_->length() -
movement_);
}
if (out_adjacent_) {
if (we_created_out_adjacent_) {
// We created this, so we can remove it now
track_->ripple_remove_block(out_adjacent_);
out_adjacent_->setParent(&memory_manager_);
} else if (we_removed_out_adjacent_) {
if (out_adjacent_remove_command_) {
out_adjacent_remove_command_->undo_now();
}
track_->insert_block_after(out_adjacent_, blocks_.last());
} else {
out_adjacent_->set_length_and_media_in(out_adjacent_->length() +
movement_);
}
}
// Make sure all movement blocks' new positions are invalidated
invalidate_range.set_range(
qMin(invalidate_range.in(), blocks_.first()->in()),
qMax(invalidate_range.out(), blocks_.last()->out()));
}
void TrackSlideCommand::prepare()
{
if (!in_adjacent_) {
in_adjacent_ = new GapBlock();
in_adjacent_->set_length_and_media_out(movement_);
in_adjacent_->setParent(&memory_manager_);
we_created_in_adjacent_ = true;
} else {
we_created_in_adjacent_ = false;
}
if (!out_adjacent_ && blocks_.last()->next()) {
out_adjacent_ = new GapBlock();
out_adjacent_->set_length_and_media_out(-movement_);
out_adjacent_->setParent(&memory_manager_);
we_created_out_adjacent_ = true;
} else {
we_created_out_adjacent_ = false;
}
}
//
// TrackPlaceBlockCommand
//
TrackPlaceBlockCommand::~TrackPlaceBlockCommand()
{
delete ripple_remove_command_;
qDeleteAll(add_track_commands_);
}
void TrackPlaceBlockCommand::redo()
{
// Determine if we need to add tracks
if (track_index_ >= timeline_->get_tracks().size()) {
if (add_track_commands_.isEmpty()) {
// First redo, create tracks now
add_track_commands_.resize(track_index_ -
timeline_->get_tracks().size() + 1);
for (int i = 0; i < add_track_commands_.size(); i++) {
add_track_commands_[i] = new TimelineAddTrackCommand(timeline_);
}
}
for (int i = 0; i < add_track_commands_.size(); i++) {
add_track_commands_.at(i)->redo_now();
}
}
Track *track = timeline_->get_track_at(track_index_);
bool append = (in_ >= track->track_length());
// Check if the placement location is past the end of the timeline
if (append) {
if (in_ > track->track_length()) {
// If so, insert a gap here
if (!gap_) {
gap_ = new GapBlock();
gap_->set_length_and_media_out(in_ - track->track_length());
}
gap_->setParent(track->parent());
track->append_block(gap_);
}
track->append_block(insert_);
} else {
// Place the Block at this point
if (!ripple_remove_command_) {
ripple_remove_command_ = new TrackRippleRemoveAreaCommand(
track, TimeRange(in_, in_ + insert_->length()));
ripple_remove_command_->set_allow_splitting_gaps(true);
}
ripple_remove_command_->redo_now();
track->insert_block_after(insert_,
ripple_remove_command_->get_insertion_index());
}
}
void TrackPlaceBlockCommand::undo()
{
Track *t = timeline_->get_track_at(track_index_);
TimeRange insert_range(insert_->in(), insert_->out());
// Firstly, remove our insert
t->ripple_remove_block(insert_);
if (ripple_remove_command_) {
// If we ripple removed, just undo that
ripple_remove_command_->undo_now();
} else if (gap_) {
t->ripple_remove_block(gap_);
gap_->setParent(&memory_manager_);
}
// Remove tracks if we added them
for (int i = add_track_commands_.size() - 1; i >= 0; i--) {
add_track_commands_.at(i)->undo_now();
}
}
}