Code

A simple layout document as to what, why and how is cppification.
[inkscape.git] / src / ui / dialog / undo-history.cpp
1 /** @file
2  * @brief Undo History dialog - implementation
3  */
4 /* Author:
5  *   Gustav Broberg <broberg@kth.se>
6  *
7  * Copyright (C) 2006 Authors
8  * Released under GNU GPL.  Read the file 'COPYING' for more information.
9  */
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 #endif
15 #include <glibmm/i18n.h>
16 #include <gtk/gtkimage.h>
17 #include <sigc++/sigc++.h>
20 #include "document.h"
21 #include "inkscape.h"
22 #include "verbs.h"
23 #include "desktop-handles.h"
25 #include "undo-history.h"
27 namespace Inkscape {
28 namespace UI {
29 namespace Dialog {
31 /* Rendering functions for custom cell renderers */
33 void
34 CellRendererSPIcon::render_vfunc(const Glib::RefPtr<Gdk::Drawable>& window,
35                                  Gtk::Widget& widget,
36                                  const Gdk::Rectangle& background_area,
37                                  const Gdk::Rectangle& cell_area,
38                                  const Gdk::Rectangle& expose_area,
39                                  Gtk::CellRendererState flags)
40 {
41     // if this event type doesn't have an icon...
42     if ( !Inkscape::Verb::get(_property_event_type)->get_image() ) return;
44     // if the icon isn't cached, render it to a pixbuf
45     if ( !_icon_cache[_property_event_type] ) {
47         Glib::ustring image = Inkscape::Verb::get(_property_event_type)->get_image();
48         Gtk::Widget* icon = sp_icon_get_icon(image, Inkscape::ICON_SIZE_MENU);
50         if (icon) {
52             // check icon type (inkscape, gtk, none)
53             if ( SP_IS_ICON(icon->gobj()) ) {
54                 SPIcon* sp_icon = SP_ICON(icon->gobj());
55                 sp_icon_fetch_pixbuf(sp_icon);
56                 _property_icon = Glib::wrap(sp_icon->pb, true);
57             } else if ( GTK_IS_IMAGE(icon->gobj()) ) {
58                 _property_icon = Gtk::Invisible().render_icon(Gtk::StockID(image),
59                                                               Gtk::ICON_SIZE_MENU);
60             } else {
61                 delete icon;
62                 return;
63             }
65             delete icon;
66             property_pixbuf() = _icon_cache[_property_event_type] = _property_icon.get_value();
67         }
69     } else {
70         property_pixbuf() = _icon_cache[_property_event_type];
71     }
73     Gtk::CellRendererPixbuf::render_vfunc(window, widget, background_area,
74                                           cell_area, expose_area, flags);
75 }
78 void
79 CellRendererInt::render_vfunc(const Glib::RefPtr<Gdk::Drawable>& window,
80                               Gtk::Widget& widget,
81                               const Gdk::Rectangle& background_area,
82                               const Gdk::Rectangle& cell_area,
83                               const Gdk::Rectangle& expose_area,
84                               Gtk::CellRendererState flags)
85 {
86     if( _filter(_property_number) ) {
87         std::ostringstream s;
88         s << _property_number << std::flush;
89         property_text() = s.str();
90         Gtk::CellRendererText::render_vfunc(window, widget, background_area,
91                                             cell_area, expose_area, flags);
92     }
93 }
95 const CellRendererInt::Filter& CellRendererInt::no_filter = CellRendererInt::NoFilter();
97 UndoHistory& UndoHistory::getInstance()
98 {
99     return *new UndoHistory();
102 void
103 UndoHistory::setDesktop(SPDesktop* desktop)
105     Panel::setDesktop(desktop);
107     if (!desktop) return;
109     _document = sp_desktop_document(desktop);
111     _event_log = desktop->event_log;
113     _callback_connections[EventLog::CALLB_SELECTION_CHANGE].block();
115     _event_list_store = _event_log->getEventListStore();
116     _event_list_view.set_model(_event_list_store);
117     _event_list_selection = _event_list_view.get_selection();
119     _event_log->connectWithDialog(&_event_list_view, &_callback_connections);
120     _event_list_view.scroll_to_row(_event_list_store->get_path(_event_list_selection->get_selected()));
122     _callback_connections[EventLog::CALLB_SELECTION_CHANGE].block(false);
125 UndoHistory::UndoHistory()
126     : UI::Widget::Panel ("", "/dialogs/undo-history", SP_VERB_DIALOG_UNDO_HISTORY),
127       _document (sp_desktop_document(getDesktop())),
128       _event_log (getDesktop() ? getDesktop()->event_log : NULL),
129       _columns (_event_log ? &_event_log->getColumns() : NULL),
130       _event_list_selection (_event_list_view.get_selection())
132     if ( !_document || !_event_log || !_columns ) return;
134     set_size_request(300, 95);
136     _getContents()->pack_start(_scrolled_window);
137     _scrolled_window.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
139     _event_list_store = _event_log->getEventListStore();
141     _event_list_view.set_model(_event_list_store);
142     _event_list_view.set_rules_hint(false);
143     _event_list_view.set_enable_search(false);
144     _event_list_view.set_headers_visible(false);
146     CellRendererSPIcon* icon_renderer = Gtk::manage(new CellRendererSPIcon());
147     icon_renderer->property_xpad() = 8;
148     icon_renderer->property_width() = 36;
149     int cols_count = _event_list_view.append_column("Icon", *icon_renderer);
151     Gtk::TreeView::Column* icon_column = _event_list_view.get_column(cols_count-1);
152     icon_column->add_attribute(icon_renderer->property_event_type(), _columns->type);
154     Gtk::CellRendererText* description_renderer = Gtk::manage(new Gtk::CellRendererText());
156     cols_count = _event_list_view.append_column("Description", *description_renderer);
157     Gtk::TreeView::Column* description_column = _event_list_view.get_column(cols_count-1);
158     description_column->add_attribute(description_renderer->property_text(), _columns->description);
159     description_column->set_resizable();
161     _event_list_view.set_expander_column( *_event_list_view.get_column(cols_count-1) );
163     CellRendererInt* children_renderer = Gtk::manage(new CellRendererInt(greater_than_1));
164     children_renderer->property_weight() = 600; // =Pango::WEIGHT_SEMIBOLD (not defined in old versions of pangomm)
165     children_renderer->property_xalign() = 1.0;
166     children_renderer->property_xpad() = 20;
168     cols_count = _event_list_view.append_column("Children", *children_renderer);
169     Gtk::TreeView::Column* children_column = _event_list_view.get_column(cols_count-1);
170     children_column->add_attribute(children_renderer->property_number(), _columns->child_count);
172     _scrolled_window.add(_event_list_view);
174     // connect EventLog callbacks
175     _callback_connections[EventLog::CALLB_SELECTION_CHANGE] =
176         _event_list_selection->signal_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onListSelectionChange));
178     _callback_connections[EventLog::CALLB_EXPAND] =
179         _event_list_view.signal_row_expanded().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onExpandEvent));
181     _callback_connections[EventLog::CALLB_COLLAPSE] =
182         _event_list_view.signal_row_collapsed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onCollapseEvent));
184     // connect with the EventLog
185     _event_log->connectWithDialog(&_event_list_view, &_callback_connections);
187     show_all_children();
189     // scroll to the selected row
190     _event_list_view.set_cursor(_event_list_store->get_path(_event_log->getCurrEvent()));
193 UndoHistory::~UndoHistory()
197 void
198 UndoHistory::_onListSelectionChange()
201     EventLog::const_iterator selected = _event_list_selection->get_selected();
203     /* If no event is selected in the view, find the right one and select it. This happens whenever
204      * a branch we're currently in is collapsed.
205      */
206     if (!selected) {
208         EventLog::iterator curr_event = _event_log->getCurrEvent();
210         if (curr_event->parent()) {
212             EventLog::iterator curr_event_parent = curr_event->parent();
213             EventLog::iterator last = curr_event_parent->children().end();
215             _event_log->blockNotifications();
216             for ( --last ; curr_event != last ; ++curr_event ) {
217                 SPDocumentUndo::redo(_document);
218             }
219             _event_log->blockNotifications(false);
221             _event_log->setCurrEvent(curr_event);
222             _event_list_selection->select(curr_event_parent);
224         } else {  // this should not happen
225             _event_list_selection->select(curr_event);
226         }
228     } else {
230         EventLog::const_iterator last_selected = _event_log->getCurrEvent();
232         /* Selecting a collapsed parent event is equal to selecting the last child
233          * of that parent's branch.
234          */
236         if ( !selected->children().empty() &&
237              !_event_list_view.row_expanded(_event_list_store->get_path(selected)) )
238         {
239             selected = selected->children().end();
240             --selected;
241         }
243         // An event before the current one has been selected. Undo to the selected event.
244         if ( _event_list_store->get_path(selected) <
245              _event_list_store->get_path(last_selected) )
246         {
247             _event_log->blockNotifications();
249             while ( selected != last_selected ) {
251                 SPDocumentUndo::undo(_document);
253                 if ( last_selected->parent() &&
254                      last_selected == last_selected->parent()->children().begin() )
255                 {
256                     last_selected = last_selected->parent();
257                     _event_log->setCurrEventParent((EventLog::iterator)NULL);
258                 } else {
259                     --last_selected;
260                     if ( !last_selected->children().empty() ) {
261                         _event_log->setCurrEventParent(last_selected);
262                         last_selected = last_selected->children().end();
263                         --last_selected;
264                     }
265                 }
266             }
267             _event_log->blockNotifications(false);
268             _event_log->updateUndoVerbs();
270         } else { // An event after the current one has been selected. Redo to the selected event.
272             _event_log->blockNotifications();
274             while ( selected != last_selected ) {
276                 SPDocumentUndo::redo(_document);
278                 if ( !last_selected->children().empty() ) {
279                     _event_log->setCurrEventParent(last_selected);
280                     last_selected = last_selected->children().begin();
281                 } else {
282                     ++last_selected;
283                     if ( last_selected->parent() &&
284                          last_selected == last_selected->parent()->children().end() )
285                     {
286                         last_selected = last_selected->parent();
287                         ++last_selected;
288                         _event_log->setCurrEventParent((EventLog::iterator)NULL);
289                     }
290                 }
291             }
292             _event_log->blockNotifications(false);
294         }
296         _event_log->setCurrEvent(selected);
297         _event_log->updateUndoVerbs();
298     }
302 void
303 UndoHistory::_onExpandEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &/*path*/)
305     if ( iter == _event_list_selection->get_selected() ) {
306         _event_list_selection->select(_event_log->getCurrEvent());
307     }
310 void
311 UndoHistory::_onCollapseEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &/*path*/)
313     // Collapsing a branch we're currently in is equal to stepping to the last event in that branch
314     if ( iter == _event_log->getCurrEvent() ) {
315         EventLog::const_iterator curr_event_parent = _event_log->getCurrEvent();
316         EventLog::const_iterator curr_event = curr_event_parent->children().begin();
317         EventLog::const_iterator last = curr_event_parent->children().end();
319         _event_log->blockNotifications();
320         SPDocumentUndo::redo(_document);
322         for ( --last ; curr_event != last ; ++curr_event ) {
323             SPDocumentUndo::redo(_document);
324         }
325         _event_log->blockNotifications(false);
327         _event_log->setCurrEvent(curr_event);
328         _event_log->setCurrEventParent(curr_event_parent);
329         _event_list_selection->select(curr_event_parent);
330     }
333 const CellRendererInt::Filter& UndoHistory::greater_than_1 = UndoHistory::GreaterThan(1);
335 } // namespace Dialog
336 } // namespace UI
337 } // namespace Inkscape
339 /*
340   Local Variables:
341   mode:c++
342   c-file-style:"stroustrup"
343   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
344   indent-tabs-mode:nil
345   fill-column:99
346   End:
347 */
348 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :