From c57da20fee24a6e5b368730153d72eabe26425b2 Mon Sep 17 00:00:00 2001 From: buliabyak Date: Thu, 29 Jun 2006 21:27:45 +0000 Subject: [PATCH] added files from Gustav Broberg's patch --- src/event-log.cpp | 255 ++++++++++++++++++++++++++ src/event-log.h | 147 +++++++++++++++ src/event.h | 55 ++++++ src/ui/dialog/undo-history.cpp | 322 +++++++++++++++++++++++++++++++++ src/ui/dialog/undo-history.h | 175 ++++++++++++++++++ 5 files changed, 954 insertions(+) create mode 100644 src/event-log.cpp create mode 100644 src/event-log.h create mode 100644 src/event.h create mode 100644 src/ui/dialog/undo-history.cpp create mode 100644 src/ui/dialog/undo-history.h diff --git a/src/event-log.cpp b/src/event-log.cpp new file mode 100644 index 000000000..40c8364ac --- /dev/null +++ b/src/event-log.cpp @@ -0,0 +1,255 @@ +/* + * Author: + * Gustav Broberg + * + * Copyright (c) 2006 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +#include "desktop.h" + +#include "event-log.h" + +namespace Inkscape { + +EventLog::EventLog() : + UndoStackObserver(), + _connected (false), + _event_list_store (Gtk::TreeStore::create(_columns)), + _event_list_selection (NULL), + _event_list_view (NULL), + _curr_event_parent (NULL), + _notifications_blocked (false), + _callback_connections (NULL) +{ + // add initial pseudo event + Gtk::TreeRow curr_row = *(_event_list_store->append()); + _curr_event = _last_event = curr_row; + + curr_row[_columns.description] = _("[Unchanged]"); + curr_row[_columns.type] = SP_VERB_FILE_NEW; +} + +EventLog::~EventLog() { } + +void +EventLog::notifyUndoEvent(Event* log) +{ + if ( !_notifications_blocked ) { + + // if we're on the first child event... + if ( _curr_event->parent() && + _curr_event == _curr_event->parent()->children().begin() ) + { + // ...back up to the parent + _curr_event = _curr_event->parent(); + _curr_event_parent = (iterator)NULL; + + } else { + + // if we're about to leave a branch, collapse it + if ( !_curr_event->children().empty() && _connected ) { + (*_callback_connections)[CALLB_COLLAPSE].block(); + _event_list_view->collapse_row(_event_list_store->get_path(_curr_event)); + (*_callback_connections)[CALLB_COLLAPSE].block(false); + } + + --_curr_event; + + // if we're entering a branch, move to the end of it + if (!_curr_event->children().empty()) { + _curr_event_parent = _curr_event; + _curr_event = _curr_event->children().end(); + --_curr_event; + } + } + + // update the view + if (_connected) { + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(); + (*_callback_connections)[CALLB_EXPAND].block(); + + Gtk::TreePath curr_path = _event_list_store->get_path(_curr_event); + _event_list_view->expand_to_path(curr_path); + _event_list_selection->select(curr_path); + _event_list_view->scroll_to_row(curr_path); + + (*_callback_connections)[CALLB_EXPAND].block(false); + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false); + } + + } +} + +void +EventLog::notifyRedoEvent(Event* log) +{ + if ( !_notifications_blocked ) { + + // if we're on a parent event... + if ( !_curr_event->children().empty() ) { + + // ...move to its first child + _curr_event_parent = _curr_event; + _curr_event = _curr_event->children().begin(); + + } else { + + ++_curr_event; + + // if we are about to leave a branch... + if ( _curr_event->parent() && + _curr_event == _curr_event->parent()->children().end() ) + { + + // ...collapse it + if (_connected) { + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(); + (*_callback_connections)[CALLB_COLLAPSE].block(); + _event_list_view->collapse_row(_event_list_store->get_path(_curr_event->parent())); + (*_callback_connections)[CALLB_COLLAPSE].block(false); + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false); + } + + // ...and move to the next event at parent level + _curr_event = _curr_event->parent(); + _curr_event_parent = (iterator)NULL; + + ++_curr_event; + } + } + + // update the view + if (_connected) { + Gtk::TreePath curr_path = _event_list_store->get_path(_curr_event); + + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(); + (*_callback_connections)[CALLB_EXPAND].block(); + + _event_list_view->expand_to_path(curr_path); + _event_list_selection->select(curr_path); + _event_list_view->scroll_to_row(curr_path); + + (*_callback_connections)[CALLB_EXPAND].block(false); + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false); + } + + } +} + +void +EventLog::notifyUndoCommitEvent(Event* log) +{ + // If we're not at the last event in list then erase the previously undone events + if ( _last_event != _curr_event ) { + + _last_event = _curr_event; + + if ( !_last_event->children().empty() ) { + _last_event = _last_event->children().begin(); + } else { + ++_last_event; + } + + while ( _last_event != _event_list_store->children().end() ) { + + if (_last_event->parent()) { + while ( _last_event != _last_event->parent()->children().end() ) { + _last_event = _event_list_store->erase(_last_event); + } + _last_event = _last_event->parent(); + + (*_last_event)[_columns.child_count] = _last_event->children().size() + 1; + + ++_last_event; + } else { + _last_event = _event_list_store->erase(_last_event); + } + + } + } + + const unsigned int event_type = log->type; + + Gtk::TreeRow curr_row; + + // if the new event is of the same type as the previous then create a new branch + if ( event_type == (*_curr_event)[_columns.type] ) { + if ( !_curr_event_parent ) { + _curr_event_parent = _curr_event; + } + curr_row = *(_event_list_store->append(_curr_event_parent->children())); + (*_curr_event_parent)[_columns.child_count] = _curr_event_parent->children().size() + 1; + } else { + curr_row = *(_event_list_store->append()); + curr_row[_columns.child_count] = 1; + + _curr_event = _last_event = curr_row; + + // collapse if we're leaving a branch + if (_curr_event_parent && _connected) { + (*_callback_connections)[CALLB_COLLAPSE].block(); + _event_list_view->collapse_row(_event_list_store->get_path(_curr_event_parent)); + (*_callback_connections)[CALLB_COLLAPSE].block(false); + } + + _curr_event_parent = (iterator)(NULL); + } + + _curr_event = _last_event = curr_row; + + curr_row[_columns.type] = event_type; + curr_row[_columns.description] = log->description; + + // update the view + if (_connected) { + Gtk::TreePath curr_path = _event_list_store->get_path(_curr_event); + + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(); + (*_callback_connections)[CALLB_EXPAND].block(); + + _event_list_view->expand_to_path(curr_path); + _event_list_selection->select(curr_path); + _event_list_view->scroll_to_row(curr_path); + + (*_callback_connections)[CALLB_EXPAND].block(false); + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false); + } + +} + +void +EventLog::connectWithDialog(Gtk::TreeView *event_list_view, CallbackMap *callback_connections) +{ + _event_list_view = event_list_view; + _event_list_selection = event_list_view->get_selection(); + _event_list_selection->set_mode(Gtk::SELECTION_SINGLE); + + _callback_connections = callback_connections; + + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(); + (*_callback_connections)[CALLB_EXPAND].block(); + + _event_list_view->expand_to_path(_event_list_store->get_path(_curr_event)); + _event_list_selection->select(_curr_event); + + (*_callback_connections)[CALLB_EXPAND].block(false); + (*_callback_connections)[CALLB_SELECTION_CHANGE].block(false); + + _connected = true; +} + +} +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/event-log.h b/src/event-log.h new file mode 100644 index 000000000..148d6329d --- /dev/null +++ b/src/event-log.h @@ -0,0 +1,147 @@ +/** + * Inkscape::EventLog + * + * A simple log for maintaining a history of commited, undone and redone events along with their + * type. It implements the UndoStackObserver and should be registered with a + * CompositeUndoStackObserver for each document. The event log is then notified on all commit, undo + * and redo events and will store a representation of them in an internal Gtk::TreeStore. + * + * Consecutive events of the same type are grouped with the first event as a parent and following + * as its children. + * + * If a Gtk::TreeView is connected to the event log, the TreeView's selection and its nodes + * expanded/collapsed state will be updated as events are commited, undone and redone. Whenever + * this happens, the event log will block the TreeView's callbacks to prevent circular updates. + * + * Author: + * Gustav Broberg + * + * Copyright (c) 2006 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_EVENT_LOG_H +#define INKSCAPE_EVENT_LOG_H + +#include +#include +#include +#include +#include + +#include "undo-stack-observer.h" +#include "event.h" + +namespace Inkscape { + +/** + * + */ +class EventLog : public UndoStackObserver { + +public: + typedef Gtk::TreeModel::iterator iterator; + typedef Gtk::TreeModel::const_iterator const_iterator; + + EventLog(); + ~EventLog(); + + /** + * Event datatype + */ + + struct EventModelColumns : public Gtk::TreeModelColumnRecord + { + Gtk::TreeModelColumn type; + Gtk::TreeModelColumn description; + Gtk::TreeModelColumn child_count; + + EventModelColumns() + { + add(type); add(description); add(child_count); + } + }; + + /** + * Implementation of Inkscape::UndoStackObserver methods + * \brief Modifies the log's entries and the view's selection when triggered + */ + + void notifyUndoEvent(Event *log); + void notifyRedoEvent(Event *log); + void notifyUndoCommitEvent(Event *log); + + /** + * Accessor functions + */ + + Glib::RefPtr getEventListStore() const { return _event_list_store; } + const EventModelColumns& getColumns() const { return _columns; } + iterator getCurrEvent() const { return _curr_event; } + iterator getCurrEventParent() const { return _curr_event_parent; } + + void setCurrEvent(iterator event) { _curr_event = event; } + void setCurrEventParent(iterator event) { _curr_event_parent = event; } + void blockNotifications(bool status=true) { _notifications_blocked = status; } + + /* + * Callback types for TreeView changes. + */ + + enum CallbackTypes { + CALLB_SELECTION_CHANGE, + CALLB_EXPAND, + CALLB_COLLAPSE, + CALLB_LAST + }; + + typedef std::map CallbackMap; + + /** + * Connect with a TreeView. + */ + void connectWithDialog(Gtk::TreeView *event_list_view, CallbackMap *callback_connections); + +private: + bool _connected; //< connected with dialog + + const EventModelColumns _columns; + + /** + * Helper functions for initialization + */ + + Glib::RefPtr _event_list_store; + Glib::RefPtr _event_list_selection; + Gtk::TreeView *_event_list_view; + + iterator _curr_event; //< current event in _event_list_store + iterator _last_event; //< end position in _event_list_store + iterator _curr_event_parent; //< parent to current event, if any + + bool _notifications_blocked; //< if notifications should be handled + + // Map of connections used to temporary block/unblock callbacks in a TreeView + CallbackMap *_callback_connections; + + // noncopyable, nonassignable + EventLog(EventLog const &other); + EventLog& operator=(EventLog const &other); + +}; + +} // namespace Inkscape + +#endif // INKSCAPE_EVENT_LOG_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/event.h b/src/event.h new file mode 100644 index 000000000..1e3e233cc --- /dev/null +++ b/src/event.h @@ -0,0 +1,55 @@ +#ifndef INKSCAPE_EVENT_H +#define INKSCAPE_EVENT_H + +/* + * Inkscape::Event -- Container for an XML::Event along with some additional information +* describing it. + * + * Author: + * Gustav Broberg + * + * Copyright (c) 2006 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#include + +#include "xml/event-fns.h" +#include "verbs.h" + +namespace Inkscape { +namespace XML { +class Event; +} +} + +namespace Inkscape { + +struct Event { + + Event(XML::Event *_event, unsigned int _type=SP_VERB_NONE, Glib::ustring _description="") + : event (_event), type (_type), description (_description) { } + + ~Event() { sp_repr_free_log (event); } + + XML::Event *event; + const unsigned int type; + Glib::ustring description; +}; + +} // namespace Inkscape + +#endif // INKSCAPE_EVENT_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/undo-history.cpp b/src/ui/dialog/undo-history.cpp new file mode 100644 index 000000000..13f6bff26 --- /dev/null +++ b/src/ui/dialog/undo-history.cpp @@ -0,0 +1,322 @@ +/** + * \brief Undo History dialog + * + * Author: + * Gustav Broberg + * + * Copyright (C) 2006 Authors + * + * Released under GNU GPL. Read the file 'COPYING' for more information. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + + +#include "document.h" +#include "inkscape.h" +#include "ui/icons.h" +#include "verbs.h" + +#include "undo-history.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + +/* Rendering functions for custom cell renderers */ + +void +CellRendererSPIcon::render_vfunc(const Glib::RefPtr& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags) +{ + // if this event type doesn't have an icon... + if ( !Inkscape::Verb::get(_property_event_type)->get_image() ) return; + + // if the icon isn't cached, render it to a pixbuf + if ( !_icon_cache[_property_event_type] ) { + + Glib::ustring image = Inkscape::Verb::get(_property_event_type)->get_image(); + Gtk::Widget* icon = sp_icon_get_icon(image, Inkscape::ICON_SIZE_MENU); + + if (icon) { + + // check icon type (inkscape, gtk, none) + if ( SP_IS_ICON(icon->gobj()) ) { + SPIcon* sp_icon = SP_ICON(icon->gobj()); + sp_icon_fetch_pixbuf(sp_icon); + _property_icon = Glib::wrap(sp_icon->pb, true); + } else if ( GTK_IS_IMAGE(icon->gobj()) ) { + _property_icon = Gtk::Invisible().render_icon(Gtk::StockID(image), + Gtk::ICON_SIZE_MENU); + } else { + delete icon; + return; + } + + delete icon; + property_pixbuf() = _icon_cache[_property_event_type] = _property_icon.get_value(); + } + + } else { + property_pixbuf() = _icon_cache[_property_event_type]; + } + + Gtk::CellRendererPixbuf::render_vfunc(window, widget, background_area, + cell_area, expose_area, flags); +} + + +void +CellRendererInt::render_vfunc(const Glib::RefPtr& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags) +{ + if( _filter(_property_number) ) { + std::ostringstream s; + s << _property_number << std::flush; + property_text() = s.str(); + Gtk::CellRendererText::render_vfunc(window, widget, background_area, + cell_area, expose_area, flags); + } +} + +const CellRendererInt::Filter& CellRendererInt::no_filter = CellRendererInt::NoFilter(); + + +UndoHistory::UndoHistory() + : Dialog ("dialogs.undo-history", SP_VERB_DIALOG_UNDO_HISTORY), + _document (SP_ACTIVE_DOCUMENT), + _event_log (_document ? &_document->getEventLog() : NULL), + _columns (_event_log ? &_event_log->getColumns() : NULL), + _event_list_selection (_event_list_view.get_selection()) +{ + if( !_document || !_event_log || !_columns ) return; + + set_size_request(300, 400); + + get_vbox()->pack_start(_scrolled_window); + _scrolled_window.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); + + _event_list_store = _event_log->getEventListStore(); + + _event_list_view.set_model(_event_list_store); + _event_list_view.set_rules_hint(false); + _event_list_view.set_enable_search(false); + _event_list_view.set_headers_visible(false); + + CellRendererSPIcon* icon_renderer = Gtk::manage(new CellRendererSPIcon()); + icon_renderer->property_xpad() = 8; + icon_renderer->property_width() = 36; + int cols_count = _event_list_view.append_column("Icon", *icon_renderer); + + Gtk::TreeView::Column* icon_column = _event_list_view.get_column(cols_count-1); + icon_column->add_attribute(icon_renderer->property_event_type(), _columns->type); + + Gtk::CellRendererText* description_renderer = Gtk::manage(new Gtk::CellRendererText()); + + cols_count = _event_list_view.append_column("Description", *description_renderer); + Gtk::TreeView::Column* description_column = _event_list_view.get_column(cols_count-1); + description_column->add_attribute(description_renderer->property_text(), _columns->description); + description_column->set_resizable(); + + _event_list_view.set_expander_column( *_event_list_view.get_column(cols_count-1) ); + + CellRendererInt* children_renderer = Gtk::manage(new CellRendererInt(greater_than_1)); + children_renderer->property_weight() = 600; // =Pango::WEIGHT_SEMIBOLD (not defined in old versions of pangomm) + children_renderer->property_xalign() = 1.0; + children_renderer->property_xpad() = 20; + + cols_count = _event_list_view.append_column("Children", *children_renderer); + Gtk::TreeView::Column* children_column = _event_list_view.get_column(cols_count-1); + children_column->add_attribute(children_renderer->property_number(), _columns->child_count); + + _scrolled_window.add(_event_list_view); + + // connect callbacks + _callback_connections[EventLog::CALLB_SELECTION_CHANGE] = + _event_list_selection->signal_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onListSelectionChange)); + + _callback_connections[EventLog::CALLB_EXPAND] = + _event_list_view.signal_row_expanded().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onExpandEvent)); + + _callback_connections[EventLog::CALLB_COLLAPSE] = + _event_list_view.signal_row_collapsed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::UndoHistory::_onCollapseEvent)); + + // connect with the event log + _event_log->connectWithDialog(&_event_list_view, &_callback_connections); + + _event_list_view.scroll_to_row(_event_list_store->get_path(_event_list_selection->get_selected())); + + show_all_children(); + +} + +UndoHistory::~UndoHistory() +{ +} + +void +UndoHistory::_onListSelectionChange() +{ + + EventLog::const_iterator selected = _event_list_selection->get_selected(); + + /* If no event is selected in the view, find the right one and select it. This happens whenever + * a branch we're currently in is collapsed. + */ + if (!selected) { + + EventLog::iterator curr_event = _event_log->getCurrEvent(); + + if (curr_event->parent()) { + + EventLog::iterator curr_event_parent = curr_event->parent(); + EventLog::iterator last = curr_event_parent->children().end(); + + _event_log->blockNotifications(); + for ( --last ; curr_event != last ; ++curr_event ) { + sp_document_redo(_document); + } + _event_log->blockNotifications(false); + + _event_log->setCurrEvent(curr_event); + _event_list_selection->select(curr_event_parent); + + } else { // this should not happen + _event_list_selection->select(curr_event); + } + + } else { + + EventLog::const_iterator last_selected = _event_log->getCurrEvent(); + + /* Selecting a collapsed parent event is equal to selecting the last child + * of that parent's branch. + */ + + if ( !selected->children().empty() && + !_event_list_view.row_expanded(_event_list_store->get_path(selected)) ) + { + selected = selected->children().end(); + --selected; + } + + // An event before the current one has been selected. Undo to the selected event. + if ( _event_list_store->get_path(selected) < + _event_list_store->get_path(last_selected) ) + { + _event_log->blockNotifications(); + + while ( selected != last_selected ) { + + sp_document_undo(_document); + + if ( last_selected->parent() && + last_selected == last_selected->parent()->children().begin() ) + { + last_selected = last_selected->parent(); + _event_log->setCurrEventParent((EventLog::iterator)NULL); + } else { + --last_selected; + if ( !last_selected->children().empty() ) { + _event_log->setCurrEventParent(last_selected); + last_selected = last_selected->children().end(); + --last_selected; + } + } + } + _event_log->blockNotifications(false); + + } else { // An event after the current one has been selected. Redo to the selected event. + + _event_log->blockNotifications(); + + while ( selected != last_selected ) { + + sp_document_redo(_document); + + if ( !last_selected->children().empty() ) { + _event_log->setCurrEventParent(last_selected); + last_selected = last_selected->children().begin(); + } else { + ++last_selected; + if ( last_selected->parent() && + last_selected == last_selected->parent()->children().end() ) + { + last_selected = last_selected->parent(); + ++last_selected; + _event_log->setCurrEventParent((EventLog::iterator)NULL); + } + } + } + _event_log->blockNotifications(false); + + } + + _event_log->setCurrEvent(selected); + } + +} + +void +UndoHistory::_onExpandEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) +{ + if ( iter == _event_list_selection->get_selected() ) + { + _event_list_selection->select(_event_log->getCurrEvent()); + } +} + +void +UndoHistory::_onCollapseEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) +{ + // Collapsing a branch we're currently in is equal to stepping to the last event in that branch + if ( iter == _event_log->getCurrEvent() ) + { + EventLog::const_iterator curr_event_parent = _event_log->getCurrEvent(); + EventLog::const_iterator curr_event = curr_event_parent->children().begin(); + EventLog::const_iterator last = curr_event_parent->children().end(); + + _event_log->blockNotifications(); + sp_document_redo(_document); + + for ( --last ; curr_event != last ; ++curr_event ) { + sp_document_redo(_document); + } + _event_log->blockNotifications(false); + + _event_log->setCurrEvent(curr_event); + _event_log->setCurrEventParent(curr_event_parent); + _event_list_selection->select(curr_event_parent); + } +} + +const CellRendererInt::Filter& UndoHistory::greater_than_1 = UndoHistory::GreaterThan(1); + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/undo-history.h b/src/ui/dialog/undo-history.h new file mode 100644 index 000000000..19c5740a4 --- /dev/null +++ b/src/ui/dialog/undo-history.h @@ -0,0 +1,175 @@ +/** + * Undo History dialog + * + * \brief A dialog for presenting an event log of commited, undone and redone events. Allows the + * user to undo and redo multiple events in a more convinient way than repateaded ctrl-z, + * ctrl-shift-z. + * + * + * Author: + * Gustav Broberg + * + * Copyright (C) 2006 Authors + * + * Released under GNU GPL. Read the file 'COPYING' for more information. + */ + +#ifndef INKSCAPE_UI_DIALOG_UNDO_HISTORY_H +#define INKSCAPE_UI_DIALOG_UNDO_HISTORY_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dialog.h" +#include "event-log.h" + +#include "widgets/icon.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + + +/* Custom cell renderers */ + +class CellRendererSPIcon : public Gtk::CellRendererPixbuf { +public: + + CellRendererSPIcon() : + Glib::ObjectBase(typeid(CellRendererPixbuf)), + Gtk::CellRendererPixbuf(), + _property_icon(*this, "icon", Glib::RefPtr(0)), + _property_event_type(*this, "event_type", 0) + { } + + Glib::PropertyProxy + property_event_type() { return _property_event_type.get_proxy(); } + +protected: + + virtual void + render_vfunc(const Glib::RefPtr& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags); +private: + + Glib::Property > _property_icon; + Glib::Property _property_event_type; + std::map > _icon_cache; +}; + + +class CellRendererInt : public Gtk::CellRendererText { +public: + + struct Filter : std::unary_function { + virtual ~Filter() {} + virtual bool operator() (const int&) const =0; + }; + + CellRendererInt(const Filter& filter=no_filter) : + Glib::ObjectBase(typeid(CellRendererText)), + Gtk::CellRendererText(), + _property_number(*this, "number", 0), + _filter (filter) + { } + + + Glib::PropertyProxy + property_number() { return _property_number.get_proxy(); } + + static const Filter& no_filter; + + protected: + + virtual void + render_vfunc(const Glib::RefPtr& window, + Gtk::Widget& widget, + const Gdk::Rectangle& background_area, + const Gdk::Rectangle& cell_area, + const Gdk::Rectangle& expose_area, + Gtk::CellRendererState flags); + +private: + + Glib::Property _property_number; + const Filter& _filter; + + struct NoFilter : Filter { bool operator() (const int& x) const { return true; } }; +}; + + +/** + * + */ + +class UndoHistory : public Dialog { +public: + virtual ~UndoHistory(); + + static UndoHistory *create() { return new UndoHistory(); } + +protected: + + SPDocument *_document; + EventLog *_event_log; + + const EventLog::EventModelColumns *_columns; + + Gtk::ScrolledWindow _scrolled_window; + + Glib::RefPtr _event_list_store; + Gtk::TreeView _event_list_view; + Glib::RefPtr _event_list_selection; + + EventLog::CallbackMap _callback_connections; + + void _onListSelectionChange(); + void _onExpandEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path); + void _onCollapseEvent(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path); + +private: + + // no default constructor, noncopyable, nonassignable + UndoHistory(); + UndoHistory(UndoHistory const &d); + UndoHistory operator=(UndoHistory const &d); + + struct GreaterThan : CellRendererInt::Filter { + GreaterThan(int _i) : i (_i) {} + bool operator() (const int& x) const { return x > i; } + int i; + }; + + static const CellRendererInt::Filter& greater_than_1; +}; + + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + +#endif //INKSCAPE_UI_DIALOG_UNDO_HISTORY_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : -- 2.30.2