Code

c53583741f5b13613ac515316c94613cc87ecaec
[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"
25 #include "desktop-handles.h"
27 #include "undo-history.h"
29 namespace Inkscape {
30 namespace UI {
31 namespace Dialog {
33 /* Rendering functions for custom cell renderers */
35 void
36 CellRendererSPIcon::render_vfunc(const Glib::RefPtr<Gdk::Drawable>& window,
37                                  Gtk::Widget& widget,
38                                  const Gdk::Rectangle& background_area,
39                                  const Gdk::Rectangle& cell_area,
40                                  const Gdk::Rectangle& expose_area,
41                                  Gtk::CellRendererState flags)
42 {
43     // if this event type doesn't have an icon...
44     if ( !Inkscape::Verb::get(_property_event_type)->get_image() ) return;
46     // if the icon isn't cached, render it to a pixbuf
47     if ( !_icon_cache[_property_event_type] ) {
49         Glib::ustring image = Inkscape::Verb::get(_property_event_type)->get_image();
50         Gtk::Widget* icon = sp_icon_get_icon(image, Inkscape::ICON_SIZE_MENU);
52         if (icon) {
54             // check icon type (inkscape, gtk, none)
55             if ( SP_IS_ICON(icon->gobj()) ) {
56                 SPIcon* sp_icon = SP_ICON(icon->gobj());
57                 sp_icon_fetch_pixbuf(sp_icon);
58                 _property_icon = Glib::wrap(sp_icon->pb, true);
59             } else if ( GTK_IS_IMAGE(icon->gobj()) ) {
60                 _property_icon = Gtk::Invisible().render_icon(Gtk::StockID(image),
61                                                               Gtk::ICON_SIZE_MENU);
62             } else {
63                 delete icon;
64                 return;
65             }
67             delete icon;
68             property_pixbuf() = _icon_cache[_property_event_type] = _property_icon.get_value();
69         }
71     } else {
72         property_pixbuf() = _icon_cache[_property_event_type];
73     }
75     Gtk::CellRendererPixbuf::render_vfunc(window, widget, background_area,
76                                           cell_area, expose_area, flags);
77 }
80 void
81 CellRendererInt::render_vfunc(const Glib::RefPtr<Gdk::Drawable>& window,
82                               Gtk::Widget& widget,
83                               const Gdk::Rectangle& background_area,
84                               const Gdk::Rectangle& cell_area,
85                               const Gdk::Rectangle& expose_area,
86                               Gtk::CellRendererState flags)
87 {
88     if( _filter(_property_number) ) {
89         std::ostringstream s;
90         s << _property_number << std::flush;
91         property_text() = s.str();
92         Gtk::CellRendererText::render_vfunc(window, widget, background_area,
93                                             cell_area, expose_area, flags);
94     }
95 }
97 const CellRendererInt::Filter& CellRendererInt::no_filter = CellRendererInt::NoFilter();
99 UndoHistory& UndoHistory::getInstance()
101     return *new UndoHistory();
104 void
105 UndoHistory::setDesktop(SPDesktop* desktop)
107     Panel::setDesktop(desktop);
109     if (!desktop) return;
111     _document = sp_desktop_document(desktop);
113     _event_log = desktop->event_log;
115     _callback_connections[EventLog::CALLB_SELECTION_CHANGE].block();
117     _event_list_store = _event_log->getEventListStore();
118     _event_list_view.set_model(_event_list_store);
119     _event_list_selection = _event_list_view.get_selection();
121     _event_log->connectWithDialog(&_event_list_view, &_callback_connections);
122     _event_list_view.scroll_to_row(_event_list_store->get_path(_event_list_selection->get_selected()));
124     _callback_connections[EventLog::CALLB_SELECTION_CHANGE].block(false);
127 UndoHistory::UndoHistory()
128     : UI::Widget::Panel ("", "dialogs.undo-history", SP_VERB_DIALOG_UNDO_HISTORY),
129       _document (sp_desktop_document(getDesktop())),
130       _event_log (getDesktop() ? getDesktop()->event_log : NULL),
131       _columns (_event_log ? &_event_log->getColumns() : NULL),
132       _event_list_selection (_event_list_view.get_selection())
134     if ( !_document || !_event_log || !_columns ) return;
136     set_size_request(300, 200);
138     _getContents()->pack_start(_scrolled_window);
139     _scrolled_window.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
141     _event_list_store = _event_log->getEventListStore();
143     _event_list_view.set_model(_event_list_store);
144     _event_list_view.set_rules_hint(false);
145     _event_list_view.set_enable_search(false);
146     _event_list_view.set_headers_visible(false);
148     CellRendererSPIcon* icon_renderer = Gtk::manage(new CellRendererSPIcon());
149     icon_renderer->property_xpad() = 8;
150     icon_renderer->property_width() = 36;
151     int cols_count = _event_list_view.append_column("Icon", *icon_renderer);
153     Gtk::TreeView::Column* icon_column = _event_list_view.get_column(cols_count-1);
154     icon_column->add_attribute(icon_renderer->property_event_type(), _columns->type);
156     Gtk::CellRendererText* description_renderer = Gtk::manage(new Gtk::CellRendererText());
158     cols_count = _event_list_view.append_column("Description", *description_renderer);
159     Gtk::TreeView::Column* description_column = _event_list_view.get_column(cols_count-1);
160     description_column->add_attribute(description_renderer->property_text(), _columns->description);
161     description_column->set_resizable();
163     _event_list_view.set_expander_column( *_event_list_view.get_column(cols_count-1) );
165     CellRendererInt* children_renderer = Gtk::manage(new CellRendererInt(greater_than_1));
166     children_renderer->property_weight() = 600; // =Pango::WEIGHT_SEMIBOLD (not defined in old versions of pangomm)
167     children_renderer->property_xalign() = 1.0;
168     children_renderer->property_xpad() = 20;
170     cols_count = _event_list_view.append_column("Children", *children_renderer);
171     Gtk::TreeView::Column* children_column = _event_list_view.get_column(cols_count-1);
172     children_column->add_attribute(children_renderer->property_number(), _columns->child_count);
174     _scrolled_window.add(_event_list_view);
176     // connect EventLog callbacks
177     _callback_connections[EventLog::CALLB_SELECTION_CHANGE] =
178         _event_list_selection->signal_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onListSelectionChange));
180     _callback_connections[EventLog::CALLB_EXPAND] =
181         _event_list_view.signal_row_expanded().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onExpandEvent));
183     _callback_connections[EventLog::CALLB_COLLAPSE] =
184         _event_list_view.signal_row_collapsed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onCollapseEvent));
186     // connect with the EventLog
187     _event_log->connectWithDialog(&_event_list_view, &_callback_connections);
189     show_all_children();
191     // scroll to the selected row
192     _event_list_view.set_cursor(_event_list_store->get_path(_event_log->getCurrEvent()));
195 UndoHistory::~UndoHistory()
199 void
200 UndoHistory::_onListSelectionChange()
203     EventLog::const_iterator selected = _event_list_selection->get_selected();
205     /* If no event is selected in the view, find the right one and select it. This happens whenever
206      * a branch we're currently in is collapsed.
207      */
208     if (!selected) {
210         EventLog::iterator curr_event = _event_log->getCurrEvent();
212         if (curr_event->parent()) {
214             EventLog::iterator curr_event_parent = curr_event->parent();
215             EventLog::iterator last = curr_event_parent->children().end();
217             _event_log->blockNotifications();
218             for ( --last ; curr_event != last ; ++curr_event ) {
219                 sp_document_redo(_document);
220             }
221             _event_log->blockNotifications(false);
223             _event_log->setCurrEvent(curr_event);
224             _event_list_selection->select(curr_event_parent);
226         } else {  // this should not happen
227             _event_list_selection->select(curr_event);
228         }
230     } else {
232         EventLog::const_iterator last_selected = _event_log->getCurrEvent();
234         /* Selecting a collapsed parent event is equal to selecting the last child
235          * of that parent's branch.
236          */
238         if ( !selected->children().empty() &&
239              !_event_list_view.row_expanded(_event_list_store->get_path(selected)) )
240         {
241             selected = selected->children().end();
242             --selected;
243         }
245         // An event before the current one has been selected. Undo to the selected event.
246         if ( _event_list_store->get_path(selected) <
247              _event_list_store->get_path(last_selected) )
248         {
249             _event_log->blockNotifications();
251             while ( selected != last_selected ) {
253                 sp_document_undo(_document);
255                 if ( last_selected->parent() &&
256                      last_selected == last_selected->parent()->children().begin() )
257                 {
258                     last_selected = last_selected->parent();
259                     _event_log->setCurrEventParent((EventLog::iterator)NULL);
260                 } else {
261                     --last_selected;
262                     if ( !last_selected->children().empty() ) {
263                         _event_log->setCurrEventParent(last_selected);
264                         last_selected = last_selected->children().end();
265                         --last_selected;
266                     }
267                 }
268             }
269             _event_log->blockNotifications(false);
270             _event_log->updateUndoVerbs();
272         } else { // An event after the current one has been selected. Redo to the selected event.
274             _event_log->blockNotifications();
276             while ( selected != last_selected ) {
278                 sp_document_redo(_document);
280                 if ( !last_selected->children().empty() ) {
281                     _event_log->setCurrEventParent(last_selected);
282                     last_selected = last_selected->children().begin();
283                 } else {
284                     ++last_selected;
285                     if ( last_selected->parent() &&
286                          last_selected == last_selected->parent()->children().end() )
287                     {
288                         last_selected = last_selected->parent();
289                         ++last_selected;
290                         _event_log->setCurrEventParent((EventLog::iterator)NULL);
291                     }
292                 }
293             }
294             _event_log->blockNotifications(false);
296         }
298         _event_log->setCurrEvent(selected);
299         _event_log->updateUndoVerbs();
300     }
304 void
305 UndoHistory::_onExpandEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &/*path*/)
307     if ( iter == _event_list_selection->get_selected() ) {
308         _event_list_selection->select(_event_log->getCurrEvent());
309     }
312 void
313 UndoHistory::_onCollapseEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &/*path*/)
315     // Collapsing a branch we're currently in is equal to stepping to the last event in that branch
316     if ( iter == _event_log->getCurrEvent() ) {
317         EventLog::const_iterator curr_event_parent = _event_log->getCurrEvent();
318         EventLog::const_iterator curr_event = curr_event_parent->children().begin();
319         EventLog::const_iterator last = curr_event_parent->children().end();
321         _event_log->blockNotifications();
322         sp_document_redo(_document);
324         for ( --last ; curr_event != last ; ++curr_event ) {
325             sp_document_redo(_document);
326         }
327         _event_log->blockNotifications(false);
329         _event_log->setCurrEvent(curr_event);
330         _event_log->setCurrEventParent(curr_event_parent);
331         _event_list_selection->select(curr_event_parent);
332     }
335 const CellRendererInt::Filter& UndoHistory::greater_than_1 = UndoHistory::GreaterThan(1);
337 } // namespace Dialog
338 } // namespace UI
339 } // namespace Inkscape
341 /*
342   Local Variables:
343   mode:c++
344   c-file-style:"stroustrup"
345   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
346   indent-tabs-mode:nil
347   fill-column:99
348   End:
349 */
350 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :