Code

b808e57dee7076d7bcdba25d20f26b418eafef88
[inkscape.git] / src / ui / dialog / undo-history.cpp
1 /**
2  * \brief Undo History dialog
3  *
4  * Author:
5  *   Gustav Broberg <broberg@kth.se>
6  *
7  * Copyright (C) 2006 Authors
8  *
9  * Released under GNU GPL.  Read the file 'COPYING' for more information.
10  */
12 #ifdef HAVE_CONFIG_H
13 # include <config.h>
14 #endif
16 #include <glibmm/i18n.h>
17 #include <gtk/gtkimage.h>
18 #include <sigc++/sigc++.h>
21 #include "document.h"
22 #include "inkscape.h"
23 #include "ui/icons.h"
24 #include "verbs.h"
26 #include "undo-history.h"
28 namespace Inkscape {
29 namespace UI {
30 namespace Dialog {
32 /* Rendering functions for custom cell renderers */
34 void
35 CellRendererSPIcon::render_vfunc(const Glib::RefPtr<Gdk::Drawable>& window,
36                                  Gtk::Widget& widget,
37                                  const Gdk::Rectangle& background_area,
38                                  const Gdk::Rectangle& cell_area,
39                                  const Gdk::Rectangle& expose_area,
40                                  Gtk::CellRendererState flags)
41 {
42     // if this event type doesn't have an icon...
43     if ( !Inkscape::Verb::get(_property_event_type)->get_image() ) return;
45     // if the icon isn't cached, render it to a pixbuf
46     if ( !_icon_cache[_property_event_type] ) {
48         Glib::ustring image = Inkscape::Verb::get(_property_event_type)->get_image();
49         Gtk::Widget* icon = sp_icon_get_icon(image, Inkscape::ICON_SIZE_MENU);
51         if (icon) {
53             // check icon type (inkscape, gtk, none)
54             if ( SP_IS_ICON(icon->gobj()) ) {
55                 SPIcon* sp_icon = SP_ICON(icon->gobj());
56                 sp_icon_fetch_pixbuf(sp_icon);
57                 _property_icon = Glib::wrap(sp_icon->pb, true);
58             } else if ( GTK_IS_IMAGE(icon->gobj()) ) {
59                 _property_icon = Gtk::Invisible().render_icon(Gtk::StockID(image),
60                                                               Gtk::ICON_SIZE_MENU);
61             } else {
62                 delete icon;
63                 return;
64             }
66             delete icon;
67             property_pixbuf() = _icon_cache[_property_event_type] = _property_icon.get_value();
68         }
70     } else {
71         property_pixbuf() = _icon_cache[_property_event_type];
72     }
74     Gtk::CellRendererPixbuf::render_vfunc(window, widget, background_area,
75                                           cell_area, expose_area, flags);
76 }
79 void
80 CellRendererInt::render_vfunc(const Glib::RefPtr<Gdk::Drawable>& window,
81                               Gtk::Widget& widget,
82                               const Gdk::Rectangle& background_area,
83                               const Gdk::Rectangle& cell_area,
84                               const Gdk::Rectangle& expose_area,
85                               Gtk::CellRendererState flags)
86 {
87     if( _filter(_property_number) ) {
88         std::ostringstream s;
89         s << _property_number << std::flush;
90         property_text() = s.str();
91         Gtk::CellRendererText::render_vfunc(window, widget, background_area,
92                                             cell_area, expose_area, flags);
93     }
94 }
96 const CellRendererInt::Filter& CellRendererInt::no_filter = CellRendererInt::NoFilter();
98 static UndoHistory *_instance = 0;
100 /* local desktop event handlers */
101 static void on_document_replaced(SPDesktop* desktop, SPDocument*);
102 static void on_activate_desktop(Inkscape::Application*, SPDesktop* desktop, void*);
103 static void on_deactivate_desktop(Inkscape::Application*, SPDesktop* desktop, void*);
105 UndoHistory& UndoHistory::getInstance()
107     if (!_instance)
108         _instance = new UndoHistory();
110     return *_instance;
113 void
114 UndoHistory::setDesktop(SPDesktop* desktop)
116     if (!desktop || !SP_ACTIVE_DOCUMENT) return;
118     _document = SP_ACTIVE_DOCUMENT;
120     _event_log = desktop->event_log;
122     _callback_connections[EventLog::CALLB_SELECTION_CHANGE].block();
124     _event_list_store = _event_log->getEventListStore();
125     _event_list_view.set_model(_event_list_store);
126     _event_list_selection = _event_list_view.get_selection();
128     _event_log->connectWithDialog(&_event_list_view, &_callback_connections);
129     _event_list_view.scroll_to_row(_event_list_store->get_path(_event_list_selection->get_selected()));
131     _callback_connections[EventLog::CALLB_SELECTION_CHANGE].block(false);
134 UndoHistory::UndoHistory()
135     : UI::Widget::Panel ("", "dialogs.undo-history", SP_VERB_DIALOG_UNDO_HISTORY),
136       _desktop (SP_ACTIVE_DESKTOP),
137       _document (SP_ACTIVE_DOCUMENT),
138       _event_log (_desktop ? _desktop->event_log : NULL),
139       _columns (_event_log ? &_event_log->getColumns() : NULL),
140       _event_list_selection (_event_list_view.get_selection())
142     if ( !_document || !_event_log || !_columns ) return;
144     set_size_request(300, 200);
146     _getContents()->pack_start(_scrolled_window);
147     _scrolled_window.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
149     _event_list_store = _event_log->getEventListStore();
151     _event_list_view.set_model(_event_list_store);
152     _event_list_view.set_rules_hint(false);
153     _event_list_view.set_enable_search(false);
154     _event_list_view.set_headers_visible(false);
156     CellRendererSPIcon* icon_renderer = Gtk::manage(new CellRendererSPIcon());
157     icon_renderer->property_xpad() = 8;
158     icon_renderer->property_width() = 36;
159     int cols_count = _event_list_view.append_column("Icon", *icon_renderer);
161     Gtk::TreeView::Column* icon_column = _event_list_view.get_column(cols_count-1);
162     icon_column->add_attribute(icon_renderer->property_event_type(), _columns->type);
164     Gtk::CellRendererText* description_renderer = Gtk::manage(new Gtk::CellRendererText());
166     cols_count = _event_list_view.append_column("Description", *description_renderer);
167     Gtk::TreeView::Column* description_column = _event_list_view.get_column(cols_count-1);
168     description_column->add_attribute(description_renderer->property_text(), _columns->description);
169     description_column->set_resizable();
171     _event_list_view.set_expander_column( *_event_list_view.get_column(cols_count-1) );
173     CellRendererInt* children_renderer = Gtk::manage(new CellRendererInt(greater_than_1));
174     children_renderer->property_weight() = 600; // =Pango::WEIGHT_SEMIBOLD (not defined in old versions of pangomm)
175     children_renderer->property_xalign() = 1.0;
176     children_renderer->property_xpad() = 20;
178     cols_count = _event_list_view.append_column("Children", *children_renderer);
179     Gtk::TreeView::Column* children_column = _event_list_view.get_column(cols_count-1);
180     children_column->add_attribute(children_renderer->property_number(), _columns->child_count);
182     _scrolled_window.add(_event_list_view);
184     // connect desktop event callbacks
185     _document_replaced_connection = _desktop->connectDocumentReplaced(sigc::ptr_fun(on_document_replaced));
186     g_signal_connect(G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(on_activate_desktop), 0);
187     g_signal_connect(G_OBJECT(INKSCAPE), "deactivate_desktop", G_CALLBACK(on_deactivate_desktop), 0);
189     // connect EventLog callbacks
190     _callback_connections[EventLog::CALLB_SELECTION_CHANGE] =
191         _event_list_selection->signal_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onListSelectionChange));
193     _callback_connections[EventLog::CALLB_EXPAND] =
194         _event_list_view.signal_row_expanded().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onExpandEvent));
196     _callback_connections[EventLog::CALLB_COLLAPSE] =
197         _event_list_view.signal_row_collapsed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onCollapseEvent));
199     // connect with the EventLog
200     _event_log->connectWithDialog(&_event_list_view, &_callback_connections);
202     show_all_children();
204     // scroll to the selected row
205     _event_list_view.set_cursor(_event_list_store->get_path(_event_log->getCurrEvent()));
208 UndoHistory::~UndoHistory()
212 void
213 UndoHistory::_onListSelectionChange()
216     EventLog::const_iterator selected = _event_list_selection->get_selected();
218     /* If no event is selected in the view, find the right one and select it. This happens whenever
219      * a branch we're currently in is collapsed.
220      */
221     if (!selected) {
223         EventLog::iterator curr_event = _event_log->getCurrEvent();
225         if (curr_event->parent()) {
227             EventLog::iterator curr_event_parent = curr_event->parent();
228             EventLog::iterator last = curr_event_parent->children().end();
230             _event_log->blockNotifications();
231             for ( --last ; curr_event != last ; ++curr_event ) {
232                 sp_document_redo(_document);
233             }
234             _event_log->blockNotifications(false);
236             _event_log->setCurrEvent(curr_event);
237             _event_list_selection->select(curr_event_parent);
239         } else {  // this should not happen
240             _event_list_selection->select(curr_event);
241         }
243     } else {
245         EventLog::const_iterator last_selected = _event_log->getCurrEvent();
247         /* Selecting a collapsed parent event is equal to selecting the last child
248          * of that parent's branch.
249          */
251         if ( !selected->children().empty() &&
252              !_event_list_view.row_expanded(_event_list_store->get_path(selected)) )
253         {
254             selected = selected->children().end();
255             --selected;
256         }
258         // An event before the current one has been selected. Undo to the selected event.
259         if ( _event_list_store->get_path(selected) <
260              _event_list_store->get_path(last_selected) )
261         {
262             _event_log->blockNotifications();
264             while ( selected != last_selected ) {
266                 sp_document_undo(_document);
268                 if ( last_selected->parent() &&
269                      last_selected == last_selected->parent()->children().begin() )
270                 {
271                     last_selected = last_selected->parent();
272                     _event_log->setCurrEventParent((EventLog::iterator)NULL);
273                 } else {
274                     --last_selected;
275                     if ( !last_selected->children().empty() ) {
276                         _event_log->setCurrEventParent(last_selected);
277                         last_selected = last_selected->children().end();
278                         --last_selected;
279                     }
280                 }
281             }
282             _event_log->blockNotifications(false);
283             _event_log->updateUndoVerbs();
285         } else { // An event after the current one has been selected. Redo to the selected event.
287             _event_log->blockNotifications();
289             while ( selected != last_selected ) {
291                 sp_document_redo(_document);
293                 if ( !last_selected->children().empty() ) {
294                     _event_log->setCurrEventParent(last_selected);
295                     last_selected = last_selected->children().begin();
296                 } else {
297                     ++last_selected;
298                     if ( last_selected->parent() &&
299                          last_selected == last_selected->parent()->children().end() )
300                     {
301                         last_selected = last_selected->parent();
302                         ++last_selected;
303                         _event_log->setCurrEventParent((EventLog::iterator)NULL);
304                     }
305                 }
306             }
307             _event_log->blockNotifications(false);
309         }
311         _event_log->setCurrEvent(selected);
312         _event_log->updateUndoVerbs();
313     }
317 void
318 UndoHistory::_onExpandEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &/*path*/)
320     if ( iter == _event_list_selection->get_selected() )
321     {
322         _event_list_selection->select(_event_log->getCurrEvent());
323     }
326 void
327 UndoHistory::_onCollapseEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &/*path*/)
329     // Collapsing a branch we're currently in is equal to stepping to the last event in that branch
330     if ( iter == _event_log->getCurrEvent() )
331     {
332         EventLog::const_iterator curr_event_parent = _event_log->getCurrEvent();
333         EventLog::const_iterator curr_event = curr_event_parent->children().begin();
334         EventLog::const_iterator last = curr_event_parent->children().end();
336         _event_log->blockNotifications();
337         sp_document_redo(_document);
339         for ( --last ; curr_event != last ; ++curr_event ) {
340             sp_document_redo(_document);
341         }
342         _event_log->blockNotifications(false);
344         _event_log->setCurrEvent(curr_event);
345         _event_log->setCurrEventParent(curr_event_parent);
346         _event_list_selection->select(curr_event_parent);
347     }
350 const CellRendererInt::Filter& UndoHistory::greater_than_1 = UndoHistory::GreaterThan(1);
352 static void
353 on_activate_desktop(Inkscape::Application*, SPDesktop* desktop, void*)
355     if (!_instance) return;
357     _instance->_document_replaced_connection =
358         SP_ACTIVE_DESKTOP->connectDocumentReplaced(sigc::ptr_fun(on_document_replaced));
360     _instance->setDesktop(desktop);
363 static void
364 on_deactivate_desktop(Inkscape::Application*, SPDesktop* /*desktop*/, void*)
366     if (!_instance) return;
368     _instance->_document_replaced_connection.disconnect();
371 static void
372 on_document_replaced(SPDesktop* desktop, SPDocument*)
374     if (!_instance) return;
376     _instance->setDesktop(desktop);
379 } // namespace Dialog
380 } // namespace UI
381 } // namespace Inkscape
383 /*
384   Local Variables:
385   mode:c++
386   c-file-style:"stroustrup"
387   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
388   indent-tabs-mode:nil
389   fill-column:99
390   End:
391 */
392 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :