Code

Fix for #1684042 (undo while adding a shape with the mouse results in a crash.)
[inkscape.git] / src / event-log.cpp
1 /*
2  * Author:
3  *   Gustav Broberg <broberg@kth.se>
4  *
5  * Copyright (c) 2006 Authors
6  *
7  * Released under GNU GPL, read the file 'COPYING' for more information
8  */
10 #include <glibmm/i18n.h>
12 #include "desktop.h"
13 #include "event-log.h"
14 #include "inkscape.h"
15 #include "util/ucompose.hpp"
17 namespace Inkscape {
19 EventLog::EventLog(SPDocument* document) :
20     UndoStackObserver(),
21     _connected (false),
22     _document (document),
23     _event_list_store (Gtk::TreeStore::create(_columns)),
24     _event_list_selection (NULL),
25     _event_list_view (NULL),
26     _curr_event_parent (NULL),
27     _notifications_blocked (false),
28     _callback_connections (NULL)
29 {
30     // add initial pseudo event
31     Gtk::TreeRow curr_row = *(_event_list_store->append());
32     _curr_event = _last_event = curr_row;
33     
34     curr_row[_columns.description] = _("[Unchanged]");
35     curr_row[_columns.type] = SP_VERB_FILE_NEW;
36 }
38 EventLog::~EventLog() { }
40 void
41 EventLog::notifyUndoEvent(Event* log) 
42 {
43     if ( !_notifications_blocked ) {
44     
45         // make sure the supplied event matches the next undoable event
46         g_return_if_fail ( _getUndoEvent() && (*(_getUndoEvent()))[_columns.event] == log );
48         // if we're on the first child event...
49         if ( _curr_event->parent() &&
50              _curr_event == _curr_event->parent()->children().begin() )
51         {
52             // ...back up to the parent
53             _curr_event = _curr_event->parent();
54             _curr_event_parent = (iterator)NULL;
56         } else {
58             // if we're about to leave a branch, collapse it
59             if ( !_curr_event->children().empty() && _connected ) {
60                 (*_callback_connections)[CALLB_COLLAPSE].block();
61                 _event_list_view->collapse_row(_event_list_store->get_path(_curr_event));
62                 (*_callback_connections)[CALLB_COLLAPSE].block(false);
63             }
65             --_curr_event;
67             // if we're entering a branch, move to the end of it
68             if (!_curr_event->children().empty()) {
69                 _curr_event_parent = _curr_event;
70                 _curr_event = _curr_event->children().end();
71                 --_curr_event;
72             }
73         }
75         // update the view
76         if (_connected) {
77             (*_callback_connections)[CALLB_SELECTION_CHANGE].block();
78             (*_callback_connections)[CALLB_EXPAND].block();
80             Gtk::TreePath curr_path = _event_list_store->get_path(_curr_event);
81             _event_list_view->expand_to_path(curr_path);
82             _event_list_selection->select(curr_path);
83             _event_list_view->scroll_to_row(curr_path);
85             (*_callback_connections)[CALLB_EXPAND].block(false);
86             (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false);
87         }
89         updateUndoVerbs();
90     }
92 }
94 void
95 EventLog::notifyRedoEvent(Event* log)
96 {
97     if ( !_notifications_blocked ) {
99         // make sure the supplied event matches the next redoable event
100         g_return_if_fail ( _getRedoEvent() && (*(_getRedoEvent()))[_columns.event] == log );
102         // if we're on a parent event...
103         if ( !_curr_event->children().empty() ) {
105             // ...move to its first child
106             _curr_event_parent = _curr_event;
107             _curr_event = _curr_event->children().begin();
109         } else {
110         
111             ++_curr_event;
113             // if we are about to leave a branch...
114             if ( _curr_event->parent() &&
115                  _curr_event == _curr_event->parent()->children().end() )
116             {
118                 // ...collapse it
119                 if (_connected) {
120                     (*_callback_connections)[CALLB_SELECTION_CHANGE].block();
121                     (*_callback_connections)[CALLB_COLLAPSE].block();
122                     _event_list_view->collapse_row(_event_list_store->get_path(_curr_event->parent()));
123                     (*_callback_connections)[CALLB_COLLAPSE].block(false);
124                     (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false);
125                 }
127                 // ...and move to the next event at parent level
128                 _curr_event = _curr_event->parent();
129                 _curr_event_parent = (iterator)NULL;
131                 ++_curr_event;
132             }
133         }
135         // update the view
136         if (_connected) {
137             Gtk::TreePath curr_path = _event_list_store->get_path(_curr_event);
139             (*_callback_connections)[CALLB_SELECTION_CHANGE].block();
140             (*_callback_connections)[CALLB_EXPAND].block();
142             _event_list_view->expand_to_path(curr_path);
143             _event_list_selection->select(curr_path);
144             _event_list_view->scroll_to_row(curr_path);
146             (*_callback_connections)[CALLB_EXPAND].block(false);
147             (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false);
148         }
150         updateUndoVerbs();
151     }
155 void 
156 EventLog::notifyUndoCommitEvent(Event* log)
158     // If we're not at the last event in list then erase the previously undone events 
159     if ( _last_event != _curr_event ) {
161         _last_event = _curr_event;
163         if ( !_last_event->children().empty() ) {
164             _last_event = _last_event->children().begin();
165         } else {
166             ++_last_event;
167         }
169         while ( _last_event != _event_list_store->children().end() ) {
171             if (_last_event->parent()) {
172                 while ( _last_event != _last_event->parent()->children().end() ) {
173                     _last_event = _event_list_store->erase(_last_event);
174                 }
175                 _last_event = _last_event->parent();
177                 (*_last_event)[_columns.child_count] = _last_event->children().size() + 1;
179                 ++_last_event;
180             } else {
181                 _last_event = _event_list_store->erase(_last_event);
182             }
184         }
185     }
187     const unsigned int event_type = log->type;
189     Gtk::TreeRow curr_row;
191     // if the new event is of the same type as the previous then create a new branch
192     if ( event_type == (*_curr_event)[_columns.type] ) {
193         if ( !_curr_event_parent ) {
194             _curr_event_parent = _curr_event;
195         }
196         curr_row = *(_event_list_store->append(_curr_event_parent->children()));
197         (*_curr_event_parent)[_columns.child_count] = _curr_event_parent->children().size() + 1;
198     } else {
199         curr_row = *(_event_list_store->append());
200         curr_row[_columns.child_count] = 1;
202         _curr_event = _last_event = curr_row;
204         // collapse if we're leaving a branch
205         if (_curr_event_parent && _connected) {
206             (*_callback_connections)[CALLB_COLLAPSE].block();
207             _event_list_view->collapse_row(_event_list_store->get_path(_curr_event_parent));
208             (*_callback_connections)[CALLB_COLLAPSE].block(false);
209         }
211         _curr_event_parent = (iterator)(NULL);
212     }      
214     _curr_event = _last_event = curr_row;
216     curr_row[_columns.event] = log;
217     curr_row[_columns.type] = event_type;
218     curr_row[_columns.description] = log->description;
220     // update the view
221     if (_connected) {
222         Gtk::TreePath curr_path = _event_list_store->get_path(_curr_event);
224         (*_callback_connections)[CALLB_SELECTION_CHANGE].block();
225         (*_callback_connections)[CALLB_EXPAND].block();
227         _event_list_view->expand_to_path(curr_path);
228         _event_list_selection->select(curr_path);
229         _event_list_view->scroll_to_row(curr_path);
231         (*_callback_connections)[CALLB_EXPAND].block(false);
232         (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false);
233     }
235     updateUndoVerbs();
238 void 
239 EventLog::connectWithDialog(Gtk::TreeView *event_list_view, CallbackMap *callback_connections)
241     _event_list_view = event_list_view;
242     _event_list_selection = event_list_view->get_selection();
243     _event_list_selection->set_mode(Gtk::SELECTION_SINGLE);
245     _callback_connections = callback_connections;
247     (*_callback_connections)[CALLB_SELECTION_CHANGE].block();
248     (*_callback_connections)[CALLB_EXPAND].block();
250     _event_list_view->expand_to_path(_event_list_store->get_path(_curr_event));
251     _event_list_selection->select(_curr_event);
253     (*_callback_connections)[CALLB_EXPAND].block(false);
254     (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false);
256     _connected = true;
259 void
260 EventLog::updateUndoVerbs()
262     if(_document) {
264         if(_getUndoEvent()) { 
265             Inkscape::Verb::get(SP_VERB_EDIT_UNDO)->sensitive(_document, true);
267             Inkscape::Verb::get(SP_VERB_EDIT_UNDO)->name(_document, String::ucompose("%1: %2", 
268                       Glib::ustring(_("_Undo")),
269                       Glib::ustring((*_getUndoEvent())[_columns.description])));
270         } else {
271             Inkscape::Verb::get(SP_VERB_EDIT_UNDO)->name(_document, _("_Undo"));
272             Inkscape::Verb::get(SP_VERB_EDIT_UNDO)->sensitive(_document, false);
273         }
275         if(_getRedoEvent()) {
276             Inkscape::Verb::get(SP_VERB_EDIT_REDO)->sensitive(_document, true);
277             Inkscape::Verb::get(SP_VERB_EDIT_REDO)->name(_document, String::ucompose("%1: %2", 
278                       Glib::ustring(_("_Redo")),
279                       Glib::ustring((*_getRedoEvent())[_columns.description])));
281         } else {
282             Inkscape::Verb::get(SP_VERB_EDIT_REDO)->name(_document, _("_Redo"));
283             Inkscape::Verb::get(SP_VERB_EDIT_REDO)->sensitive(_document, false);
284         }
286     }
291 EventLog::const_iterator
292 EventLog::_getUndoEvent() const
294     const_iterator undo_event = (const_iterator)NULL;
295     if( _curr_event != _event_list_store->children().begin() )
296         undo_event = _curr_event;
297     return undo_event;
300 EventLog::const_iterator
301 EventLog::_getRedoEvent() const
303     const_iterator redo_event = (const_iterator)NULL;
305     if ( _curr_event != _last_event ) {
307         if ( !_curr_event->children().empty() )
308             redo_event = _curr_event->children().begin();
309         else  {
310             redo_event = _curr_event;
311             ++redo_event;
313             if ( redo_event->parent() &&
314                  redo_event == redo_event->parent()->children().end() ) {
316                 redo_event = redo_event->parent();
317                 ++redo_event;
319             }
320         }
322     }
324     return redo_event;
329 /*
330   Local Variables:
331   mode:c++
332   c-file-style:"stroustrup"
333   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
334   indent-tabs-mode:nil
335   fill-column:99
336   End:
337 */
338 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :