Skip to content

Commit 74ba93d

Browse files
pool editors: add undo/redo
changelog: New Features/Pool Manager: undo/redo for Unit, Entity and Part editors
1 parent 2cdd289 commit 74ba93d

11 files changed

+177
-9
lines changed

Diff for: Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ SRC_POOL_PRJ_MGR = \
715715
src/pool-prj-mgr/pool-mgr/pool_notebook_frames.cpp\
716716
src/pool-prj-mgr/pool-mgr/pool_notebook_decals.cpp\
717717
src/pool-prj-mgr/pool-mgr/pool_notebook_parametric.cpp\
718+
src/util/history_manager.cpp\
718719
src/pool-prj-mgr/pool-mgr/editors/editor_base.cpp\
719720
src/pool-prj-mgr/pool-mgr/editors/unit_editor.cpp\
720721
src/pool-prj-mgr/pool-mgr/editors/part_editor.cpp\

Diff for: src/pool-prj-mgr/pool-mgr/editors/editor_base.cpp

+32
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,36 @@ void PoolEditorBase::label_make_item_link(Gtk::Label &la, ObjectType type)
4242
false);
4343
}
4444

45+
bool PoolEditorBase::can_redo() const
46+
{
47+
return history_manager.can_redo();
48+
}
49+
50+
bool PoolEditorBase::can_undo() const
51+
{
52+
return history_manager.can_undo();
53+
}
54+
55+
void PoolEditorBase::history_append(const std::string &comment)
56+
{
57+
history_manager.push(make_history_item(comment));
58+
}
59+
60+
void PoolEditorBase::undo()
61+
{
62+
if (!history_manager.can_undo())
63+
return;
64+
history_load(history_manager.undo());
65+
set_needs_save(true);
66+
}
67+
68+
void PoolEditorBase::redo()
69+
{
70+
if (!history_manager.can_redo())
71+
return;
72+
history_load(history_manager.redo());
73+
set_needs_save(true);
74+
}
75+
76+
4577
} // namespace horizon

Diff for: src/pool-prj-mgr/pool-mgr/editors/editor_base.hpp

+27-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "util/pool_goto_provider.hpp"
33
#include "util/item_set.hpp"
44
#include "rules/rules.hpp"
5+
#include "util/history_manager.hpp"
56
#include <gtkmm.h>
67

78
namespace horizon {
@@ -32,6 +33,13 @@ class PoolEditorBase : public PoolGotoProvider {
3233

3334
std::string filename;
3435

36+
void undo();
37+
void redo();
38+
39+
bool can_undo() const;
40+
bool can_redo() const;
41+
42+
void history_append(const std::string &comment);
3543
typedef sigc::signal<void> type_signal_needs_save;
3644
type_signal_needs_save signal_needs_save()
3745
{
@@ -52,17 +60,19 @@ class PoolEditorBase : public PoolGotoProvider {
5260
protected:
5361
IPool &pool;
5462

55-
void unset_needs_save()
63+
HistoryManager history_manager;
64+
65+
virtual std::unique_ptr<HistoryManager::HistoryItem> make_history_item(const std::string &comment) = 0;
66+
virtual void history_load(const HistoryManager::HistoryItem &it) = 0;
67+
68+
void set_needs_save()
5669
{
57-
needs_save = false;
58-
s_signal_needs_save.emit();
70+
set_needs_save(false);
5971
}
6072

61-
void set_needs_save()
73+
void unset_needs_save()
6274
{
63-
if (loading)
64-
throw std::runtime_error("set_needs_save called while loading");
65-
needs_save = true;
75+
needs_save = false;
6676
s_signal_needs_save.emit();
6777
}
6878

@@ -102,5 +112,15 @@ class PoolEditorBase : public PoolGotoProvider {
102112
bool loading = false;
103113
bool needs_save = false;
104114
type_signal_needs_save s_signal_needs_save;
115+
116+
void set_needs_save(bool from_undo)
117+
{
118+
if (loading)
119+
throw std::runtime_error("set_needs_save called while loading");
120+
needs_save = true;
121+
if (!from_undo)
122+
history_append("edit");
123+
s_signal_needs_save.emit();
124+
}
105125
};
106126
} // namespace horizon

Diff for: src/pool-prj-mgr/pool-mgr/editors/editor_window.cpp

+39-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,24 @@ EditorWindow::EditorWindow(ObjectType ty, const std::string &filename, IPool *p,
3030
save_button = Gtk::manage(new Gtk::Button());
3131
hb->pack_start(*save_button);
3232

33+
{
34+
auto box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
35+
box->get_style_context()->add_class("linked");
36+
37+
undo_button = Gtk::manage(new Gtk::Button);
38+
undo_button->set_tooltip_text("Undo");
39+
undo_button->set_image_from_icon_name("edit-undo-symbolic", Gtk::ICON_SIZE_BUTTON);
40+
box->pack_start(*undo_button, false, false, 0);
41+
42+
redo_button = Gtk::manage(new Gtk::Button);
43+
redo_button->set_tooltip_text("Redo");
44+
redo_button->set_image_from_icon_name("edit-redo-symbolic", Gtk::ICON_SIZE_BUTTON);
45+
box->pack_start(*redo_button, false, false, 0);
46+
47+
hb->pack_start(*box);
48+
box->show_all();
49+
}
50+
3351
check_button = Gtk::manage(new Gtk::MenuButton());
3452
{
3553
auto box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 5));
@@ -91,6 +109,17 @@ EditorWindow::EditorWindow(ObjectType ty, const std::string &filename, IPool *p,
91109
default:;
92110
}
93111

112+
update_undo_redo();
113+
undo_button->signal_clicked().connect([this] {
114+
iface->undo();
115+
update_undo_redo();
116+
});
117+
redo_button->signal_clicked().connect([this] {
118+
iface->redo();
119+
update_undo_redo();
120+
});
121+
122+
94123
auto box = Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL, 0));
95124
add(*box);
96125
box->show();
@@ -140,7 +169,10 @@ EditorWindow::EditorWindow(ObjectType ty, const std::string &filename, IPool *p,
140169
iface->signal_open_item().connect([this](ObjectType t, UUID uu) { s_signal_open_item.emit(t, uu); });
141170
if (!read_only) {
142171
save_button->set_sensitive(iface->get_needs_save());
143-
iface->signal_needs_save().connect([this] { save_button->set_sensitive(iface->get_needs_save()); });
172+
iface->signal_needs_save().connect([this] {
173+
save_button->set_sensitive(iface->get_needs_save());
174+
update_undo_redo();
175+
});
144176
}
145177
iface->signal_needs_save().connect([this] {
146178
if (iface->get_needs_save()) {
@@ -332,4 +364,10 @@ void EditorWindow::run_checks()
332364
check_label->set_text(s);
333365
}
334366

367+
void EditorWindow::update_undo_redo()
368+
{
369+
undo_button->set_sensitive(iface->can_undo());
370+
redo_button->set_sensitive(iface->can_redo());
371+
}
372+
335373
} // namespace horizon

Diff for: src/pool-prj-mgr/pool-mgr/editors/editor_window.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ class EditorWindow : public Gtk::Window, public PoolGotoProvider {
5252
Gtk::Label *check_label = nullptr;
5353
Gtk::InfoBar *info_bar = nullptr;
5454
Gtk::Label *info_bar_label = nullptr;
55+
Gtk::Button *undo_button = nullptr;
56+
Gtk::Button *redo_button = nullptr;
57+
void update_undo_redo();
5558
class IPool &pool;
5659
class PoolParametric *pool_parametric;
5760
bool need_update = false;

Diff for: src/pool-prj-mgr/pool-mgr/editors/entity_editor.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ EntityEditor::EntityEditor(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Buil
102102
: Gtk::Box(cobject), PoolEditorBase(fn, p),
103103
entity(filename.size() ? Entity::new_from_file(filename, pool) : Entity(UUID::random()))
104104
{
105+
history_append("init");
106+
105107
x->get_widget("entity_name", name_entry);
106108
x->get_widget("entity_manufacturer", manufacturer_entry);
107109
{
@@ -292,6 +294,26 @@ ObjectType EntityEditor::get_type() const
292294
return ObjectType::ENTITY;
293295
}
294296

297+
class HistoryItemEntity : public HistoryManager::HistoryItem {
298+
public:
299+
HistoryItemEntity(const Entity &e, const std::string &cm) : HistoryManager::HistoryItem(cm), entity(e)
300+
{
301+
}
302+
Entity entity;
303+
};
304+
305+
std::unique_ptr<HistoryManager::HistoryItem> EntityEditor::make_history_item(const std::string &comment)
306+
{
307+
return std::make_unique<HistoryItemEntity>(entity, comment);
308+
}
309+
310+
void EntityEditor::history_load(const HistoryManager::HistoryItem &it)
311+
{
312+
const auto &x = dynamic_cast<const HistoryItemEntity &>(it);
313+
entity = x.entity;
314+
load();
315+
}
316+
295317
EntityEditor *EntityEditor::create(const std::string &fn, class IPool &p)
296318
{
297319
EntityEditor *w;

Diff for: src/pool-prj-mgr/pool-mgr/editors/entity_editor.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,8 @@ class EntityEditor : public Gtk::Box, public PoolEditorBase {
5151
SortHelper sort_helper;
5252

5353
void bind_entry(Gtk::Entry *e, std::string &s);
54+
55+
std::unique_ptr<HistoryManager::HistoryItem> make_history_item(const std::string &comment) override;
56+
void history_load(const HistoryManager::HistoryItem &it) override;
5457
};
5558
} // namespace horizon

Diff for: src/pool-prj-mgr/pool-mgr/editors/part_editor.cpp

+23-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ PartEditor::PartEditor(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder>
200200
: Gtk::Box(cobject), PoolEditorBase(fn, po),
201201
part(filename.size() ? Part::new_from_file(filename, pool) : Part(UUID::random())), pool_parametric(pp)
202202
{
203-
203+
history_append("init");
204204
auto add_entry = [x](const char *name) {
205205
Gtk::Box *t;
206206
x->get_widget(name, t);
@@ -1172,6 +1172,28 @@ ObjectType PartEditor::get_type() const
11721172
return ObjectType::PART;
11731173
}
11741174

1175+
class HistoryItemPart : public HistoryManager::HistoryItem {
1176+
public:
1177+
HistoryItemPart(const Part &p, const std::string &cm) : HistoryManager::HistoryItem(cm), part(p)
1178+
{
1179+
}
1180+
Part part;
1181+
};
1182+
1183+
std::unique_ptr<HistoryManager::HistoryItem> PartEditor::make_history_item(const std::string &comment)
1184+
{
1185+
return std::make_unique<HistoryItemPart>(part, comment);
1186+
}
1187+
1188+
void PartEditor::history_load(const HistoryManager::HistoryItem &it)
1189+
{
1190+
const auto &x = dynamic_cast<const HistoryItemPart &>(it);
1191+
part = x.part;
1192+
part.update_refs(pool);
1193+
load();
1194+
}
1195+
1196+
11751197
PartEditor *PartEditor::create(const std::string &fn, IPool &po, PoolParametric &pp)
11761198
{
11771199
PartEditor *w;

Diff for: src/pool-prj-mgr/pool-mgr/editors/part_editor.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -151,5 +151,8 @@ class PartEditor : public Gtk::Box, public PoolEditorBase {
151151
class OrderableMPNEditor *create_orderable_MPN_editor(const UUID &uu);
152152

153153
UUID pending_base_part;
154+
155+
std::unique_ptr<HistoryManager::HistoryItem> make_history_item(const std::string &comment) override;
156+
void history_load(const HistoryManager::HistoryItem &it) override;
154157
};
155158
} // namespace horizon

Diff for: src/pool-prj-mgr/pool-mgr/editors/unit_editor.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ UnitEditor::UnitEditor(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder>
136136
: Gtk::Box(cobject), PoolEditorBase(fn, p),
137137
unit(filename.size() ? Unit::new_from_file(filename) : Unit(UUID::random()))
138138
{
139+
history_append("init");
139140
x->get_widget("unit_name", name_entry);
140141
x->get_widget("unit_manufacturer", manufacturer_entry);
141142
x->get_widget("unit_pins", pins_listbox);
@@ -383,6 +384,26 @@ ObjectType UnitEditor::get_type() const
383384
return ObjectType::UNIT;
384385
}
385386

387+
class HistoryItemUnit : public HistoryManager::HistoryItem {
388+
public:
389+
HistoryItemUnit(const Unit &u, const std::string &cm) : HistoryManager::HistoryItem(cm), unit(u)
390+
{
391+
}
392+
Unit unit;
393+
};
394+
395+
std::unique_ptr<HistoryManager::HistoryItem> UnitEditor::make_history_item(const std::string &comment)
396+
{
397+
return std::make_unique<HistoryItemUnit>(unit, comment);
398+
}
399+
400+
void UnitEditor::history_load(const HistoryManager::HistoryItem &it)
401+
{
402+
const auto &x = dynamic_cast<const HistoryItemUnit &>(it);
403+
unit = x.unit;
404+
load();
405+
}
406+
386407
UnitEditor *UnitEditor::create(const std::string &fn, class IPool &p)
387408
{
388409
UnitEditor *w;

Diff for: src/pool-prj-mgr/pool-mgr/editors/unit_editor.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,8 @@ class UnitEditor : public Gtk::Box, public PoolEditorBase {
4848
SortHelper sort_helper;
4949
void load();
5050
bool propagating = false;
51+
52+
std::unique_ptr<HistoryManager::HistoryItem> make_history_item(const std::string &comment) override;
53+
void history_load(const HistoryManager::HistoryItem &it) override;
5154
};
5255
} // namespace horizon

0 commit comments

Comments
 (0)