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.
272 lines
5.7 KiB
C++
272 lines
5.7 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 "undostack.h"
|
|
|
|
#include <QCoreApplication>
|
|
|
|
namespace olive
|
|
{
|
|
|
|
const int UndoStack::k_max_undo_commands = 200;
|
|
|
|
class EmptyCommand : public UndoCommand {
|
|
public:
|
|
EmptyCommand()
|
|
{
|
|
}
|
|
|
|
virtual Project *get_relevant_project() const override
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
protected:
|
|
virtual void redo() override
|
|
{
|
|
}
|
|
virtual void undo() override
|
|
{
|
|
}
|
|
};
|
|
|
|
UndoStack::UndoStack()
|
|
{
|
|
undo_action_ = new QAction();
|
|
connect(undo_action_, &QAction::triggered, this, &UndoStack::undo);
|
|
|
|
redo_action_ = new QAction();
|
|
connect(redo_action_, &QAction::triggered, this, &UndoStack::redo);
|
|
|
|
clear();
|
|
update_actions();
|
|
}
|
|
|
|
UndoStack::~UndoStack()
|
|
{
|
|
clear();
|
|
|
|
delete undo_action_;
|
|
delete redo_action_;
|
|
}
|
|
|
|
void UndoStack::push(UndoCommand *command, const QString &name)
|
|
{
|
|
MultiUndoCommand *mcu = dynamic_cast<MultiUndoCommand *>(command);
|
|
if (mcu && mcu->child_count() == 0) {
|
|
delete command;
|
|
return;
|
|
}
|
|
|
|
// Clear any redoable commands
|
|
this->beginRemoveRows(QModelIndex(), commands_.size(),
|
|
commands_.size() + undone_commands_.size());
|
|
if (can_redo()) {
|
|
for (auto it = undone_commands_.cbegin(); it != undone_commands_.cend();
|
|
it++) {
|
|
delete (*it).command;
|
|
}
|
|
undone_commands_.clear();
|
|
}
|
|
this->endRemoveRows();
|
|
|
|
// Do command and push
|
|
this->beginInsertRows(QModelIndex(), commands_.size(), commands_.size());
|
|
command->redo_and_set_modified();
|
|
commands_.push_back({ command, name });
|
|
this->endInsertRows();
|
|
|
|
// Delete oldest
|
|
if (commands_.size() > k_max_undo_commands) {
|
|
this->beginRemoveRows(QModelIndex(), 0, 0);
|
|
delete commands_.front().command;
|
|
commands_.pop_front();
|
|
this->endRemoveRows();
|
|
}
|
|
|
|
update_actions();
|
|
}
|
|
|
|
void UndoStack::jump(size_t index)
|
|
{
|
|
while (commands_.size() > index) {
|
|
undo();
|
|
}
|
|
while (commands_.size() < index) {
|
|
redo();
|
|
}
|
|
}
|
|
|
|
void UndoStack::undo()
|
|
{
|
|
if (can_undo()) {
|
|
// Undo most recently done command
|
|
commands_.back().command->undo_and_set_modified();
|
|
|
|
// Place at the front of the "undone commands" list
|
|
undone_commands_.push_front(commands_.back());
|
|
|
|
// Remove undone command from the commands list
|
|
commands_.pop_back();
|
|
|
|
// Update actions
|
|
update_actions();
|
|
}
|
|
}
|
|
|
|
void UndoStack::redo()
|
|
{
|
|
if (can_redo()) {
|
|
// Redo most recently undone command
|
|
undone_commands_.front().command->redo_and_set_modified();
|
|
|
|
// Place at the back of the done commands list
|
|
commands_.push_back(undone_commands_.front());
|
|
|
|
// Remove done command from undone list
|
|
undone_commands_.pop_front();
|
|
|
|
// Update actions
|
|
update_actions();
|
|
}
|
|
}
|
|
|
|
void UndoStack::clear()
|
|
{
|
|
this->beginResetModel();
|
|
|
|
for (auto it = commands_.cbegin(); it != commands_.cend(); it++) {
|
|
delete (*it).command;
|
|
}
|
|
commands_.clear();
|
|
for (auto it = undone_commands_.cbegin(); it != undone_commands_.cend();
|
|
it++) {
|
|
delete (*it).command;
|
|
}
|
|
undone_commands_.clear();
|
|
|
|
this->endResetModel();
|
|
|
|
push(new EmptyCommand(), tr("New/Open Project"));
|
|
}
|
|
|
|
bool UndoStack::can_undo() const
|
|
{
|
|
return !commands_.empty() &&
|
|
!dynamic_cast<EmptyCommand *>(commands_.back().command);
|
|
}
|
|
|
|
void UndoStack::update_actions()
|
|
{
|
|
undo_action_->setEnabled(can_undo());
|
|
redo_action_->setEnabled(can_redo());
|
|
|
|
undo_action_->setText(
|
|
QCoreApplication::translate("UndoStack", "Undo %1")
|
|
.arg(can_undo() ? commands_.back().name : QString()));
|
|
redo_action_->setText(
|
|
QCoreApplication::translate("UndoStack", "Redo %1")
|
|
.arg(can_redo() ? undone_commands_.front().name : QString()));
|
|
|
|
emit index_changed(commands_.size());
|
|
}
|
|
|
|
int UndoStack::columnCount(const QModelIndex &parent) const
|
|
{
|
|
if (parent.isValid()) {
|
|
return 0;
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
QVariant UndoStack::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (role == Qt::DisplayRole) {
|
|
switch (index.column()) {
|
|
case 0:
|
|
return index.row() + 1;
|
|
case 1: {
|
|
std::list<CommandEntry>::const_iterator it;
|
|
size_t real_index = index.row();
|
|
if (real_index < commands_.size()) {
|
|
it = commands_.cbegin();
|
|
} else {
|
|
real_index -= commands_.size();
|
|
it = undone_commands_.cbegin();
|
|
}
|
|
for (size_t i = 0; i < real_index; i++) {
|
|
it++;
|
|
}
|
|
const QString &name = (*it).name;
|
|
return (name.isEmpty()) ? tr("Command") : name;
|
|
}
|
|
}
|
|
} else if (role == Qt::ForegroundRole) {
|
|
if (size_t(index.row()) >= commands_.size()) {
|
|
return QVariant(QColor(Qt::gray));
|
|
}
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
QModelIndex UndoStack::index(int row, int column,
|
|
const QModelIndex &parent) const
|
|
{
|
|
return createIndex(row, column, nullptr);
|
|
}
|
|
|
|
QModelIndex UndoStack::parent(const QModelIndex &index) const
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
int UndoStack::rowCount(const QModelIndex &parent) const
|
|
{
|
|
if (parent.isValid()) {
|
|
return 0;
|
|
}
|
|
|
|
return commands_.size() + undone_commands_.size();
|
|
}
|
|
|
|
QVariant UndoStack::headerData(int section, Qt::Orientation orientation,
|
|
int role) const
|
|
{
|
|
if (role == Qt::DisplayRole) {
|
|
switch (section) {
|
|
case 0:
|
|
return QStringLiteral("Number");
|
|
case 1:
|
|
return QStringLiteral("Action");
|
|
}
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
bool UndoStack::hasChildren(const QModelIndex &parent) const
|
|
{
|
|
return !parent.isValid();
|
|
}
|
|
|
|
}
|