diff options
| author | Martin Owens <doctormo@gmail.com> | 2018-09-14 16:58:18 +0000 |
|---|---|---|
| committer | Martin Owens <doctormo@gmail.com> | 2018-09-14 16:58:18 +0000 |
| commit | b92e577ea62c2a94ba38390fa67d9dcea3db88af (patch) | |
| tree | 3b48af7caf1e7fd14fd0015f6113a6545f11e9b7 /src/ui | |
| parent | Merge branch 'master' of gitlab.com:jordim/inkscape (diff) | |
| download | inkscape-b92e577ea62c2a94ba38390fa67d9dcea3db88af.tar.gz inkscape-b92e577ea62c2a94ba38390fa67d9dcea3db88af.zip | |
Remove sp-xmlview-attr with attrdialog (C++) and improve interface
Diffstat (limited to 'src/ui')
| -rw-r--r-- | src/ui/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/ui/dialog/attrdialog.cpp | 272 | ||||
| -rw-r--r-- | src/ui/dialog/attrdialog.h | 92 | ||||
| -rw-r--r-- | src/ui/dialog/cssdialog.cpp | 25 | ||||
| -rw-r--r-- | src/ui/dialog/cssdialog.h | 1 | ||||
| -rw-r--r-- | src/ui/dialog/xml-tree.cpp | 38 | ||||
| -rw-r--r-- | src/ui/dialog/xml-tree.h | 10 |
7 files changed, 391 insertions, 49 deletions
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 0ae3b8624..6d1ebb785 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -82,6 +82,7 @@ set(ui_SRC dialog/calligraphic-profile-rename.cpp dialog/clonetiler.cpp dialog/color-item.cpp + dialog/attrdialog.cpp dialog/cssdialog.cpp dialog/debug.cpp dialog/desktop-tracker.cpp @@ -233,6 +234,7 @@ set(ui_SRC dialog/calligraphic-profile-rename.h dialog/clonetiler.h dialog/color-item.h + dialog/attrdialog.h dialog/cssdialog.h dialog/debug.h dialog/desktop-tracker.h diff --git a/src/ui/dialog/attrdialog.cpp b/src/ui/dialog/attrdialog.cpp new file mode 100644 index 000000000..529a59720 --- /dev/null +++ b/src/ui/dialog/attrdialog.cpp @@ -0,0 +1,272 @@ +/** @file + * @brief A dialog for XML attributes + */ +/* Authors: + * Martin Owens + * + * Copyright (C) Martin Owens 2018 <doctormo@gmail.com> + * + * Released under GNU GPLv2 or later, read the file 'COPYING' for more information + */ + +#include "attrdialog.h" + +#include "verbs.h" +#include "selection.h" + +#include "helper/icon-loader.h" +#include "ui/widget/addtoicon.h" + +#include "xml/node-event-vector.h" +#include "xml/attribute-record.h" + +static void on_attr_changed (Inkscape::XML::Node * repr, + const gchar * name, + const gchar * /*old_value*/, + const gchar * new_value, + bool /*is_interactive*/, + gpointer data) +{ + ATTR_DIALOG(data)->onAttrChanged(repr, name, new_value); +} +Inkscape::XML::NodeEventVector _repr_events = { + nullptr, /* child_added */ + nullptr, /* child_removed */ + on_attr_changed, + nullptr, /* content_changed */ + nullptr /* order_changed */ +}; + +namespace Inkscape { +namespace UI { +namespace Dialog { + + +/** + * Constructor + * A treeview whose each row corresponds to an XML attribute of a selected node + * New attribute can be added by clicking '+' at bottom of the attr pane. '-' + */ +AttrDialog::AttrDialog(): + UI::Widget::Panel("/dialogs/attr", SP_VERB_DIALOG_CSS), + _desktop(nullptr), + _repr(nullptr) +{ + set_size_request(20, 15); + _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET); + _treeView.set_headers_visible(true); + _scrolledWindow.add(_treeView); + _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + + _store = Gtk::ListStore::create(_attrColumns); + _treeView.set_model(_store); + + Inkscape::UI::Widget::AddToIcon * addRenderer = manage(new Inkscape::UI::Widget::AddToIcon()); + addRenderer->property_active() = false; + + int addCol = _treeView.append_column("", *addRenderer) - 1; + Gtk::TreeViewColumn *col = _treeView.get_column(addCol); + if (col) { + auto add_icon = Gtk::manage(sp_get_icon_image("list-add", GTK_ICON_SIZE_SMALL_TOOLBAR)); + col->add_attribute(addRenderer->property_active(), _attrColumns._colUnsetAttr); + col->set_clickable(true); + col->set_widget(*add_icon); + add_icon->set_tooltip_text("Add a new attribute"); + add_icon->show(); + // This gets the GtkButton inside the GtkBox, inside the GtkAlignment, inside the GtkImage icon. + auto button = add_icon->get_parent()->get_parent()->get_parent(); + // Assign the button event so that create happens BEFORE delete. If this code + // isn't in this exact way, the onAttrDelete is called when the header lines are pressed. + button->signal_button_release_event().connect(sigc::mem_fun(*this, &AttrDialog::onAttrCreate), false); + } + _treeView.signal_button_release_event().connect(sigc::mem_fun(*this, &AttrDialog::onAttrDelete)); + + _nameRenderer = Gtk::manage(new Gtk::CellRendererText()); + _nameRenderer->property_editable() = true; + _nameRenderer->property_placeholder_text().set_value("Attribute Name"); + _nameRenderer->signal_edited().connect(sigc::mem_fun(*this, &AttrDialog::nameEdited)); + int nameColNum = _treeView.append_column("Name", *_nameRenderer) - 1; + _nameCol = _treeView.get_column(nameColNum); + if (_nameCol) { + _nameCol->add_attribute(_nameRenderer->property_text(), _attrColumns._attributeName); + } + + _valueRenderer = Gtk::manage(new Gtk::CellRendererText()); + _valueRenderer->property_editable() = true; + _valueRenderer->property_placeholder_text().set_value("Attribute Value"); + _valueRenderer->property_ellipsize().set_value(Pango::ELLIPSIZE_MIDDLE); + _valueRenderer->signal_edited().connect(sigc::mem_fun(*this, &AttrDialog::valueEdited)); + int valueColNum = _treeView.append_column("Value", *_valueRenderer) - 1; + _valueCol = _treeView.get_column(valueColNum); + if (_valueCol) { + _valueCol->add_attribute(_valueRenderer->property_text(), _attrColumns._attributeValue); + } + + _getContents()->pack_start(_mainBox, Gtk::PACK_EXPAND_WIDGET); + + setDesktop(getDesktop()); +} + + +/** + * @brief AttrDialog::~AttrDialog + * Class destructor + */ +AttrDialog::~AttrDialog() +{ + setDesktop(nullptr); +} + + +/** + * @brief AttrDialog::setDesktop + * @param desktop + * This function sets the 'desktop' for the CSS pane. + */ +void AttrDialog::setDesktop(SPDesktop* desktop) +{ + _desktop = desktop; +} + +/** + * @brief AttrDialog::setRepr + * Set the internal xml object that I'm working on right now. + */ +void AttrDialog::setRepr(Inkscape::XML::Node * repr) +{ + if ( repr == _repr ) return; + if (_repr) { + _store->clear(); + _repr->removeListenerByData(this); + Inkscape::GC::release(_repr); + } + _repr = repr; + if (repr) { + Inkscape::GC::anchor(_repr); + _repr->addListener(&_repr_events, this); + _repr->synthesizeEvents(&_repr_events, this); + } +} + +/** + * @brief AttrDialog::onAttrChanged + * This is called when the XML has an updated attribute + */ +void AttrDialog::onAttrChanged(Inkscape::XML::Node *repr, const gchar * name, const gchar * new_value) +{ + for(auto iter: this->_store->children()) + { + Gtk::TreeModel::Row row = *iter; + Glib::ustring col_name = row[_attrColumns._attributeName]; + if(name == col_name) { + if(new_value) { + row[_attrColumns._attributeValue] = new_value; + new_value = nullptr; // Don't make a new one + } else { + _store->erase(iter); + } + } + } + if(new_value) { + Gtk::TreeModel::Row row = *(_store->append()); + row[_attrColumns._attributeName] = name; + row[_attrColumns._attributeValue] = new_value; + } +} + +/** + * @brief AttrDialog::onAttrCreate + * This function is a slot to signal_clicked for '+' button panel. + */ +bool AttrDialog::onAttrCreate(GdkEventButton *event) +{ + if(event->type == GDK_BUTTON_RELEASE && event->button == 1 && this->_repr) { + Gtk::TreeIter iter = _store->append(); + Gtk::TreeModel::Path path = (Gtk::TreeModel::Path)iter; + _treeView.set_cursor(path, *_nameCol, true); + grab_focus(); + return true; + } + return false; +} + +/** + * @brief AttrDialog::onAttrDelete + * @param event + * @return + * Delete the attribute from the xml + */ +bool AttrDialog::onAttrDelete(GdkEventButton *event) +{ + if (event->type == GDK_BUTTON_RELEASE && event->button == 1 && this->_repr) { + if(!this->_treeView.has_focus()) { + // If the treeView doesn't have focus, then it's probably the edit + // input box or some other widget that propergates to the same click + return false; + } + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn *col = nullptr; + int x2, y2 = 0; + if (_treeView.get_path_at_pos(x, y, path, col, x2, y2)) { + Gtk::TreeModel::Row row = *_store->get_iter(path); + if (col == _treeView.get_column(0) && row) { + Glib::ustring name = row[_attrColumns._attributeName]; + this->_repr->setAttribute(name.c_str(), nullptr, false); + // Return true to prevent propergation + return true; + } + } + } + return false; +} + + +/** + * @brief AttrDialog::nameEdited + * @param event + * @return + * Called when the name is edited in the TreeView editable column + */ +void AttrDialog::nameEdited (const Glib::ustring& path, const Glib::ustring& name) +{ + + Gtk::TreeModel::Row row = *_store->get_iter(path); + if(row) { + Glib::ustring old_name = row[_attrColumns._attributeName]; + Glib::ustring value = row[_attrColumns._attributeValue]; + if(!old_name.empty()) { + // Remove named value + _repr->setAttribute(old_name, nullptr, false); + _repr->setAttribute(name, value, false); + } else { + // Move to editing value, we set the name as a temporary store value + row[_attrColumns._attributeName] = name; + Gtk::TreeModel::Path _path = (Gtk::TreeModel::Path)row; + _treeView.set_cursor(_path, *_valueCol, true); + grab_focus(); + } + } +} + +/** + * @brief AttrDialog::valueEdited + * @param event + * @return + * Called when the value is edited in the TreeView editable column + */ +void AttrDialog::valueEdited (const Glib::ustring& path, const Glib::ustring& value) +{ + + Gtk::TreeModel::Row row = *_store->get_iter(path); + if(row) { + Glib::ustring name = row[_attrColumns._attributeName]; + if(name.empty()) return; + _repr->setAttribute(name, value, false); + } +} + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape diff --git a/src/ui/dialog/attrdialog.h b/src/ui/dialog/attrdialog.h new file mode 100644 index 000000000..23ba8f321 --- /dev/null +++ b/src/ui/dialog/attrdialog.h @@ -0,0 +1,92 @@ +/** @file + * @brief A dialog for XML attributes based on Gtk TreeView + */ +/* Authors: + * Martin Owens + * + * Copyright (C) Martin Owens 2018 <doctormo@gmail.com> + * + * Released under GNU GPLv2 or later, read the file 'COPYING' for more information + */ + +#ifndef ATTRDIALOG_H +#define ATTRDIALOG_H + +#include <gtkmm/treeview.h> +#include <gtkmm/liststore.h> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/dialog.h> +#include <ui/widget/panel.h> + +#include "desktop.h" + +#define ATTR_DIALOG(obj) (dynamic_cast<Inkscape::UI::Dialog::AttrDialog*>((Inkscape::UI::Dialog::AttrDialog*)obj)) + +namespace Inkscape { +namespace UI { +namespace Dialog { + +/** + * @brief The AttrDialog class + * This dialog allows to add, delete and modify XML attributes created in the + * xml editor. + */ +class AttrDialog : public UI::Widget::Panel +{ +public: + AttrDialog(); + ~AttrDialog() override; + + static AttrDialog &getInstance() { return *new AttrDialog(); } + + // Data structure + class AttrColumns : public Gtk::TreeModel::ColumnRecord { + public: + AttrColumns() { + add(_colUnsetAttr); + add(_attributeName); + add(_attributeValue); + } + Gtk::TreeModelColumn<bool> _colUnsetAttr; + Gtk::TreeModelColumn<Glib::ustring> _attributeName; + Gtk::TreeModelColumn<Glib::ustring> _attributeValue; + }; + AttrColumns _attrColumns; + + // TreeView + Gtk::TreeView _treeView; + Glib::RefPtr<Gtk::ListStore> _store; + Gtk::CellRendererText *_nameRenderer; + Gtk::CellRendererText *_valueRenderer; + Gtk::TreeViewColumn *_nameCol; + Gtk::TreeViewColumn *_valueCol; + + // Widgets + Gtk::VBox _mainBox; + Gtk::ScrolledWindow _scrolledWindow; + Gtk::HBox _buttonBox; + Gtk::Button _buttonAddAttribute; + + // Variables - Inkscape + SPDesktop* _desktop; + Inkscape::XML::Node* _repr; + + // Helper functions + void setDesktop(SPDesktop* desktop) override; + void setRepr(Inkscape::XML::Node * repr); + + // Signal handlers + void onAttrChanged(Inkscape::XML::Node *repr, const gchar * name, const gchar * new_value); + bool onAttrCreate(GdkEventButton *event); + bool onAttrDelete(GdkEventButton *event); + void nameEdited(const Glib::ustring &path, const Glib::ustring &name); + void valueEdited(const Glib::ustring &path, const Glib::ustring &value); + +}; + + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + +#endif // ATTRDIALOG_H diff --git a/src/ui/dialog/cssdialog.cpp b/src/ui/dialog/cssdialog.cpp index 901bcf36c..47922fc64 100644 --- a/src/ui/dialog/cssdialog.cpp +++ b/src/ui/dialog/cssdialog.cpp @@ -80,7 +80,11 @@ CssDialog::CssDialog(): _attrCol->add_attribute(_attrRenderer->property_text(), _cssColumns._styleAttrVal); } - _styleButton(_buttonAddProperty, "list-add", "Add a new property"); + GtkWidget *child = GTK_WIDGET(sp_get_icon_image("list-add", GTK_ICON_SIZE_SMALL_TOOLBAR)->gobj()); + gtk_widget_show(child); + _buttonAddProperty.add(*manage(Glib::wrap(child))); + _buttonAddProperty.set_relief(Gtk::RELIEF_NONE); + _buttonAddProperty.set_tooltip_text("Add a new property"); _mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK); _buttonBox.pack_start(_buttonAddProperty, Gtk::PACK_SHRINK); @@ -113,25 +117,6 @@ void CssDialog::setDesktop(SPDesktop* desktop) _desktop = desktop; } - -/** - * @brief CssDialog::_styleButton - * @param btn - * @param iconName - * @param tooltip - * This function sets the style of '+'button at the bottom of dialog. - */ -void CssDialog::_styleButton(Gtk::Button& btn, char const* iconName, - char const* tooltip) -{ - GtkWidget *child = GTK_WIDGET(sp_get_icon_image(iconName, GTK_ICON_SIZE_SMALL_TOOLBAR)->gobj()); - gtk_widget_show(child); - btn.add(*manage(Glib::wrap(child))); - btn.set_relief(Gtk::RELIEF_NONE); - btn.set_tooltip_text(tooltip); -} - - /** * @brief CssDialog::_addProperty * This function is a slot to signal_clicked for '+' button at the bottom of CSS diff --git a/src/ui/dialog/cssdialog.h b/src/ui/dialog/cssdialog.h index 76c98ee8d..6b9657599 100644 --- a/src/ui/dialog/cssdialog.h +++ b/src/ui/dialog/cssdialog.h @@ -79,7 +79,6 @@ public: // Helper functions void setDesktop(SPDesktop* desktop) override; - void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip); // Signal handlers void _addProperty(); diff --git a/src/ui/dialog/xml-tree.cpp b/src/ui/dialog/xml-tree.cpp index e62f0232d..2e53ebf32 100644 --- a/src/ui/dialog/xml-tree.cpp +++ b/src/ui/dialog/xml-tree.cpp @@ -41,8 +41,8 @@ #include "ui/interface.h" #include "ui/tools/tool-base.h" -#include "widgets/sp-xmlview-attr-list.h" #include "widgets/sp-xmlview-tree.h" +#include "ui/dialog/attrdialog.h" #include "ui/dialog/cssdialog.h" namespace Inkscape { @@ -59,7 +59,6 @@ XmlTree::XmlTree() : selected_attr (0), selected_repr (nullptr), tree (nullptr), - attributes (nullptr), status (""), tree_toolbar(), xml_element_new_button ( _("New element node")), @@ -181,28 +180,19 @@ XmlTree::XmlTree() : node_box.pack_start(*tree_scroller); - /* node view */ + /* attributes */ + attributes = new AttrDialog; + attr_box.pack_start(*attributes); notebook_content->insert_page(attr_box, _("_Attributes"), NOTEBOOK_PAGE_ATTRS, true); notebook_content->set_tab_detachable(attr_box, true); - /* attributes */ - attributes = SP_XMLVIEW_ATTR_LIST(sp_xmlview_attr_list_new(nullptr)); - - Gtk::ScrolledWindow *attr_scroller = new Gtk::ScrolledWindow(); - attr_scroller->set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC ); - attr_scroller->set_shadow_type(Gtk::SHADOW_IN); - attr_scroller->set_size_request(-1, 80); - - attr_box.pack_start( *attr_scroller ); - attr_scroller->add(*Gtk::manage(Glib::wrap(GTK_WIDGET(attributes)))); - /* Signal handlers */ GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree)); g_signal_connect (G_OBJECT(selection), "changed", G_CALLBACK (on_tree_select_row), this); g_signal_connect_after( G_OBJECT(tree), "tree_move", G_CALLBACK(after_tree_move), this); - g_signal_connect( G_OBJECT(attributes), "row-value-changed", G_CALLBACK(on_attr_row_changed), this); - g_signal_connect( G_OBJECT(attributes), "attr-value-edited", G_CALLBACK(on_attr_edited), this); + //g_signal_connect( G_OBJECT(attributes), "row-value-changed", G_CALLBACK(on_attr_row_changed), this); + //g_signal_connect( G_OBJECT(attributes), "attr-value-edited", G_CALLBACK(on_attr_edited), this); xml_element_new_button.signal_clicked().connect(sigc::mem_fun(*this, &XmlTree::cmd_new_element_node)); xml_text_new_button.signal_clicked().connect(sigc::mem_fun(*this, &XmlTree::cmd_new_text_node)); @@ -388,9 +378,9 @@ void XmlTree::set_tree_select(Inkscape::XML::Node *repr) void XmlTree::propagate_tree_select(Inkscape::XML::Node *repr) { if (repr && (repr->type() == Inkscape::XML::ELEMENT_NODE)) { - sp_xmlview_attr_list_set_repr(attributes, repr); + attributes->setRepr(repr); } else { - sp_xmlview_attr_list_set_repr(attributes, nullptr); + attributes->setRepr(nullptr); } } @@ -482,7 +472,7 @@ void XmlTree::on_tree_select_row(GtkTreeSelection *selection, gpointer data) } -void XmlTree::after_tree_move(SPXMLViewTree * /*attributes*/, gpointer value, gpointer data) +void XmlTree::after_tree_move(SPXMLViewTree * /*tree*/, gpointer value, gpointer data) { XmlTree *self = static_cast<XmlTree *>(data); guint val = GPOINTER_TO_UINT(value); @@ -631,7 +621,7 @@ void XmlTree::on_tree_unselect_row_disable() lower_node_button.set_sensitive(false); } -void XmlTree::on_attr_edited(SPXMLViewAttrList *attributes, const gchar * name, const gchar * value, gpointer data) +/*void XmlTree::on_attr_edited(SPXMLViewAttrList *attributes, const gchar * name, const gchar * value, gpointer data) { XmlTree *self = static_cast<XmlTree *>(data); g_assert(self->selected_repr != nullptr); @@ -656,10 +646,10 @@ void XmlTree::on_attr_edited(SPXMLViewAttrList *attributes, const gchar * name, } else { DocumentUndo::done(self->current_document, SP_VERB_DIALOG_XML_EDITOR, _("Delete attribute")); } -} +}*/ -void XmlTree::on_attr_row_changed(SPXMLViewAttrList *attributes, const gchar * name, gpointer /*data*/) -{ +//void XmlTree::on_attr_row_changed(SPXMLViewAttrList *attributes, const gchar * name, gpointer /*data*/) +/*{ // Reselect the selected row if the data changes to refresh the attribute and value edit boxes. GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(attributes)); GtkTreeIter iter; @@ -679,7 +669,7 @@ void XmlTree::on_attr_row_changed(SPXMLViewAttrList *attributes, const gchar * n g_free(attr_name); attr_name = nullptr; } -} +}*/ void XmlTree::onCreateNameChanged() { diff --git a/src/ui/dialog/xml-tree.h b/src/ui/dialog/xml-tree.h index 2eff808ef..d5049bb84 100644 --- a/src/ui/dialog/xml-tree.h +++ b/src/ui/dialog/xml-tree.h @@ -27,6 +27,7 @@ #include <gtkmm/paned.h> #include <gtkmm/button.h> +#include "ui/dialog/attrdialog.h" #include "ui/dialog/cssdialog.h" #include "ui/dialog/desktop-tracker.h" #include "message.h" @@ -125,17 +126,17 @@ private: /** * Callback when a node is moved in the tree */ - static void after_tree_move(SPXMLViewTree *attributes, gpointer value, gpointer data); + static void after_tree_move(SPXMLViewTree *tree, gpointer value, gpointer data); /** * Callback for when an attribute is edited. */ - static void on_attr_edited(SPXMLViewAttrList *attributes, const gchar * name, const gchar * value, gpointer /*data*/); + //static void on_attr_edited(SPXMLViewAttrList *attributes, const gchar * name, const gchar * value, gpointer /*data*/); /** * Callback for when attribute list values change */ - static void on_attr_row_changed(SPXMLViewAttrList *attributes, const gchar * name, gpointer data); + //static void on_attr_row_changed(SPXMLViewAttrList *attributes, const gchar * name, gpointer data); /** * Enable widgets based on current selections @@ -216,7 +217,8 @@ private: /* XmlTree Widgets */ SPXMLViewTree *tree; - SPXMLViewAttrList *attributes; + //SPXMLViewAttrList *attributes; + AttrDialog *attributes; CssDialog *styles; /* XML Node Creation pop-up window */ |
