Skip to content

Commit

Permalink
Add basic undo/redo functionality
Browse files Browse the repository at this point in the history
This works by simply keeping backups the entire relevant state of the
Wobbly project after each action and rolling back to such states on
undo/redo. This is feasible since Wobbly's internal state is not very
memory-intensive.

A drawback is that all list or table models have to be repopulated from
scratch, so selections in dialogs are cleared on every undo/redo. This
could be improved later on by saving the UI state before a rollback and
restoring it after it is completed.

Fixes dubhater#15 .
  • Loading branch information
arch1t3cht committed Apr 17, 2023
1 parent 7c41893 commit 4fd9b8a
Show file tree
Hide file tree
Showing 16 changed files with 361 additions and 16 deletions.
8 changes: 8 additions & 0 deletions src/shared/BookmarksModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,11 @@ void BookmarksModel::erase(int frame) {

endRemoveRows();
}

void BookmarksModel::clear() {
if (!size()) return;

beginRemoveRows(QModelIndex(), 0, size() - 1);
BookmarkMap::clear();
endRemoveRows();
}
4 changes: 3 additions & 1 deletion src/shared/BookmarksModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ SOFTWARE.
#include "WobblyTypes.h"


class BookmarksModel : public QAbstractTableModel, private BookmarkMap {
class BookmarksModel : public QAbstractTableModel, public BookmarkMap {
Q_OBJECT

public:
Expand Down Expand Up @@ -62,6 +62,8 @@ class BookmarksModel : public QAbstractTableModel, private BookmarkMap {
void insert(const value_type &bookmark);

void erase(int frame);

void clear();
};

#endif // BOOKMARKSMODEL_H
2 changes: 1 addition & 1 deletion src/shared/CombedFramesModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ SOFTWARE.
#include <QAbstractListModel>


class CombedFramesModel : public QAbstractListModel, private std::set<int> {
class CombedFramesModel : public QAbstractListModel, public std::set<int> {
Q_OBJECT

public:
Expand Down
9 changes: 9 additions & 0 deletions src/shared/CustomListsModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@ void CustomListsModel::erase(int list_index) {
}


void CustomListsModel::clear() {
if (!size()) return;

beginRemoveRows(QModelIndex(), 0, size() - 1);
CustomListVector::clear();
endRemoveRows();
}


void CustomListsModel::moveCustomListUp(int list_index) {
if (beginMoveRows(QModelIndex(), list_index, list_index, QModelIndex(), list_index - 1)) {
std::swap(at(list_index - 1), at(list_index));
Expand Down
4 changes: 3 additions & 1 deletion src/shared/CustomListsModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ SOFTWARE.
#include "WobblyTypes.h"


class CustomListsModel : public QAbstractTableModel, private CustomListVector {
class CustomListsModel : public QAbstractTableModel, public CustomListVector {
Q_OBJECT

enum Columns {
Expand Down Expand Up @@ -61,6 +61,8 @@ class CustomListsModel : public QAbstractTableModel, private CustomListVector {

void erase(int list_index);

void clear();

void moveCustomListUp(int list_index);

void moveCustomListDown(int list_index);
Expand Down
2 changes: 1 addition & 1 deletion src/shared/FrameRangesModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct FrameRange {
};


class FrameRangesModel : public QAbstractTableModel, private std::map<int, FrameRange> {
class FrameRangesModel : public QAbstractTableModel, public std::map<int, FrameRange> {
Q_OBJECT

enum Columns {
Expand Down
8 changes: 8 additions & 0 deletions src/shared/FrozenFramesModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,11 @@ void FrozenFramesModel::erase(int freeze_frame) {

endRemoveRows();
}

void FrozenFramesModel::clear() {
if (!size()) return;

beginRemoveRows(QModelIndex(), 0, size() - 1);
FreezeFrameMap::clear();
endRemoveRows();
}
4 changes: 3 additions & 1 deletion src/shared/FrozenFramesModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ SOFTWARE.
#include "WobblyTypes.h"


class FrozenFramesModel : public QAbstractTableModel, private FreezeFrameMap {
class FrozenFramesModel : public QAbstractTableModel, public FreezeFrameMap {
Q_OBJECT

enum Columns {
Expand Down Expand Up @@ -57,6 +57,8 @@ class FrozenFramesModel : public QAbstractTableModel, private FreezeFrameMap {
void insert(const value_type &freeze_frame);

void erase(int freeze_frame);

void clear();
};

#endif // FROZENFRAMESMODEL_H
8 changes: 8 additions & 0 deletions src/shared/PresetsModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,11 @@ void PresetsModel::erase(const std::string &preset_name) {

endRemoveRows();
}

void PresetsModel::clear() {
if (!size()) return;

beginRemoveRows(QModelIndex(), 0, size() - 1);
PresetMap::clear();
endRemoveRows();
}
4 changes: 3 additions & 1 deletion src/shared/PresetsModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ SOFTWARE.
#include "WobblyTypes.h"


class PresetsModel : public QAbstractListModel, private PresetMap {
class PresetsModel : public QAbstractListModel, public PresetMap {
Q_OBJECT

public:
Expand All @@ -46,6 +46,8 @@ class PresetsModel : public QAbstractListModel, private PresetMap {
void insert(const value_type &preset);

void erase(const std::string &preset_name);

void clear();
};

#endif // PRESETSMODEL_H
6 changes: 6 additions & 0 deletions src/shared/SectionsModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ void SectionsModel::erase(int section_start) {
endRemoveRows();
}

void SectionsModel::clear() {
beginRemoveRows(QModelIndex(), 0, size());
SectionMap::clear();
endRemoveRows();
}


void SectionsModel::setSectionPresetName(int section_start, size_t preset_index, const std::string &preset_name) {
SectionMap::iterator it = find(section_start);
Expand Down
4 changes: 3 additions & 1 deletion src/shared/SectionsModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ SOFTWARE.
#include "WobblyTypes.h"


class SectionsModel : public QAbstractTableModel, private SectionMap {
class SectionsModel : public QAbstractTableModel, public SectionMap {
Q_OBJECT

public:
Expand Down Expand Up @@ -55,6 +55,8 @@ class SectionsModel : public QAbstractTableModel, private SectionMap {

void erase(int section_start);

void clear();

void setSectionPresetName(int section_start, size_t preset_index, const std::string &preset_name);

void appendSectionPreset(int section_start, const std::string &preset_name);
Expand Down
126 changes: 118 additions & 8 deletions src/shared/WobblyProject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2589,6 +2589,18 @@ std::map<size_t, size_t> WobblyProject::getCMatchSequences(int minimum) const {
}


void WobblyProject::updateOrphanFrames() {
// Find the ends manually so this is not O(#sections^2)
auto it = sections->cbegin();
while (it != sections->cend()) {
int section_start = it->second.start;
it++;
int section_end = it == sections->cend() ? getNumFrames(PostSource) : it->second.start;

updateSectionOrphanFrames(section_start, section_end);
}
}

void WobblyProject::updateSectionOrphanFrames(int section_start, int section_end) {
if (getMatch(section_start) == 'n')
addOrphanFrame({ section_start, 'n' });
Expand Down Expand Up @@ -2809,6 +2821,108 @@ void WobblyProject::setModified(bool modified) {
}


std::string WobblyProject::getUndoDescription() {
if (undo_stack.size() <= 1)
return "";
return undo_stack.back().description;
}

std::string WobblyProject::getRedoDescription() {
if (redo_stack.empty())
return "";
return redo_stack.back().description;
}

void WobblyProject::restoreState(UndoStep state) {
matches = state.matches;
decimated_frames = state.decimated_frames;
pattern_guessing = state.pattern_guessing;

presets->clear();
for (auto const& p : state.presets)
presets->insert(p);

custom_lists->clear();
for (auto const& c : state.custom_lists) {
custom_lists->push_back(c);
custom_lists->back().ranges = std::make_shared<FrameRangesModel>();
for (auto const& r : *c.ranges)
custom_lists->back().ranges->insert(r);
}

combed_frames->clear();
for (auto const& c : state.combed_frames)
combed_frames->insert(c);

frozen_frames->clear();
for (auto const& f : state.frozen_frames)
frozen_frames->insert(f);

sections->clear();
for (auto const& s : state.sections)
sections->insert(s);

bookmarks->clear();
for (auto const& b : state.bookmarks)
bookmarks->insert(b);
}

void WobblyProject::commit(std::string description) {
UndoStep step = {
.description = description,
.matches = matches,
.decimated_frames = decimated_frames,
.pattern_guessing = pattern_guessing,

.presets = *presets,
.custom_lists = *custom_lists,
.combed_frames = *combed_frames,
.frozen_frames = *frozen_frames,
.sections = *sections,
.bookmarks = *bookmarks,
};
for (auto &cl : step.custom_lists) {
std::shared_ptr<FrameRangesModel> oldranges = cl.ranges;
cl.ranges = std::make_shared<FrameRangesModel>();

for (auto const& r : *oldranges)
cl.ranges->insert(r);
}

undo_stack.push_back(step);

redo_stack.clear();

while (undo_stack.size() > undo_steps)
undo_stack.pop_front();
}

void WobblyProject::undo() {
if (undo_stack.size() <= 1) return;
redo_stack.push_back(undo_stack.back());
undo_stack.pop_back();
restoreState(undo_stack.back());
}

void WobblyProject::redo() {
if (redo_stack.empty()) return;
restoreState(redo_stack.back());
undo_stack.push_back(redo_stack.back());
redo_stack.pop_back();
}

void WobblyProject::setUndoSteps(size_t steps) {
undo_steps = steps;
if (undo_steps < redo_stack.size()) {
undo_stack.clear();
while (undo_steps < redo_stack.size())
redo_stack.pop_front();
}
while (undo_steps < undo_stack.size() + redo_stack.size())
undo_stack.pop_front();
}


int WobblyProject::getZoom() const {
return zoom;
}
Expand Down Expand Up @@ -3495,8 +3609,7 @@ void WobblyProject::guessProjectPatternsFromMics(int minimum_length, int edge_cu
for (auto it = sections->cbegin(); it != sections->cend(); it++)
guessSectionPatternsFromMics(it->second.start, minimum_length, edge_cutoff, use_patterns, drop_duplicate);

for (auto it = sections->cbegin(); it != sections->cend(); it++)
updateSectionOrphanFrames(it->second.start, getSectionEnd(it->second.start));
updateOrphanFrames();

pattern_guessing.method = PatternGuessingFromMics;
pattern_guessing.minimum_length = minimum_length;
Expand All @@ -3514,8 +3627,7 @@ void WobblyProject::guessProjectPatternsFromDMetrics(int minimum_length, int edg
for (auto it = sections->cbegin(); it != sections->cend(); it++)
guessSectionPatternsFromDMetrics(it->second.start, minimum_length, edge_cutoff, use_patterns, drop_duplicate);

for (auto it = sections->cbegin(); it != sections->cend(); it++)
updateSectionOrphanFrames(it->second.start, getSectionEnd(it->second.start));
updateOrphanFrames();

pattern_guessing.method = PatternGuessingFromDMetrics;
pattern_guessing.minimum_length = minimum_length;
Expand All @@ -3532,8 +3644,7 @@ void WobblyProject::guessProjectPatternsFromMicsAndDMetrics(int minimum_length,
for (auto it = sections->cbegin(); it != sections->cend(); it++)
guessSectionPatternsFromMicsAndDMetrics(it->second.start, minimum_length, edge_cutoff, use_patterns, drop_duplicate);

for (auto it = sections->cbegin(); it != sections->cend(); it++)
updateSectionOrphanFrames(it->second.start, getSectionEnd(it->second.start));
updateOrphanFrames();

pattern_guessing.method = PatternGuessingFromMicsAndDMetrics;
pattern_guessing.minimum_length = minimum_length;
Expand Down Expand Up @@ -3668,8 +3779,7 @@ void WobblyProject::guessProjectPatternsFromMatches(int minimum_length, int edge
for (auto it = sections->cbegin(); it != sections->cend(); it++)
guessSectionPatternsFromMatches(it->second.start, minimum_length, edge_cutoff, use_third_n_match, drop_duplicate);

for (auto it = sections->cbegin(); it != sections->cend(); it++)
updateSectionOrphanFrames(it->second.start, getSectionEnd(it->second.start));
updateOrphanFrames();

pattern_guessing.method = PatternGuessingFromMatches;
pattern_guessing.minimum_length = minimum_length;
Expand Down
Loading

0 comments on commit 4fd9b8a

Please sign in to comment.