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();
99 UndoHistory::UndoHistory()
100 : Dialog ("dialogs.undo-history", SP_VERB_DIALOG_UNDO_HISTORY),
101 _desktop (SP_ACTIVE_DESKTOP),
102 _document (SP_ACTIVE_DOCUMENT),
103 _event_log (_desktop ? _desktop->event_log : NULL),
104 _columns (_event_log ? &_event_log->getColumns() : NULL),
105 _event_list_selection (_event_list_view.get_selection())
106 {
107 if( !_document || !_event_log || !_columns ) return;
109 set_size_request(300, 400);
111 get_vbox()->pack_start(_scrolled_window);
112 _scrolled_window.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
114 _event_list_store = _event_log->getEventListStore();
116 _event_list_view.set_model(_event_list_store);
117 _event_list_view.set_rules_hint(false);
118 _event_list_view.set_enable_search(false);
119 _event_list_view.set_headers_visible(false);
121 CellRendererSPIcon* icon_renderer = Gtk::manage(new CellRendererSPIcon());
122 icon_renderer->property_xpad() = 8;
123 icon_renderer->property_width() = 36;
124 int cols_count = _event_list_view.append_column("Icon", *icon_renderer);
126 Gtk::TreeView::Column* icon_column = _event_list_view.get_column(cols_count-1);
127 icon_column->add_attribute(icon_renderer->property_event_type(), _columns->type);
129 Gtk::CellRendererText* description_renderer = Gtk::manage(new Gtk::CellRendererText());
131 cols_count = _event_list_view.append_column("Description", *description_renderer);
132 Gtk::TreeView::Column* description_column = _event_list_view.get_column(cols_count-1);
133 description_column->add_attribute(description_renderer->property_text(), _columns->description);
134 description_column->set_resizable();
136 _event_list_view.set_expander_column( *_event_list_view.get_column(cols_count-1) );
138 CellRendererInt* children_renderer = Gtk::manage(new CellRendererInt(greater_than_1));
139 children_renderer->property_weight() = 600; // =Pango::WEIGHT_SEMIBOLD (not defined in old versions of pangomm)
140 children_renderer->property_xalign() = 1.0;
141 children_renderer->property_xpad() = 20;
143 cols_count = _event_list_view.append_column("Children", *children_renderer);
144 Gtk::TreeView::Column* children_column = _event_list_view.get_column(cols_count-1);
145 children_column->add_attribute(children_renderer->property_number(), _columns->child_count);
147 _scrolled_window.add(_event_list_view);
149 // connect callbacks
150 _callback_connections[EventLog::CALLB_SELECTION_CHANGE] =
151 _event_list_selection->signal_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onListSelectionChange));
153 _callback_connections[EventLog::CALLB_EXPAND] =
154 _event_list_view.signal_row_expanded().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onExpandEvent));
156 _callback_connections[EventLog::CALLB_COLLAPSE] =
157 _event_list_view.signal_row_collapsed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onCollapseEvent));
159 // connect with the event log
160 _event_log->connectWithDialog(&_event_list_view, &_callback_connections);
162 _event_list_view.scroll_to_row(_event_list_store->get_path(_event_list_selection->get_selected()));
164 show_all_children();
166 }
168 UndoHistory::~UndoHistory()
169 {
170 }
172 void
173 UndoHistory::_onListSelectionChange()
174 {
176 EventLog::const_iterator selected = _event_list_selection->get_selected();
178 /* If no event is selected in the view, find the right one and select it. This happens whenever
179 * a branch we're currently in is collapsed.
180 */
181 if (!selected) {
183 EventLog::iterator curr_event = _event_log->getCurrEvent();
185 if (curr_event->parent()) {
187 EventLog::iterator curr_event_parent = curr_event->parent();
188 EventLog::iterator last = curr_event_parent->children().end();
190 _event_log->blockNotifications();
191 for ( --last ; curr_event != last ; ++curr_event ) {
192 sp_document_redo(_document);
193 }
194 _event_log->blockNotifications(false);
196 _event_log->setCurrEvent(curr_event);
197 _event_list_selection->select(curr_event_parent);
199 } else { // this should not happen
200 _event_list_selection->select(curr_event);
201 }
203 } else {
205 EventLog::const_iterator last_selected = _event_log->getCurrEvent();
207 /* Selecting a collapsed parent event is equal to selecting the last child
208 * of that parent's branch.
209 */
211 if ( !selected->children().empty() &&
212 !_event_list_view.row_expanded(_event_list_store->get_path(selected)) )
213 {
214 selected = selected->children().end();
215 --selected;
216 }
218 // An event before the current one has been selected. Undo to the selected event.
219 if ( _event_list_store->get_path(selected) <
220 _event_list_store->get_path(last_selected) )
221 {
222 _event_log->blockNotifications();
224 while ( selected != last_selected ) {
226 sp_document_undo(_document);
228 if ( last_selected->parent() &&
229 last_selected == last_selected->parent()->children().begin() )
230 {
231 last_selected = last_selected->parent();
232 _event_log->setCurrEventParent((EventLog::iterator)NULL);
233 } else {
234 --last_selected;
235 if ( !last_selected->children().empty() ) {
236 _event_log->setCurrEventParent(last_selected);
237 last_selected = last_selected->children().end();
238 --last_selected;
239 }
240 }
241 }
242 _event_log->blockNotifications(false);
243 _event_log->updateUndoVerbs();
245 } else { // An event after the current one has been selected. Redo to the selected event.
247 _event_log->blockNotifications();
249 while ( selected != last_selected ) {
251 sp_document_redo(_document);
253 if ( !last_selected->children().empty() ) {
254 _event_log->setCurrEventParent(last_selected);
255 last_selected = last_selected->children().begin();
256 } else {
257 ++last_selected;
258 if ( last_selected->parent() &&
259 last_selected == last_selected->parent()->children().end() )
260 {
261 last_selected = last_selected->parent();
262 ++last_selected;
263 _event_log->setCurrEventParent((EventLog::iterator)NULL);
264 }
265 }
266 }
267 _event_log->blockNotifications(false);
269 }
271 _event_log->setCurrEvent(selected);
272 _event_log->updateUndoVerbs();
273 }
275 }
277 void
278 UndoHistory::_onExpandEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path)
279 {
280 if ( iter == _event_list_selection->get_selected() )
281 {
282 _event_list_selection->select(_event_log->getCurrEvent());
283 }
284 }
286 void
287 UndoHistory::_onCollapseEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path)
288 {
289 // Collapsing a branch we're currently in is equal to stepping to the last event in that branch
290 if ( iter == _event_log->getCurrEvent() )
291 {
292 EventLog::const_iterator curr_event_parent = _event_log->getCurrEvent();
293 EventLog::const_iterator curr_event = curr_event_parent->children().begin();
294 EventLog::const_iterator last = curr_event_parent->children().end();
296 _event_log->blockNotifications();
297 sp_document_redo(_document);
299 for ( --last ; curr_event != last ; ++curr_event ) {
300 sp_document_redo(_document);
301 }
302 _event_log->blockNotifications(false);
304 _event_log->setCurrEvent(curr_event);
305 _event_log->setCurrEventParent(curr_event_parent);
306 _event_list_selection->select(curr_event_parent);
307 }
308 }
310 const CellRendererInt::Filter& UndoHistory::greater_than_1 = UndoHistory::GreaterThan(1);
312 } // namespace Dialog
313 } // namespace UI
314 } // namespace Inkscape
316 /*
317 Local Variables:
318 mode:c++
319 c-file-style:"stroustrup"
320 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
321 indent-tabs-mode:nil
322 fill-column:99
323 End:
324 */
325 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :