1 /*
2  * gnote
3  *
4  * Copyright (C) 2013,2016-2017,2019 Aurimas Cernius
5  * Copyright (C) 2009 Hubert Figuiere
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 
23 #ifndef __UNDO_HPP_
24 #define __UNDO_HPP_
25 
26 #include <stack>
27 
28 #include <sigc++/signal.h>
29 #include <gtkmm/textbuffer.h>
30 #include <gtkmm/texttag.h>
31 #include <gtkmm/textiter.h>
32 
33 #include "noncopyable.hpp"
34 #include "notebuffer.hpp"
35 #include "utils.hpp"
36 
37 namespace gnote {
38 
39 
40 class EditAction
41 {
42 public:
~EditAction()43   virtual ~EditAction() {}
44   virtual void undo (Gtk::TextBuffer * buffer) = 0;
45   virtual void redo (Gtk::TextBuffer * buffer) = 0;
46   virtual void merge (EditAction * action) = 0;
47   virtual bool can_merge (const EditAction * action) const = 0;
48   virtual void destroy () = 0;
49 };
50 
51 class EditActionGroup
52   : public EditAction
53 {
54 public:
55   EditActionGroup(bool start);
56   virtual void undo(Gtk::TextBuffer *buffer) override;
57   virtual void redo(Gtk::TextBuffer *buffer) override;
58   virtual void merge(EditAction *action) override;
59   virtual bool can_merge(const EditAction *action) const override;
60   virtual void destroy() override;
is_start() const61   bool is_start() const
62     {
63       return m_start;
64     }
65 private:
66   bool m_start;
67 };
68 
69 class ChopBuffer
70   : public Gtk::TextBuffer
71 {
72 public:
73   typedef Glib::RefPtr<ChopBuffer> Ptr;
74   ChopBuffer(const Glib::RefPtr<Gtk::TextTagTable> & table);
75   utils::TextRange add_chop(const Gtk::TextIter & start_iter, const Gtk::TextIter & end_iter);
76 };
77 
78 
79 class SplitterAction
80   : public EditAction
81 {
82 public:
83   struct TagData {
84     int start;
85     int end;
86     Glib::RefPtr<Gtk::TextTag> tag;
87   };
88 
get_chop() const89   const utils::TextRange & get_chop() const
90     {
91       return m_chop;
92     }
get_split_tags() const93   const std::vector<TagData> & get_split_tags() const
94     {
95       return m_splitTags;
96     }
97   void split(Gtk::TextIter iter, Gtk::TextBuffer *);
98   void add_split_tag(const Gtk::TextIter &, const Gtk::TextIter &,
99                      const Glib::RefPtr<Gtk::TextTag> tag);
100 protected:
101   SplitterAction();
102   int get_split_offset() const;
103   void apply_split_tag(Gtk::TextBuffer *);
104   void remove_split_tags(Gtk::TextBuffer *);
105   std::vector<TagData> m_splitTags;
106   utils::TextRange   m_chop;
107 };
108 
109 
110 
111 class InsertAction
112   : public SplitterAction
113 {
114 public:
115   InsertAction(const Gtk::TextIter & start, const Glib::ustring & text, int length,
116                const ChopBuffer::Ptr & chop_buf);
117   virtual void undo(Gtk::TextBuffer * buffer) override;
118   virtual void redo(Gtk::TextBuffer * buffer) override;
119   virtual void merge(EditAction * action) override;
120   virtual bool can_merge(const EditAction * action) const override;
121   virtual void destroy() override;
122 
123 private:
124   int m_index;
125   bool m_is_paste;
126 };
127 
128 
129 class EraseAction
130   : public SplitterAction
131 {
132 public:
133   EraseAction(const Gtk::TextIter & start_iter, const Gtk::TextIter & end_iter,
134                const ChopBuffer::Ptr & chop_buf);
135   virtual void undo(Gtk::TextBuffer * buffer) override;
136   virtual void redo(Gtk::TextBuffer * buffer) override;
137   virtual void merge(EditAction * action) override;
138   virtual bool can_merge(const EditAction * action) const override;
139   virtual void destroy() override;
140 
141 private:
142   int m_start;
143   int m_end;
144   bool m_is_forward;
145   bool m_is_cut;
146 };
147 
148 
149 
150 class TagApplyAction
151   : public EditAction
152 {
153 public:
154   TagApplyAction(const Glib::RefPtr<Gtk::TextTag> &, const Gtk::TextIter & start, const Gtk::TextIter & end);
155   virtual void undo(Gtk::TextBuffer * buffer) override;
156   virtual void redo(Gtk::TextBuffer * buffer) override;
157   virtual void merge(EditAction * action) override;
158   virtual bool can_merge(const EditAction * action) const override;
159   virtual void destroy() override;
160 
161 private:
162   Glib::RefPtr<Gtk::TextTag> m_tag;
163   int m_start;
164   int m_end;
165 };
166 
167 
168 class TagRemoveAction
169   : public EditAction
170 {
171 public:
172   TagRemoveAction(const Glib::RefPtr<Gtk::TextTag> &, const Gtk::TextIter & start, const Gtk::TextIter & end);
173   virtual void undo(Gtk::TextBuffer * buffer) override;
174   virtual void redo(Gtk::TextBuffer * buffer) override;
175   virtual void merge(EditAction * action) override;
176   virtual bool can_merge(const EditAction * action) const override;
177   virtual void destroy() override;
178 private:
179   Glib::RefPtr<Gtk::TextTag> m_tag;
180   int m_start;
181   int m_end;
182 };
183 
184 
185 class ChangeDepthAction
186   : public EditAction
187 {
188 public:
189   ChangeDepthAction(int line, bool direction);
190   virtual void undo(Gtk::TextBuffer * buffer) override;
191   virtual void redo(Gtk::TextBuffer * buffer) override;
192   virtual void merge(EditAction * action) override;
193   virtual bool can_merge(const EditAction * action) const override;
194   virtual void destroy() override;
195 private:
196   int m_line;
197   bool m_direction;
198 };
199 
200 
201 
202 class InsertBulletAction
203   : public EditAction
204 {
205 public:
206   InsertBulletAction(int offset, int depth);
207   virtual void undo(Gtk::TextBuffer * buffer) override;
208   virtual void redo(Gtk::TextBuffer * buffer) override;
209   virtual void merge(EditAction * action) override;
210   virtual bool can_merge(const EditAction * action) const override;
211   virtual void destroy() override;
212 private:
213   int m_offset;
214   int m_depth;
215 };
216 
217 class UndoManager
218   : public gnote::NonCopyable
219 {
220 public:
221   /** the buffer it NOT owned by the UndoManager
222    *  it is assume to have a longer life than UndoManager
223    *  Actually the UndoManager belong to the buffer.
224    */
225   UndoManager(NoteBuffer * buffer);
226   ~UndoManager();
get_can_undo()227   bool get_can_undo()
228     {
229       return !m_undo_stack.empty();
230     }
get_can_redo()231   bool get_can_redo()
232     {
233       return !m_redo_stack.empty();
234     }
undo()235   void undo()
236     {
237       undo_redo(m_undo_stack, m_redo_stack, true);
238     }
redo()239   void redo()
240     {
241       undo_redo(m_redo_stack, m_undo_stack, false);
242     }
freeze_undo()243   void freeze_undo()
244     {
245       ++m_frozen_cnt;
246     }
thaw_undo()247   void thaw_undo()
248     {
249       --m_frozen_cnt;
250     }
251 
252   void undo_redo(std::stack<EditAction *> &, std::stack<EditAction *> &, bool);
253   void undo_redo_action(EditAction & action, bool);
254   void clear_undo_history();
255   void add_undo_action(EditAction * action);
256 
signal_undo_changed()257   sigc::signal<void> & signal_undo_changed()
258     { return m_undo_changed; }
259 
260 private:
261 
262   void clear_action_stack(std::stack<EditAction *> &);
263   void on_insert_text(const Gtk::TextIter &, const Glib::ustring &, int);
264   void on_delete_range(const Gtk::TextIter &, const Gtk::TextIter &);
265   void on_tag_applied(const Glib::RefPtr<Gtk::TextTag> &,
266                       const Gtk::TextIter &, const Gtk::TextIter &);
267   void on_tag_removed(const Glib::RefPtr<Gtk::TextTag> &,
268                       const Gtk::TextIter &, const Gtk::TextIter &);
269 
270   void on_change_depth(int, bool);
271   void on_bullet_inserted(int, int);
272 
273   guint m_frozen_cnt;
274   bool m_try_merge;
275   NoteBuffer * m_buffer;
276   ChopBuffer::Ptr m_chop_buffer;
277   std::stack<EditAction *> m_undo_stack;
278   std::stack<EditAction *> m_redo_stack;
279   sigc::signal<void> m_undo_changed;
280 };
281 
282 
283 }
284 
285 #endif
286