summaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/cssdialog.cpp
diff options
context:
space:
mode:
authorMartin Owens <doctormo@gmail.com>2018-09-28 17:53:29 +0000
committerJabiertxof <jabier.arraiza@marker.es>2019-02-20 15:35:31 +0000
commitbaa3f20659cb61730a051c70ff7e91b054f5c43a (patch)
tree3effb8442a3caf1c7f2838c916ac8dfd83e85c14 /src/ui/dialog/cssdialog.cpp
parentAllow style properties to be accessed outside (diff)
downloadinkscape-baa3f20659cb61730a051c70ff7e91b054f5c43a.tar.gz
inkscape-baa3f20659cb61730a051c70ff7e91b054f5c43a.zip
Move towards the css dialog being self-sufficient and disable style dialog
Diffstat (limited to 'src/ui/dialog/cssdialog.cpp')
-rw-r--r--src/ui/dialog/cssdialog.cpp269
1 files changed, 218 insertions, 51 deletions
diff --git a/src/ui/dialog/cssdialog.cpp b/src/ui/dialog/cssdialog.cpp
index 3ad621f06..110a1502a 100644
--- a/src/ui/dialog/cssdialog.cpp
+++ b/src/ui/dialog/cssdialog.cpp
@@ -17,13 +17,36 @@
#include "message-context.h"
#include "message-stack.h"
#include "selection.h"
+#include "style.h"
+#include "style-internal.h"
+#include "verbs.h"
#include "ui/icon-loader.h"
#include "ui/widget/iconrenderer.h"
-#include "verbs.h"
+#include "xml/node-event-vector.h"
#include "xml/attribute-record.h"
#include <glibmm/i18n.h>
+#include <glibmm/i18n.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)
+{
+ CSS_DIALOG(data)->onAttrChanged(repr, name, new_value);
+}
+
+Inkscape::XML::NodeEventVector css_repr_events = {
+ nullptr, /* child_added */
+ nullptr, /* child_removed */
+ on_attr_changed,
+ nullptr, /* content_changed */
+ nullptr /* order_changed */
+};
+
namespace Inkscape {
namespace UI {
namespace Dialog {
@@ -39,24 +62,29 @@ namespace Dialog {
*/
CssDialog::CssDialog():
UI::Widget::Panel("/dialogs/css", SP_VERB_DIALOG_CSS),
- _desktop(nullptr)
+ _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);
+ auto _scrolledWindow = new Gtk::ScrolledWindow();
+ _scrolledWindow->add(_treeView);
+ _scrolledWindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
_store = Gtk::ListStore::create(_cssColumns);
_treeView.set_model(_store);
Inkscape::UI::Widget::IconRenderer *addRenderer = manage(new Inkscape::UI::Widget::IconRenderer());
addRenderer->add_icon("edit-delete");
+ addRenderer->signal_activated().connect(sigc::mem_fun(*this, &CssDialog::onPropertyDelete));
- _treeView.append_column("", *addRenderer);
- Gtk::TreeViewColumn *col = _treeView.get_column(0);
+ 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_visible(), _cssColumns.deleteButton);
+ col->set_sort_column(_cssColumns.deleteButton);
+
+ auto add_icon = Gtk::manage(sp_get_icon_image("list-add", GTK_ICON_SIZE_SMALL_TOOLBAR));
col->set_clickable(true);
col->set_widget(*add_icon);
add_icon->set_tooltip_text(_("Add a new attribute"));
@@ -65,30 +93,29 @@ CssDialog::CssDialog():
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, &CssDialog::_addProperty), true);
+ button->signal_button_release_event().connect(sigc::mem_fun(*this, &CssDialog::onPropertyCreate), false);
}
- _propRenderer = Gtk::manage(new Gtk::CellRendererText());
- _propRenderer->property_editable() = true;
- int nameColNum = _treeView.append_column("CSS Property", *_propRenderer) - 1;
+
+ Gtk::CellRendererText *renderer = Gtk::manage(new Gtk::CellRendererText());
+ renderer->property_editable() = true;
+ int nameColNum = _treeView.append_column("Property", *renderer) - 1;
_propCol = _treeView.get_column(nameColNum);
if (_propCol) {
- _propCol->add_attribute(_propRenderer->property_text(), _cssColumns._propertyLabel);
- }
-
- _sheetRenderer = Gtk::manage(new Gtk::CellRendererText());
- _sheetRenderer->property_editable() = true;
- int sheetColNum = _treeView.append_column("Style Sheet", *_sheetRenderer) - 1;
- _sheetCol = _treeView.get_column(sheetColNum);
- if (_sheetCol) {
- _sheetCol->add_attribute(_sheetRenderer->property_text(), _cssColumns._styleSheetVal);
+ _propCol->add_attribute(renderer->property_text(), _cssColumns.label);
+ _propCol->add_attribute(renderer->property_foreground_rgba(), _cssColumns.label_color);
+ _propCol->set_sort_column(_cssColumns.label);
}
- _attrRenderer = Gtk::manage(new Gtk::CellRendererText());
- _attrRenderer->property_editable() = false;
- int attrColNum = _treeView.append_column("Style Attribute", *_attrRenderer) - 1;
+ renderer = Gtk::manage(new Gtk::CellRendererText());
+ renderer->property_editable() = false;
+ int attrColNum = _treeView.append_column("Set", *renderer) - 1;
_attrCol = _treeView.get_column(attrColNum);
if (_attrCol) {
- _attrCol->add_attribute(_attrRenderer->property_text(), _cssColumns._styleAttrVal);
+ _attrCol->add_attribute(renderer->property_text(), _cssColumns._styleAttrVal);
+ _attrCol->add_attribute(renderer->property_foreground_rgba(), _cssColumns.attr_color);
+ _attrCol->add_attribute(renderer->property_strikethrough(), _cssColumns.attr_strike);
+ _attrCol->add_attribute(renderer->property_editable(), _cssColumns.editable);
+ _attrCol->set_sort_column(_cssColumns._styleAttrVal);
}
status.set_halign(Gtk::ALIGN_START);
@@ -103,13 +130,12 @@ CssDialog::CssDialog():
_message_context = std::unique_ptr<Inkscape::MessageContext>(new Inkscape::MessageContext(_message_stack));
_message_changed_connection =
_message_stack->connectChanged(sigc::bind(sigc::ptr_fun(_set_status_message), GTK_WIDGET(status.gobj())));
-
- _getContents()->pack_start(_mainBox, Gtk::PACK_EXPAND_WIDGET);
+ _getContents()->pack_start(*_scrolledWindow, Gtk::PACK_EXPAND_WIDGET);
css_reset_context(0);
setDesktop(getDesktop());
-}
+}
/**
* @brief CssDialog::~CssDialog
@@ -122,13 +148,7 @@ CssDialog::~CssDialog()
_message_context = nullptr;
_message_stack = nullptr;
_message_changed_connection.~connection();
-}
-
-void CssDialog::_set_status_message(Inkscape::MessageType /*type*/, const gchar *message, GtkWidget *widget)
-{
- if (widget) {
- gtk_label_set_markup(GTK_LABEL(widget), message ? message : "");
- }
+ _repr = nullptr;
}
@@ -143,30 +163,177 @@ void CssDialog::setDesktop(SPDesktop* desktop)
}
/**
- * Sets the CSSDialog status bar, depending on which attr is selected.
+
+ * @brief CssDialog::setRepr
+ *
+ * Set the internal xml object that I'm working on right now.
+ */
+void CssDialog::setRepr(Inkscape::XML::Node * repr)
+{
+ if ( repr == _repr ) return;
+ if (_repr) {
+ _store->clear();
+ _repr->removeListenerByData(this);
+ Inkscape::GC::release(_repr);
+ _repr = nullptr;
+ }
+ _repr = repr;
+ if (repr) {
+ Inkscape::GC::anchor(_repr);
+ _repr->addListener(&css_repr_events, this);
+ _repr->synthesizeEvents(&css_repr_events, this);
+ }
+}
+
+/**
+ * @brief CssDialog::parseStyle
+ *
+ * Convert a style string into a vector map. This should be moved to style.cpp
+ *
+ */
+std::map<Glib::ustring, Glib::ustring> CssDialog::parseStyle(Glib::ustring style_string)
+{
+ std::map<Glib::ustring, Glib::ustring> ret;
+
+ REMOVE_SPACES(style_string); // We'd use const, but we need to trip spaces
+ std::vector<Glib::ustring> props = r_props->split(style_string);
+
+ for (auto const token: props) {
+ if (token.empty()) break;
+ std::vector<Glib::ustring> pair = r_pair->split(token);
+
+ if( pair.size() > 1) {
+ ret[pair[0]] = pair[1];
+ }
+ }
+ return ret;
+}
+
+/**
+ * @brief CssDialog::compileStyle
+ *
+ * Turn a vector map back into a style string.
+ *
+ */
+Glib::ustring CssDialog::compileStyle(std::map<Glib::ustring, Glib::ustring> props)
+{
+ auto ret = Glib::ustring("");
+ for (auto const pair: props) {
+ if(!pair.first.empty() && !pair.second.empty()) {
+ ret += pair.first;
+ ret += ":";
+ ret += pair.second;
+ ret += ";";
+ }
+ }
+ return ret;
+}
+
+
+/**
+ * @brief CssDialog::onAttrChanged
+ *
+ * This is called when the XML has an updated attribute (we only care about style)
+ */
+void CssDialog::onAttrChanged(Inkscape::XML::Node *repr, const gchar * name, const gchar * new_value)
+{
+ if(strcmp(name, "style")!=0) return;
+
+ // Clear the list and return if the new_value is empty
+ _store->clear();
+ if(!new_value || new_value[0] == 0) return;
+
+ // Get the object's style attribute and it's calculated properties
+ SPDocument *document = this->_desktop->doc();
+ SPObject *obj = document->getObjectByRepr(repr);
+ //std::vector<SPIBase *> calc_prop = obj->style->properties();
+
+ // Get a dictionary lookup of the style in the attribute
+ std::map<Glib::ustring, Glib::ustring> attr_prop = parseStyle(new_value);
+
+ for (auto iter: obj->style->properties())
+ {
+ if (iter->style && iter->style_src != SP_STYLE_SRC_UNSET) {
+ Gtk::TreeModel::Row row = *(_store->append());
+ // Delete is available to attribute properties only in attr mode.
+ row[_cssColumns.deleteButton] = iter->style_src == SP_STYLE_SRC_ATTRIBUTE;
+ row[_cssColumns.label] = iter->name;
+ if (attr_prop.count(iter->name)) {
+ row[_cssColumns._styleAttrVal] = attr_prop[iter->name];
+ if (attr_prop[iter->name] != iter->get_value()) {
+ row[_cssColumns._styleSheetVal] = iter->get_value();
+ row[_cssColumns.attr_color] = Gdk::RGBA("gray");
+ row[_cssColumns.attr_strike] = true;
+ }
+ row[_cssColumns.deleteButton] = true;
+ } else {
+ row[_cssColumns._styleSheetVal] = iter->get_value();
+ row[_cssColumns.label_color] = Gdk::RGBA("gray");
+ row[_cssColumns.attr_color] = Gdk::RGBA("gray");
+ row[_cssColumns.deleteButton] = false;
+ }
+ }
+
+
+ * @brief CssDialog::setStyleProperty
+ *
+ * Set or delete a single property in the style attribute.
+ */
+bool CssDialog::setStyleProperty(Glib::ustring name, Glib::ustring value)
+{
+ auto original = this->_repr->attribute("style");
+ std::map<Glib::ustring, Glib::ustring> properties = parseStyle(original);
+
+ bool updated = false;
+ if(value != nullptr && !value.empty())
+ {
+ if(properties[name] != value)
+ {
+ // Set value (create or update)
+ properties[name] = value;
+ updated = true;
+ }
+ }
+ else if (properties.count(name))
+ {
+ // Delete value
+ properties.erase(name);
+ updated = true;
+ }
+
+ if (updated)
+ {
+ auto new_styles = this->compileStyle(properties);
+ this->_repr->setAttribute("style", new_styles, false);
+ //this->setUndo(_("Delete style property"));
+ }
+ return updated;
+}
+
+/**
+ * @brief CssDialog::onPropertyDelete
+ *
+ * This function is a slot to signal_activated for '-' button panel.
*/
-void CssDialog::css_reset_context(gint css)
+void CssDialog::onPropertyDelete(Glib::ustring path)
{
- if (css == 0) {
- _message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> CSS property to edit."));
- } else {
- const gchar *name = g_quark_to_string(css);
- _message_context->setF(
- Inkscape::NORMAL_MESSAGE,
- _("Property <b>%s</b> selected. Press <b>Ctrl+Enter</b> when done editing to commit changes."), name);
+ Gtk::TreeModel::Row row = *_store->get_iter(path);
+ if (row) {
+ this->setStyleProperty(row[_cssColumns.label], nullptr);
}
}
/**
- * @brief CssDialog::_addProperty
- * This function is a slot to signal_clicked for '+' button at the bottom of CSS
- * panel. A new row is added, double clicking which text for new property can be
- * added.
+ * @brief CssDialog::onPropertyCreate
+ * This function is a slot to signal_clicked for '+' button panel.
*/
-bool CssDialog::_addProperty(GdkEventButton *event)
+bool CssDialog::onPropertyCreate(GdkEventButton *event)
{
- if (event->type == GDK_BUTTON_RELEASE && event->button == 1) {
- _propRow = *(_store->append());
+ 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, *_propCol, true);
+ grab_focus();
return true;
}
return false;