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.
189 lines
4.8 KiB
C++
189 lines
4.8 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 "timelineundosplit.h"
|
|
|
|
#include "node/block/clip/clip.h"
|
|
#include "node/block/transition/transition.h"
|
|
#include "node/nodeundo.h"
|
|
|
|
namespace olive
|
|
{
|
|
|
|
//
|
|
// BlockSplitCommand
|
|
//
|
|
void BlockSplitCommand::prepare()
|
|
{
|
|
reconnect_tree_command_ = new MultiUndoCommand();
|
|
new_block_ = static_cast<Block *>(
|
|
Node::copy_node_in_graph(block_, reconnect_tree_command_));
|
|
}
|
|
|
|
void BlockSplitCommand::redo()
|
|
{
|
|
old_length_ = block_->length();
|
|
|
|
Q_ASSERT(point_ > block_->in() && point_ < block_->out());
|
|
|
|
reconnect_tree_command_->redo_now();
|
|
|
|
// Determine our new lengths
|
|
Rational new_length = point_ - block_->in();
|
|
Rational new_part_length = block_->out() - point_;
|
|
|
|
// Begin an operation
|
|
Track *track = block_->track();
|
|
|
|
// Set lengths
|
|
block_->set_length_and_media_out(new_length);
|
|
new_block()->set_length_and_media_in(new_part_length);
|
|
|
|
// Insert new block
|
|
track->insert_block_after(new_block(), block_);
|
|
|
|
if (ClipBlock *new_clip = dynamic_cast<ClipBlock *>(new_block_)) {
|
|
ClipBlock *old_clip = static_cast<ClipBlock *>(block_);
|
|
new_clip->add_cache_passthrough_from(old_clip);
|
|
}
|
|
|
|
// If the block had an out transition, we move it to the new block
|
|
moved_transition_ = NodeInput();
|
|
|
|
TransitionBlock *potential_transition =
|
|
dynamic_cast<TransitionBlock *>(new_block()->next());
|
|
if (potential_transition) {
|
|
for (const Node::OutputConnection &output :
|
|
block_->output_connections()) {
|
|
if (output.second.node() == potential_transition) {
|
|
moved_transition_ = NodeInput(potential_transition,
|
|
TransitionBlock::k_out_block_input);
|
|
Node::disconnect_edge(block_, moved_transition_);
|
|
Node::connect_edge(new_block(), moved_transition_);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockSplitCommand::undo()
|
|
{
|
|
Track *track = block_->track();
|
|
|
|
if (moved_transition_.is_valid()) {
|
|
Node::disconnect_edge(new_block(), moved_transition_);
|
|
Node::connect_edge(block_, moved_transition_);
|
|
}
|
|
|
|
block_->set_length_and_media_out(old_length_);
|
|
track->ripple_remove_block(new_block());
|
|
|
|
// If we ran a reconnect command, disconnect now
|
|
reconnect_tree_command_->undo_now();
|
|
}
|
|
|
|
//
|
|
// BlockSplitPreservingLinksCommand
|
|
//
|
|
Block *BlockSplitPreservingLinksCommand::get_split(Block *original,
|
|
int time_index) const
|
|
{
|
|
if (time_index >= 0 && time_index < times_.size()) {
|
|
int original_index = blocks_.indexOf(original);
|
|
if (original_index != -1) {
|
|
return splits_.at(time_index).at(original_index);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void BlockSplitPreservingLinksCommand::prepare()
|
|
{
|
|
splits_.resize(times_.size());
|
|
|
|
for (int i = 0; i < times_.size(); i++) {
|
|
const Rational &time = times_.at(i);
|
|
|
|
// FIXME: I realize this isn't going to work if the times aren't ordered. I'm lazy so rather
|
|
// than writing in a sorting algorithm here, I'll just put an assert as a reminder
|
|
// if this ever becomes an issue.
|
|
Q_ASSERT(i == 0 || time > times_.at(i - 1));
|
|
|
|
QVector<Block *> splits(blocks_.size());
|
|
|
|
for (int j = 0; j < blocks_.size(); j++) {
|
|
Block *b = blocks_.at(j);
|
|
|
|
if (b->in() < time && b->out() > time) {
|
|
BlockSplitCommand *split_command =
|
|
new BlockSplitCommand(b, time);
|
|
split_command->redo_now();
|
|
splits.replace(j, split_command->new_block());
|
|
commands_.append(split_command);
|
|
} else {
|
|
splits.replace(j, nullptr);
|
|
}
|
|
}
|
|
|
|
splits_.replace(i, splits);
|
|
}
|
|
|
|
// Now that we've determined all the splits, we can relink everything
|
|
for (int i = 0; i < blocks_.size(); i++) {
|
|
Block *a = blocks_.at(i);
|
|
|
|
for (int j = 0; j < blocks_.size(); j++) {
|
|
if (i == j) {
|
|
continue;
|
|
}
|
|
|
|
Block *b = blocks_.at(j);
|
|
|
|
if (Block::are_linked(a, b)) {
|
|
// These blocks are linked, ensure all the splits are linked too
|
|
|
|
foreach (const QVector<Block *> &split_list, splits_) {
|
|
NodeLinkCommand *blc = new NodeLinkCommand(
|
|
split_list.at(i), split_list.at(j), true);
|
|
blc->redo_now();
|
|
commands_.append(blc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// TrackSplitAtTimeCommand
|
|
//
|
|
void TrackSplitAtTimeCommand::prepare()
|
|
{
|
|
// Find Block that contains this time
|
|
Block *b = track_->block_containing_time(point_);
|
|
|
|
if (b) {
|
|
command_ = new BlockSplitCommand(b, point_);
|
|
}
|
|
}
|
|
|
|
}
|