1 /*
2 * Author:
3 * Gustav Broberg <broberg@kth.se>
4 *
5 * Copyright (c) 2006, 2007 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"
16 #include "document.h"
17 #include "xml/repr.h"
18 #include "sp-object.h"
20 namespace Inkscape {
22 EventLog::EventLog(SPDocument* document) :
23 UndoStackObserver(),
24 _connected (false),
25 _document (document),
26 _event_list_store (Gtk::TreeStore::create(_columns)),
27 _event_list_selection (NULL),
28 _event_list_view (NULL),
29 _curr_event_parent (NULL),
30 _notifications_blocked (false),
31 _callback_connections (NULL)
32 {
33 // add initial pseudo event
34 Gtk::TreeRow curr_row = *(_event_list_store->append());
35 _curr_event = _last_saved = _last_event = curr_row;
37 curr_row[_columns.description] = _("[Unchanged]");
38 curr_row[_columns.type] = SP_VERB_FILE_NEW;
39 }
41 EventLog::~EventLog() { }
43 void
44 EventLog::notifyUndoEvent(Event* log)
45 {
46 if ( !_notifications_blocked ) {
48 // make sure the supplied event matches the next undoable event
49 g_return_if_fail ( _getUndoEvent() && (*(_getUndoEvent()))[_columns.event] == log );
51 // if we're on the first child event...
52 if ( _curr_event->parent() &&
53 _curr_event == _curr_event->parent()->children().begin() )
54 {
55 // ...back up to the parent
56 _curr_event = _curr_event->parent();
57 _curr_event_parent = (iterator)NULL;
59 } else {
61 // if we're about to leave a branch, collapse it
62 if ( !_curr_event->children().empty() && _connected ) {
63 (*_callback_connections)[CALLB_COLLAPSE].block();
64 _event_list_view->collapse_row(_event_list_store->get_path(_curr_event));
65 (*_callback_connections)[CALLB_COLLAPSE].block(false);
66 }
68 --_curr_event;
70 // if we're entering a branch, move to the end of it
71 if (!_curr_event->children().empty()) {
72 _curr_event_parent = _curr_event;
73 _curr_event = _curr_event->children().end();
74 --_curr_event;
75 }
76 }
78 checkForVirginity();
80 // update the view
81 if (_connected) {
82 (*_callback_connections)[CALLB_SELECTION_CHANGE].block();
83 (*_callback_connections)[CALLB_EXPAND].block();
85 Gtk::TreePath curr_path = _event_list_store->get_path(_curr_event);
86 _event_list_view->expand_to_path(curr_path);
87 _event_list_selection->select(curr_path);
88 _event_list_view->scroll_to_row(curr_path);
90 (*_callback_connections)[CALLB_EXPAND].block(false);
91 (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false);
92 }
94 updateUndoVerbs();
95 }
97 }
99 void
100 EventLog::notifyRedoEvent(Event* log)
101 {
102 if ( !_notifications_blocked ) {
104 // make sure the supplied event matches the next redoable event
105 g_return_if_fail ( _getRedoEvent() && (*(_getRedoEvent()))[_columns.event] == log );
107 // if we're on a parent event...
108 if ( !_curr_event->children().empty() ) {
110 // ...move to its first child
111 _curr_event_parent = _curr_event;
112 _curr_event = _curr_event->children().begin();
114 } else {
116 ++_curr_event;
118 // if we are about to leave a branch...
119 if ( _curr_event->parent() &&
120 _curr_event == _curr_event->parent()->children().end() )
121 {
123 // ...collapse it
124 if (_connected) {
125 (*_callback_connections)[CALLB_SELECTION_CHANGE].block();
126 (*_callback_connections)[CALLB_COLLAPSE].block();
127 _event_list_view->collapse_row(_event_list_store->get_path(_curr_event->parent()));
128 (*_callback_connections)[CALLB_COLLAPSE].block(false);
129 (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false);
130 }
132 // ...and move to the next event at parent level
133 _curr_event = _curr_event->parent();
134 _curr_event_parent = (iterator)NULL;
136 ++_curr_event;
137 }
138 }
140 checkForVirginity();
142 // update the view
143 if (_connected) {
144 Gtk::TreePath curr_path = _event_list_store->get_path(_curr_event);
146 (*_callback_connections)[CALLB_SELECTION_CHANGE].block();
147 (*_callback_connections)[CALLB_EXPAND].block();
149 _event_list_view->expand_to_path(curr_path);
150 _event_list_selection->select(curr_path);
151 _event_list_view->scroll_to_row(curr_path);
153 (*_callback_connections)[CALLB_EXPAND].block(false);
154 (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false);
155 }
157 updateUndoVerbs();
158 }
160 }
162 void
163 EventLog::notifyUndoCommitEvent(Event* log)
164 {
165 _clearRedo();
167 const unsigned int event_type = log->type;
169 Gtk::TreeRow curr_row;
171 // if the new event is of the same type as the previous then create a new branch
172 if ( event_type == (*_curr_event)[_columns.type] ) {
173 if ( !_curr_event_parent ) {
174 _curr_event_parent = _curr_event;
175 }
176 curr_row = *(_event_list_store->append(_curr_event_parent->children()));
177 (*_curr_event_parent)[_columns.child_count] = _curr_event_parent->children().size() + 1;
178 } else {
179 curr_row = *(_event_list_store->append());
180 curr_row[_columns.child_count] = 1;
182 _curr_event = _last_event = curr_row;
184 // collapse if we're leaving a branch
185 if (_curr_event_parent && _connected) {
186 (*_callback_connections)[CALLB_COLLAPSE].block();
187 _event_list_view->collapse_row(_event_list_store->get_path(_curr_event_parent));
188 (*_callback_connections)[CALLB_COLLAPSE].block(false);
189 }
191 _curr_event_parent = (iterator)(NULL);
192 }
194 _curr_event = _last_event = curr_row;
196 curr_row[_columns.event] = log;
197 curr_row[_columns.type] = event_type;
198 curr_row[_columns.description] = log->description;
200 checkForVirginity();
202 // update the view
203 if (_connected) {
204 Gtk::TreePath curr_path = _event_list_store->get_path(_curr_event);
206 (*_callback_connections)[CALLB_SELECTION_CHANGE].block();
207 (*_callback_connections)[CALLB_EXPAND].block();
209 _event_list_view->expand_to_path(curr_path);
210 _event_list_selection->select(curr_path);
211 _event_list_view->scroll_to_row(curr_path);
213 (*_callback_connections)[CALLB_EXPAND].block(false);
214 (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false);
215 }
217 updateUndoVerbs();
218 }
220 void
221 EventLog::notifyClearUndoEvent()
222 {
223 _clearUndo();
224 updateUndoVerbs();
225 }
227 void
228 EventLog::notifyClearRedoEvent()
229 {
230 _clearRedo();
231 updateUndoVerbs();
232 }
234 void
235 EventLog::connectWithDialog(Gtk::TreeView *event_list_view, CallbackMap *callback_connections)
236 {
237 _event_list_view = event_list_view;
238 _event_list_selection = event_list_view->get_selection();
239 _event_list_selection->set_mode(Gtk::SELECTION_SINGLE);
241 _callback_connections = callback_connections;
243 (*_callback_connections)[CALLB_SELECTION_CHANGE].block();
244 (*_callback_connections)[CALLB_EXPAND].block();
246 _event_list_view->expand_to_path(_event_list_store->get_path(_curr_event));
247 _event_list_selection->select(_curr_event);
249 (*_callback_connections)[CALLB_EXPAND].block(false);
250 (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false);
252 _connected = true;
253 }
255 void
256 EventLog::updateUndoVerbs()
257 {
258 if(_document) {
260 if(_getUndoEvent()) {
261 Inkscape::Verb::get(SP_VERB_EDIT_UNDO)->sensitive(_document, true);
263 Inkscape::Verb::get(SP_VERB_EDIT_UNDO)->name(_document, String::ucompose("%1: %2",
264 Glib::ustring(_("_Undo")),
265 Glib::ustring((*_getUndoEvent())[_columns.description])));
266 } else {
267 Inkscape::Verb::get(SP_VERB_EDIT_UNDO)->name(_document, _("_Undo"));
268 Inkscape::Verb::get(SP_VERB_EDIT_UNDO)->sensitive(_document, false);
269 }
271 if(_getRedoEvent()) {
272 Inkscape::Verb::get(SP_VERB_EDIT_REDO)->sensitive(_document, true);
273 Inkscape::Verb::get(SP_VERB_EDIT_REDO)->name(_document, String::ucompose("%1: %2",
274 Glib::ustring(_("_Redo")),
275 Glib::ustring((*_getRedoEvent())[_columns.description])));
277 } else {
278 Inkscape::Verb::get(SP_VERB_EDIT_REDO)->name(_document, _("_Redo"));
279 Inkscape::Verb::get(SP_VERB_EDIT_REDO)->sensitive(_document, false);
280 }
282 }
284 }
287 EventLog::const_iterator
288 EventLog::_getUndoEvent() const
289 {
290 const_iterator undo_event = (const_iterator)NULL;
291 if( _curr_event != _event_list_store->children().begin() )
292 undo_event = _curr_event;
293 return undo_event;
294 }
296 EventLog::const_iterator
297 EventLog::_getRedoEvent() const
298 {
299 const_iterator redo_event = (const_iterator)NULL;
301 if ( _curr_event != _last_event ) {
303 if ( !_curr_event->children().empty() )
304 redo_event = _curr_event->children().begin();
305 else {
306 redo_event = _curr_event;
307 ++redo_event;
309 if ( redo_event->parent() &&
310 redo_event == redo_event->parent()->children().end() ) {
312 redo_event = redo_event->parent();
313 ++redo_event;
315 }
316 }
318 }
320 return redo_event;
321 }
323 void
324 EventLog::_clearUndo()
325 {
326 // TODO: Implement when needed
327 }
329 void
330 EventLog::_clearRedo()
331 {
332 if ( _last_event != _curr_event ) {
334 _last_event = _curr_event;
336 if ( !_last_event->children().empty() ) {
337 _last_event = _last_event->children().begin();
338 } else {
339 ++_last_event;
340 }
342 while ( _last_event != _event_list_store->children().end() ) {
344 if (_last_event->parent()) {
345 while ( _last_event != _last_event->parent()->children().end() ) {
346 _last_event = _event_list_store->erase(_last_event);
347 }
348 _last_event = _last_event->parent();
350 (*_last_event)[_columns.child_count] = _last_event->children().size() + 1;
352 ++_last_event;
353 } else {
354 _last_event = _event_list_store->erase(_last_event);
355 }
357 }
359 }
360 }
362 /* mark document as untouched if we reach a state where the document was previously saved */
363 void
364 EventLog::checkForVirginity() {
365 g_return_if_fail (_document);
366 if (_curr_event == _last_saved) {
367 _document->setModifiedSinceSave(false);
368 }
369 }
371 } // namespace Inkscape
374 /*
375 Local Variables:
376 mode:c++
377 c-file-style:"stroustrup"
378 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
379 indent-tabs-mode:nil
380 fill-column:99
381 End:
382 */
383 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :