summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTavmjong Bah <tavmjong@free.fr>2017-02-04 10:22:35 +0000
committertavmjong-free <tavmjong@free.fr>2017-02-04 10:22:35 +0000
commit2a84c87bd1c6850a9bb9ad5b39f82d1dacb4b94d (patch)
tree96d622a4e1cb20e4e86b8a7fc38dd8d72cc9356c /src
parentDisable rotate guides in doc rotation (diff)
parentAdd some more comments to improve code understanding (diff)
downloadinkscape-2a84c87bd1c6850a9bb9ad5b39f82d1dacb4b94d.tar.gz
inkscape-2a84c87bd1c6850a9bb9ad5b39f82d1dacb4b94d.zip
Merge in GSoC style dialog. (Work in progress.)
(bzr r15471)
Diffstat (limited to 'src')
-rw-r--r--src/document.cpp54
-rw-r--r--src/document.h3
-rw-r--r--src/menus-skeleton.h1
-rw-r--r--src/path-chemistry.cpp6
-rw-r--r--src/sp-flowtext.cpp2
-rw-r--r--src/sp-object.cpp4
-rw-r--r--src/sp-style-elem.cpp25
-rw-r--r--src/style-internal.cpp200
-rw-r--r--src/style-internal.h36
-rw-r--r--src/style.cpp160
-rw-r--r--src/style.h7
-rw-r--r--src/ui/CMakeLists.txt4
-rw-r--r--src/ui/dialog/cssdialog.cpp125
-rw-r--r--src/ui/dialog/cssdialog.h75
-rw-r--r--src/ui/dialog/dialog-manager.cpp6
-rw-r--r--src/ui/dialog/styledialog.cpp1116
-rw-r--r--src/ui/dialog/styledialog.h117
-rw-r--r--src/verbs.cpp10
-rw-r--r--src/verbs.h2
19 files changed, 1746 insertions, 207 deletions
diff --git a/src/document.cpp b/src/document.cpp
index 8072ec53d..57208582a 100644
--- a/src/document.cpp
+++ b/src/document.cpp
@@ -1053,6 +1053,60 @@ sigc::connection SPDocument::connectIdChanged(gchar const *id,
return priv->id_changed_signals[g_quark_from_string(id)].connect(slot);
}
+void _getObjectsByClassRecursive(Glib::ustring const &klass, SPObject *parent, std::vector<SPObject *> &objects)
+{
+ if (parent) {
+ Glib::ustring class_attribute;
+ char const *temp = parent->getAttribute("class");
+ if (temp) {
+ class_attribute = temp;
+ }
+
+ if (class_attribute.find( klass ) != std::string::npos) {
+ objects.push_back( parent );
+ }
+
+ // Check children
+ for (auto& child : parent->children) {
+ _getObjectsByClassRecursive( klass, &child, objects );
+ }
+ }
+}
+
+std::vector<SPObject *> SPDocument::getObjectsByClass(Glib::ustring const &klass) const
+{
+ std::vector<SPObject *> objects;
+ g_return_val_if_fail(!klass.empty(), objects);
+
+ _getObjectsByClassRecursive(klass, root, objects);
+ return objects;
+}
+
+void _getObjectsByElementRecursive(Glib::ustring const &element, SPObject *parent,
+ std::vector<SPObject *> &objects)
+{
+ if (parent) {
+ Glib::ustring prefixed = "svg:" + element;
+ if (parent->getRepr()->name() == prefixed) {
+ objects.push_back(parent);
+ }
+
+ // Check children
+ for (auto& child : parent->children) {
+ _getObjectsByElementRecursive(element, &child, objects);
+ }
+ }
+}
+
+std::vector<SPObject *> SPDocument::getObjectsByElement(Glib::ustring const &element) const
+{
+ std::vector<SPObject *> objects;
+ g_return_val_if_fail(!element.empty(), objects);
+
+ _getObjectsByElementRecursive(element, root, objects);
+ return objects;
+}
+
void SPDocument::bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object)
{
if (object) {
diff --git a/src/document.h b/src/document.h
index 813d4ae49..49bde1897 100644
--- a/src/document.h
+++ b/src/document.h
@@ -179,6 +179,9 @@ public:
SPObject *getObjectById(char const *id) const;
sigc::connection connectIdChanged(const char *id, IDChangedSignal::slot_type slot);
+ std::vector<SPObject *> getObjectsByClass(Glib::ustring const &klass) const;
+ std::vector<SPObject *> getObjectsByElement(Glib::ustring const &element) const;
+
void bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object);
SPObject *getObjectByRepr(Inkscape::XML::Node *repr) const;
diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h
index 75302806b..39bcf0c20 100644
--- a/src/menus-skeleton.h
+++ b/src/menus-skeleton.h
@@ -184,6 +184,7 @@ static char const menus_skeleton[] =
" <submenu name=\"" N_("_Object") "\">\n"
" <verb verb-id=\"DialogObjects\" />\n"
" <verb verb-id=\"DialogTags\" />\n"
+" <verb verb-id=\"DialogStyle\" />\n"
" <separator/>\n"
" <verb verb-id=\"DialogFillStroke\" />\n"
" <verb verb-id=\"DialogObjectProperties\" />\n"
diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp
index 741f433f2..b66bcf368 100644
--- a/src/path-chemistry.cpp
+++ b/src/path-chemistry.cpp
@@ -498,7 +498,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/)
/* Whole text's style */
Glib::ustring style_str =
- item->style->write( SP_STYLE_FLAG_IFDIFF, item->parent ? item->parent->style : NULL); // TODO investigate posibility
+ item->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, item->parent ? item->parent->style : NULL); // TODO investigate posibility
g_repr->setAttribute("style", style_str.c_str());
Inkscape::Text::Layout::iterator iter = te_get_layout(item)->begin();
@@ -519,7 +519,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/)
pos_obj = pos_obj->parent; // SPStrings don't have style
}
Glib::ustring style_str =
- pos_obj->style->write( SP_STYLE_FLAG_IFDIFF, pos_obj->parent ? pos_obj->parent->style : NULL); // TODO investigate posibility
+ pos_obj->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, pos_obj->parent ? pos_obj->parent->style : NULL); // TODO investigate posibility
// get path from iter to iter_next:
SPCurve *curve = te_get_layout(item)->convertToCurves(iter, iter_next);
@@ -579,7 +579,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/)
/* Style */
Glib::ustring style_str =
- item->style->write( SP_STYLE_FLAG_IFDIFF, item->parent ? item->parent->style : NULL); // TODO investigate posibility
+ item->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, item->parent ? item->parent->style : NULL); // TODO investigate posibility
repr->setAttribute("style", style_str.c_str());
/* Mask */
diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp
index 264558014..d79652bf4 100644
--- a/src/sp-flowtext.cpp
+++ b/src/sp-flowtext.cpp
@@ -535,7 +535,7 @@ Inkscape::XML::Node *SPFlowtext::getAsText()
this->layout.getSourceOfCharacter(it, &rawptr, &span_text_start_iter);
SPObject *source_obj = reinterpret_cast<SPObject *>(rawptr);
- Glib::ustring style_text = (dynamic_cast<SPString *>(source_obj) ? source_obj->parent : source_obj)->style->write( SP_STYLE_FLAG_IFDIFF, this->style);
+ Glib::ustring style_text = (dynamic_cast<SPString *>(source_obj) ? source_obj->parent : source_obj)->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, this->style);
if (!style_text.empty()) {
span_tspan->setAttribute("style", style_text.c_str());
}
diff --git a/src/sp-object.cpp b/src/sp-object.cpp
index 0dc301c49..cc862417d 100644
--- a/src/sp-object.cpp
+++ b/src/sp-object.cpp
@@ -1061,7 +1061,9 @@ Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML
}
if (style) {
- Glib::ustring s = style->write(SP_STYLE_FLAG_IFSET);
+ // Write if property set by style attribute in this object
+ Glib::ustring s =
+ style->write(SP_STYLE_FLAG_IFSET & SP_STYLE_FLAG_IFSRC, SP_STYLE_SRC_STYLE_PROP);
// Check for valid attributes. This may be time consuming.
// It is useful, though, for debugging Inkscape code.
diff --git a/src/sp-style-elem.cpp b/src/sp-style-elem.cpp
index 80e45677c..c15f810e9 100644
--- a/src/sp-style-elem.cpp
+++ b/src/sp-style-elem.cpp
@@ -3,6 +3,7 @@
#include "xml/repr.h"
#include "document.h"
#include "sp-style-elem.h"
+#include "sp-root.h"
#include "attributes.h"
#include "style.h"
using Inkscape::XML::TEXT_NODE;
@@ -64,6 +65,7 @@ content_changed_cb(Inkscape::XML::Node *, gchar const *, gchar const *,
SPObject *obj = reinterpret_cast<SPObject *>(data);
g_assert(data != NULL);
obj->read_content();
+ obj->document->getRoot()->emitModified( SP_OBJECT_MODIFIED_CASCADE );
}
static void
@@ -249,6 +251,19 @@ property_cb(CRDocHandler *const a_handler,
g_return_if_fail(append_status == CR_OK);
}
+void update_style_recursively( SPObject *object ) {
+ if (object) {
+ // std::cout << "update_style_recursively: "
+ // << (object->getId()?object->getId():"null") << std::endl;
+ if (object->style) {
+ object->style->readFromObject( object );
+ }
+ for (auto& child : object->children) {
+ update_style_recursively( &child );
+ }
+ }
+}
+
void SPStyleElem::read_content() {
/* fixme: If there's more than one <style> element in a document, then the document stylesheet
* will be set to a random one of them, even switching between them.
@@ -315,9 +330,13 @@ void SPStyleElem::read_content() {
// the <style> is a child of the object that uses a style from it. It just forces the parent of
// <style> to reread its style as soon as the stylesheet is fully loaded. Naturally, this won't
// work if the user of the stylesheet is its grandparent or precedent.
- if ( parent ) {
- parent->style->readFromObject( parent );
- }
+ // if ( parent ) {
+ // parent->style->readFromObject( parent );
+ // }
+
+ // If style sheet has changed, we need to cascade the entire object tree, top down
+ // Get root, read style, loop through children
+ update_style_recursively( (SPObject *)document->getRoot() );
}
/**
diff --git a/src/style-internal.cpp b/src/style-internal.cpp
index 5a2a2f913..a6ccdf376 100644
--- a/src/style-internal.cpp
+++ b/src/style-internal.cpp
@@ -53,6 +53,20 @@ using Inkscape::CSSOStringStream;
// SPIBase --------------------------------------------------------------
+// Standard criteria for writing a property
+// dfp == different from parent
+inline bool should_write( guint const flags, bool set, bool dfp, bool src) {
+
+ bool should_write = false;
+ if ( ((flags & SP_STYLE_FLAG_ALWAYS)) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && set && src) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && set && src && dfp)) {
+ should_write = true;
+ }
+ return should_write;
+}
+
+
// SPIFloat -------------------------------------------------------------
@@ -75,14 +89,12 @@ SPIFloat::read( gchar const *str ) {
}
const Glib::ustring
-SPIFloat::write( guint const flags, SPIBase const *const base) const {
+SPIFloat::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIFloat const *const my_base = dynamic_cast<const SPIFloat*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else {
@@ -151,14 +163,12 @@ SPIScale24::read( gchar const *str ) {
}
const Glib::ustring
-SPIScale24::write( guint const flags, SPIBase const *const base) const {
+SPIScale24::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIScale24 const *const my_base = dynamic_cast<const SPIScale24*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else {
@@ -303,14 +313,12 @@ SPILength::read( gchar const *str ) {
}
const Glib::ustring
-SPILength::write( guint const flags, SPIBase const *const base) const {
+SPILength::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPILength const *const my_base = dynamic_cast<const SPILength*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else {
@@ -460,18 +468,16 @@ SPILengthOrNormal::read( gchar const *str ) {
};
const Glib::ustring
-SPILengthOrNormal::write( guint const flags, SPIBase const *const base) const {
+SPILengthOrNormal::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPILength const *const my_base = dynamic_cast<const SPILength*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->normal) {
return (name + ":normal;");
} else {
- return SPILength::write(flags, base);
+ return SPILength::write(flags, style_src_req, base);
}
}
return Glib::ustring("");
@@ -547,14 +553,12 @@ SPIEnum::read( gchar const *str ) {
}
const Glib::ustring
-SPIEnum::write( guint const flags, SPIBase const *const base) const {
+SPIEnum::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
}
@@ -686,14 +690,12 @@ SPIEnumBits::read( gchar const *str ) {
}
const Glib::ustring
-SPIEnumBits::write( guint const flags, SPIBase const *const base) const {
+SPIEnumBits::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
}
@@ -757,14 +759,12 @@ SPILigatures::read( gchar const *str ) {
}
const Glib::ustring
-SPILigatures::write( guint const flags, SPIBase const *const base) const {
+SPILigatures::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
}
@@ -858,14 +858,12 @@ SPINumeric::read( gchar const *str ) {
}
const Glib::ustring
-SPINumeric::write( guint const flags, SPIBase const *const base) const {
+SPINumeric::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
}
@@ -929,14 +927,12 @@ SPIString::read( gchar const *str ) {
// This routine is actually rarely used. Writing is done usually
// in sp_repr_css_write_string...
const Glib::ustring
-SPIString::write( guint const flags, SPIBase const *const base) const {
+SPIString::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIString const *const my_base = dynamic_cast<const SPIString*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this) ); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else {
@@ -1040,14 +1036,12 @@ void SPIColor::read( gchar const *str ) {
}
const Glib::ustring
-SPIColor::write( guint const flags, SPIBase const *const base) const {
+SPIColor::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIColor const *const my_base = dynamic_cast<const SPIColor*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
CSSOStringStream css;
if (this->currentcolor) {
@@ -1269,14 +1263,12 @@ SPIPaint::read( gchar const *str, SPStyle &style_in, SPDocument *document_in ) {
}
const Glib::ustring
-SPIPaint::write( guint const flags, SPIBase const *const base) const {
+SPIPaint::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIPaint const *const my_base = dynamic_cast<const SPIPaint*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
CSSOStringStream css;
if (this->inherit) {
@@ -1541,14 +1533,12 @@ SPIPaintOrder::read( gchar const *str ) {
}
const Glib::ustring
-SPIPaintOrder::write( guint const flags, SPIBase const *const base) const {
+SPIPaintOrder::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIPaintOrder const *const my_base = dynamic_cast<const SPIPaintOrder*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
CSSOStringStream css;
if (this->inherit) {
@@ -1689,14 +1679,14 @@ SPIFilter::read( gchar const *str ) {
}
}
-const Glib::ustring SPIFilter::write( guint const flags, SPIBase const *const /*base*/) const
+const Glib::ustring SPIFilter::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const /*base*/) const
{
// TODO: fix base
//SPILength const *const my_base = dynamic_cast<const SPILength*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set))
- {
+ // bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool dfp = true;
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else if(this->href && this->href->getURI()) {
@@ -1828,14 +1818,12 @@ SPIDashArray::read( gchar const *str ) {
}
const Glib::ustring
-SPIDashArray::write( guint const flags, SPIBase const *const base) const {
+SPIDashArray::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIDashArray const *const my_base = dynamic_cast<const SPIDashArray*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else if (this->values.empty() ) {
@@ -1945,14 +1933,12 @@ SPIFontSize::read( gchar const *str ) {
}
const Glib::ustring
-SPIFontSize::write( guint const flags, SPIBase const *const base) const {
+SPIFontSize::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIFontSize const *const my_base = dynamic_cast<const SPIFontSize*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
CSSOStringStream css;
if (this->inherit) {
@@ -2239,7 +2225,7 @@ SPIFont::read( gchar const *str ) {
}
}
-const Glib::ustring SPIFont::write( guint const /*flags*/, SPIBase const *const /*base*/) const
+const Glib::ustring SPIFont::write( guint const /*flags*/, SPStyleSrc const & /*style_src_req*/, SPIBase const *const /*base*/) const
{
// At the moment, do nothing. We could add a preference to write out
// 'font' shorthand rather than longhand properties.
@@ -2316,14 +2302,12 @@ SPIBaselineShift::read( gchar const *str ) {
}
const Glib::ustring
-SPIBaselineShift::write( guint const flags, SPIBase const *const base) const {
+SPIBaselineShift::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIBaselineShift const *const my_base = dynamic_cast<const SPIBaselineShift*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || !this->isZero() )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
CSSOStringStream css;
if (this->inherit) {
@@ -2507,13 +2491,11 @@ SPITextDecorationLine::read( gchar const *str ) {
}
const Glib::ustring
-SPITextDecorationLine::write( guint const flags, SPIBase const *const base) const {
+SPITextDecorationLine::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPITextDecorationLine const *const my_base = dynamic_cast<const SPITextDecorationLine*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
Inkscape::CSSOStringStream os;
os << name << ":";
if( inherit ) {
@@ -2639,13 +2621,11 @@ SPITextDecorationStyle::read( gchar const *str ) {
}
const Glib::ustring
-SPITextDecorationStyle::write( guint const flags, SPIBase const *const base) const {
+SPITextDecorationStyle::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPITextDecorationStyle const *const my_base = dynamic_cast<const SPITextDecorationStyle*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
Inkscape::CSSOStringStream os;
os << name << ":";
if( inherit ) {
@@ -2795,7 +2775,7 @@ SPITextDecoration::read( gchar const *str ) {
// Returns CSS2 'text-decoration' (using settings in SPTextDecorationLine)
// This is required until all SVG renderers support CSS3 'text-decoration'
const Glib::ustring
-SPITextDecoration::write( guint const flags, SPIBase const *const base) const {
+SPITextDecoration::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPITextDecoration const *const my_base = dynamic_cast<const SPITextDecoration*>(base);
if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
((flags & SP_STYLE_FLAG_IFSET) && style->text_decoration_line.set) ||
diff --git a/src/style-internal.h b/src/style-internal.h
index 6cc8c6c4e..69b03f503 100644
--- a/src/style-internal.h
+++ b/src/style-internal.h
@@ -35,12 +35,13 @@ struct SPStyleEnum;
static const unsigned SP_STYLE_FLAG_ALWAYS (1 << 2);
static const unsigned SP_STYLE_FLAG_IFSET (1 << 0);
static const unsigned SP_STYLE_FLAG_IFDIFF (1 << 1);
+static const unsigned SP_STYLE_FLAG_IFSRC (1 << 3); // If source matches
enum SPStyleSrc {
SP_STYLE_SRC_UNSET,
- SP_STYLE_SRC_STYLE_PROP,
- SP_STYLE_SRC_STYLE_SHEET,
- SP_STYLE_SRC_ATTRIBUTE
+ SP_STYLE_SRC_ATTRIBUTE, // fill="red"
+ SP_STYLE_SRC_STYLE_PROP, // style="fill:red"
+ SP_STYLE_SRC_STYLE_SHEET, // .red { fill:red; }
};
/* General comments:
@@ -77,6 +78,7 @@ enum SPStyleSrc {
* IFSET: Write a property if 'set' flag is true, otherwise return empty string.
* IFDIFF: Write a property if computed values are different, otherwise return empty string,
* This is only used for text!!
+ * IFSRC Write a property if the source matches the requested source (style sheet, etc.).
*
* read(): Set a property value from a string.
* clear(): Set a property to its default value and set the 'set' flag to false.
@@ -127,17 +129,19 @@ public:
{}
virtual void read( gchar const *str ) = 0;
- virtual void readIfUnset( gchar const *str ) {
+ virtual void readIfUnset( gchar const *str, SPStyleSrc const &source = SP_STYLE_SRC_STYLE_PROP ) {
if ( !set ) {
read( str );
+ style_src = source;
}
}
virtual void readAttribute( Inkscape::XML::Node *repr ) {
- readIfUnset( repr->attribute( name.c_str() ) );
+ readIfUnset( repr->attribute( name.c_str() ), SP_STYLE_SRC_ATTRIBUTE );
}
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const = 0;
virtual void clear() {
set = false, inherit = false;
@@ -176,7 +180,7 @@ public:
unsigned inherits : 1; // Property inherits by default from parent.
unsigned set : 1; // Property has been explicitly set (vs. inherited).
unsigned inherit : 1; // Property value set to 'inherit'.
- SPStyleSrc style_src : 2; // Source (attribute, style attribute, style-sheet). NOT USED YET FIX ME
+ SPStyleSrc style_src : 2; // Source (attribute, style attribute, style-sheet).
// To do: make private after g_asserts removed
public:
@@ -202,6 +206,7 @@ public:
virtual ~SPIFloat() {}
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -283,6 +288,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -355,6 +361,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -411,6 +418,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPILength::clear();
@@ -474,6 +482,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -528,6 +537,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
};
@@ -554,6 +564,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
};
@@ -577,6 +588,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
};
@@ -606,6 +618,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear(); // TODO check about value and value_default
virtual void cascade( const SPIBase* const parent );
@@ -653,6 +666,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -734,6 +748,7 @@ public:
virtual void read( gchar const *str );
virtual void read( gchar const *str, SPStyle &style, SPDocument *document = 0);
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear();
virtual void reset( bool init ); // Used internally when reading or cascading
@@ -833,6 +848,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -885,6 +901,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -924,6 +941,7 @@ public:
virtual ~SPIFilter();
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear();
virtual void cascade( const SPIBase* const parent );
@@ -968,6 +986,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -1024,6 +1043,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -1068,6 +1088,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -1124,6 +1145,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -1171,6 +1193,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -1227,6 +1250,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
diff --git a/src/style.cpp b/src/style.cpp
index c513b735d..86377db08 100644
--- a/src/style.cpp
+++ b/src/style.cpp
@@ -678,22 +678,22 @@ SPStyle::readFromPrefs(Glib::ustring const &path) {
// Matches sp_style_merge_property(SPStyle *style, gint id, gchar const *val)
void
-SPStyle::readIfUnset( gint id, gchar const *val ) {
+SPStyle::readIfUnset( gint id, gchar const *val, SPStyleSrc const &source ) {
- // std::cout << "SPStyle::readIfUnset: Entrance: " << (val?val:"null") << std::endl;
+ // std::cout << "SPStyle::readIfUnset: Entrance: " << id << ": " << (val?val:"null") << std::endl;
// To Do: If it is not too slow, use std::map instead of std::vector inorder to remove switch()
// (looking up SP_PROP_xxxx already uses a hash).
g_return_if_fail(val != NULL);
switch (id) {
case SP_PROP_INKSCAPE_FONT_SPEC:
- font_specification.readIfUnset( val );
+ font_specification.readIfUnset( val, source );
break;
case SP_PROP_FONT_FAMILY:
- font_family.readIfUnset( val );
+ font_family.readIfUnset( val, source );
break;
case SP_PROP_FONT_SIZE:
- font_size.readIfUnset( val );
+ font_size.readIfUnset( val, source );
break;
case SP_PROP_FONT_SIZE_ADJUST:
if (strcmp(val, "none") != 0) {
@@ -701,105 +701,105 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
}
break;
case SP_PROP_FONT_STYLE:
- font_style.readIfUnset( val );
+ font_style.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT:
- font_variant.readIfUnset( val );
+ font_variant.readIfUnset( val, source );
break;
case SP_PROP_FONT_WEIGHT:
- font_weight.readIfUnset( val );
+ font_weight.readIfUnset( val, source );
break;
case SP_PROP_FONT_STRETCH:
- font_stretch.readIfUnset( val );
+ font_stretch.readIfUnset( val, source );
break;
case SP_PROP_FONT:
- font.readIfUnset( val );
+ font.readIfUnset( val, source );
break;
/* Font Variants CSS 3 */
case SP_PROP_FONT_VARIANT_LIGATURES:
- font_variant_ligatures.readIfUnset( val );
+ font_variant_ligatures.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT_POSITION:
- font_variant_position.readIfUnset( val );
+ font_variant_position.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT_CAPS:
- font_variant_caps.readIfUnset( val );
+ font_variant_caps.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT_NUMERIC:
- font_variant_numeric.readIfUnset( val );
+ font_variant_numeric.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT_ALTERNATES:
- font_variant_alternates.readIfUnset( val );
+ font_variant_alternates.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT_EAST_ASIAN:
- font_variant_east_asian.readIfUnset( val );
+ font_variant_east_asian.readIfUnset( val, source );
break;
case SP_PROP_FONT_FEATURE_SETTINGS:
- font_feature_settings.readIfUnset( val );
+ font_feature_settings.readIfUnset( val, source );
break;
/* Text */
case SP_PROP_TEXT_INDENT:
- text_indent.readIfUnset( val );
+ text_indent.readIfUnset( val, source );
break;
case SP_PROP_TEXT_ALIGN:
- text_align.readIfUnset( val );
+ text_align.readIfUnset( val, source );
break;
case SP_PROP_TEXT_DECORATION:
- text_decoration.readIfUnset( val );
+ text_decoration.readIfUnset( val, source );
break;
case SP_PROP_TEXT_DECORATION_LINE:
- text_decoration_line.readIfUnset( val );
+ text_decoration_line.readIfUnset( val, source );
break;
case SP_PROP_TEXT_DECORATION_STYLE:
- text_decoration_style.readIfUnset( val );
+ text_decoration_style.readIfUnset( val, source );
break;
case SP_PROP_TEXT_DECORATION_COLOR:
- text_decoration_color.readIfUnset( val );
+ text_decoration_color.readIfUnset( val, source );
break;
case SP_PROP_LINE_HEIGHT:
- line_height.readIfUnset( val );
+ line_height.readIfUnset( val, source );
break;
case SP_PROP_LETTER_SPACING:
- letter_spacing.readIfUnset( val );
+ letter_spacing.readIfUnset( val, source );
break;
case SP_PROP_WORD_SPACING:
- word_spacing.readIfUnset( val );
+ word_spacing.readIfUnset( val, source );
break;
case SP_PROP_TEXT_TRANSFORM:
- text_transform.readIfUnset( val );
+ text_transform.readIfUnset( val, source );
break;
/* Text (css3) */
case SP_PROP_DIRECTION:
- direction.readIfUnset( val );
+ direction.readIfUnset( val, source );
break;
case SP_PROP_WRITING_MODE:
- writing_mode.readIfUnset( val );
+ writing_mode.readIfUnset( val, source );
break;
case SP_PROP_TEXT_ORIENTATION:
- text_orientation.readIfUnset( val );
+ text_orientation.readIfUnset( val, source );
break;
case SP_PROP_TEXT_ANCHOR:
- text_anchor.readIfUnset( val );
+ text_anchor.readIfUnset( val, source );
break;
case SP_PROP_WHITE_SPACE:
- white_space.readIfUnset( val );
+ white_space.readIfUnset( val, source );
break;
case SP_PROP_SHAPE_INSIDE:
- shape_inside.readIfUnset( val );
+ shape_inside.readIfUnset( val, source );
break;
case SP_PROP_SHAPE_PADDING:
- shape_padding.readIfUnset( val );
+ shape_padding.readIfUnset( val, source );
break;
case SP_PROP_DOMINANT_BASELINE:
- dominant_baseline.readIfUnset( val );
+ dominant_baseline.readIfUnset( val, source );
break;
case SP_PROP_BASELINE_SHIFT:
- baseline_shift.readIfUnset( val );
+ baseline_shift.readIfUnset( val, source );
break;
case SP_PROP_TEXT_RENDERING:
- text_rendering.readIfUnset( val );
+ text_rendering.readIfUnset( val, source );
break;
case SP_PROP_ALIGNMENT_BASELINE:
g_warning("Unimplemented style property SP_PROP_ALIGNMENT_BASELINE: value: %s", val);
@@ -818,25 +818,25 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
g_warning("Unimplemented style property SP_PROP_CLIP: value: %s", val);
break;
case SP_PROP_COLOR:
- color.readIfUnset( val );
+ color.readIfUnset( val, source );
break;
case SP_PROP_CURSOR:
g_warning("Unimplemented style property SP_PROP_CURSOR: value: %s", val);
break;
case SP_PROP_DISPLAY:
- display.readIfUnset( val );
+ display.readIfUnset( val, source );
break;
case SP_PROP_OVERFLOW:
- overflow.readIfUnset( val );
+ overflow.readIfUnset( val, source );
break;
case SP_PROP_VISIBILITY:
- visibility.readIfUnset( val );
+ visibility.readIfUnset( val, source );
break;
case SP_PROP_ISOLATION:
- isolation.readIfUnset( val );
+ isolation.readIfUnset( val, source );
break;
case SP_PROP_MIX_BLEND_MODE:
- mix_blend_mode.readIfUnset( val );
+ mix_blend_mode.readIfUnset( val, source );
break;
/* SVG */
@@ -854,7 +854,7 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
this->object->getRepr()->setAttribute("clip-path", val);
break;
case SP_PROP_CLIP_RULE:
- clip_rule.readIfUnset( val );
+ clip_rule.readIfUnset( val, source );
break;
case SP_PROP_MASK:
/** \todo
@@ -866,14 +866,14 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
this->object->getRepr()->setAttribute("mask", val);
break;
case SP_PROP_OPACITY:
- opacity.readIfUnset( val );
+ opacity.readIfUnset( val, source );
break;
case SP_PROP_ENABLE_BACKGROUND:
- enable_background.readIfUnset( val );
+ enable_background.readIfUnset( val, source );
break;
/* Filter */
case SP_PROP_FILTER:
- if( !filter.inherit ) filter.readIfUnset( val );
+ if( !filter.inherit ) filter.readIfUnset( val, source );
break;
case SP_PROP_FLOOD_COLOR:
g_warning("Unimplemented style property SP_PROP_FLOOD_COLOR: value: %s", val);
@@ -898,86 +898,86 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
/* Paint */
case SP_PROP_COLOR_INTERPOLATION:
// We read it but issue warning
- color_interpolation.readIfUnset( val );
+ color_interpolation.readIfUnset( val, source );
if( color_interpolation.value != SP_CSS_COLOR_INTERPOLATION_SRGB ) {
g_warning("Inkscape currently only supports color-interpolation = sRGB");
}
break;
case SP_PROP_COLOR_INTERPOLATION_FILTERS:
- color_interpolation_filters.readIfUnset( val );
+ color_interpolation_filters.readIfUnset( val, source );
break;
case SP_PROP_COLOR_PROFILE:
g_warning("Unimplemented style property SP_PROP_COLOR_PROFILE: value: %s", val);
break;
case SP_PROP_COLOR_RENDERING:
- color_rendering.readIfUnset( val );
+ color_rendering.readIfUnset( val, source );
break;
case SP_PROP_SOLID_COLOR:
- solid_color.readIfUnset( val );
+ solid_color.readIfUnset( val, source );
break;
case SP_PROP_SOLID_OPACITY:
- solid_opacity.readIfUnset( val );
+ solid_opacity.readIfUnset( val, source );
break;
case SP_PROP_VECTOR_EFFECT:
vector_effect.readIfUnset( val );
break;
case SP_PROP_FILL:
- fill.readIfUnset( val );
+ fill.readIfUnset( val, source );
break;
case SP_PROP_FILL_OPACITY:
- fill_opacity.readIfUnset( val );
+ fill_opacity.readIfUnset( val, source );
break;
case SP_PROP_FILL_RULE:
- fill_rule.readIfUnset( val );
+ fill_rule.readIfUnset( val, source );
break;
case SP_PROP_IMAGE_RENDERING:
- image_rendering.readIfUnset( val );
+ image_rendering.readIfUnset( val, source );
break;
case SP_PROP_MARKER:
/* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
- marker.readIfUnset( val );
+ marker.readIfUnset( val, source );
break;
case SP_PROP_MARKER_START:
/* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
- marker_start.readIfUnset( val );
+ marker_start.readIfUnset( val, source );
break;
case SP_PROP_MARKER_MID:
/* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
- marker_mid.readIfUnset( val );
+ marker_mid.readIfUnset( val, source );
break;
case SP_PROP_MARKER_END:
/* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
- marker_end.readIfUnset( val );
+ marker_end.readIfUnset( val, source );
break;
case SP_PROP_SHAPE_RENDERING:
- shape_rendering.readIfUnset( val );
+ shape_rendering.readIfUnset( val, source );
break;
case SP_PROP_STROKE:
- stroke.readIfUnset( val );
+ stroke.readIfUnset( val, source );
break;
case SP_PROP_STROKE_WIDTH:
- stroke_width.readIfUnset( val );
+ stroke_width.readIfUnset( val, source );
break;
case SP_PROP_STROKE_DASHARRAY:
- stroke_dasharray.readIfUnset( val );
+ stroke_dasharray.readIfUnset( val, source );
break;
case SP_PROP_STROKE_DASHOFFSET:
- stroke_dashoffset.readIfUnset( val );
+ stroke_dashoffset.readIfUnset( val, source );
break;
case SP_PROP_STROKE_LINECAP:
- stroke_linecap.readIfUnset( val );
+ stroke_linecap.readIfUnset( val, source );
break;
case SP_PROP_STROKE_LINEJOIN:
- stroke_linejoin.readIfUnset( val );
+ stroke_linejoin.readIfUnset( val, source );
break;
case SP_PROP_STROKE_MITERLIMIT:
- stroke_miterlimit.readIfUnset( val );
+ stroke_miterlimit.readIfUnset( val, source );
break;
case SP_PROP_STROKE_OPACITY:
- stroke_opacity.readIfUnset( val );
+ stroke_opacity.readIfUnset( val, source );
break;
case SP_PROP_PAINT_ORDER:
- paint_order.readIfUnset( val );
+ paint_order.readIfUnset( val, source );
break;
default:
g_warning("SPIStyle::readIfUnset(): Invalid style property id: %d value: %s", id, val);
@@ -999,16 +999,16 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
* \post ret != NULL.
*/
Glib::ustring
-SPStyle::write( guint const flags, SPStyle const *const base ) const {
+SPStyle::write( guint const flags, SPStyleSrc const &style_src_req, SPStyle const *const base ) const {
// std::cout << "SPStyle::write" << std::endl;
Glib::ustring style_string;
for(std::vector<SPIBase*>::size_type i = 0; i != _properties.size(); ++i) {
if( base != NULL ) {
- style_string += _properties[i]->write( flags, base->_properties[i] );
+ style_string += _properties[i]->write( flags, style_src_req, base->_properties[i] );
} else {
- style_string += _properties[i]->write( flags, NULL );
+ style_string += _properties[i]->write( flags, style_src_req, NULL );
}
}
// for(SPPropMap::iterator i = _propmap.begin(); i != _propmap.end(); ++i ) {
@@ -1112,13 +1112,13 @@ SPStyle::_mergeString( gchar const *const p ) {
CRDeclaration *const decl_list
= cr_declaration_parse_list_from_buf(reinterpret_cast<guchar const *>(p), CR_UTF_8);
if (decl_list) {
- _mergeDeclList( decl_list );
+ _mergeDeclList( decl_list, SP_STYLE_SRC_ATTRIBUTE );
cr_declaration_destroy(decl_list);
}
}
void
-SPStyle::_mergeDeclList( CRDeclaration const *const decl_list ) {
+SPStyle::_mergeDeclList( CRDeclaration const *const decl_list, SPStyleSrc const &source ) {
// std::cout << "SPStyle::_mergeDeclList" << std::endl;
@@ -1126,13 +1126,13 @@ SPStyle::_mergeDeclList( CRDeclaration const *const decl_list ) {
// (Properties are only set if not previously set. See:
// Ref: http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order point 4.)
if (decl_list->next) {
- _mergeDeclList( decl_list->next );
+ _mergeDeclList( decl_list->next, source );
}
- _mergeDecl( decl_list );
+ _mergeDecl( decl_list, source );
}
void
-SPStyle::_mergeDecl( CRDeclaration const *const decl ) {
+SPStyle::_mergeDecl( CRDeclaration const *const decl, SPStyleSrc const &source ) {
// std::cout << "SPStyle::_mergeDecl" << std::endl;
@@ -1145,7 +1145,7 @@ SPStyle::_mergeDecl( CRDeclaration const *const decl ) {
*/
guchar *const str_value_unsigned = cr_term_to_string(decl->value);
gchar *const str_value = reinterpret_cast<gchar *>(str_value_unsigned);
- readIfUnset( prop_idx, str_value );
+ readIfUnset( prop_idx, str_value, source );
g_free(str_value);
}
}
@@ -1160,7 +1160,7 @@ SPStyle::_mergeProps( CRPropList *const props ) {
_mergeProps( cr_prop_list_get_next( props ) );
CRDeclaration *decl = NULL;
cr_prop_list_get_decl(props, &decl);
- _mergeDecl( decl );
+ _mergeDecl( decl, SP_STYLE_SRC_STYLE_SHEET );
}
}
diff --git a/src/style.h b/src/style.h
index c8192f782..00c8c032a 100644
--- a/src/style.h
+++ b/src/style.h
@@ -50,8 +50,9 @@ public:
void read(SPObject *object, Inkscape::XML::Node *repr);
void readFromObject(SPObject *object);
void readFromPrefs(Glib::ustring const &path);
- void readIfUnset( int id, char const *val );
+ void readIfUnset( int id, char const *val, SPStyleSrc const &source = SP_STYLE_SRC_STYLE_PROP );
Glib::ustring write( unsigned int const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPStyle const *const base = NULL ) const;
void cascade( SPStyle const *const parent );
void merge( SPStyle const *const parent );
@@ -64,8 +65,8 @@ public:
private:
void _mergeString( char const *const p );
- void _mergeDeclList( CRDeclaration const *const decl_list );
- void _mergeDecl( CRDeclaration const *const decl );
+ void _mergeDeclList( CRDeclaration const *const decl_list, SPStyleSrc const &source );
+ void _mergeDecl( CRDeclaration const *const decl, SPStyleSrc const &source );
void _mergeProps( CRPropList *const props );
void _mergeObjectStylesheet( SPObject const *const object );
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index f2a256698..11a0c351f 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -57,6 +57,7 @@ set(ui_SRC
dialog/calligraphic-profile-rename.cpp
dialog/clonetiler.cpp
dialog/color-item.cpp
+ dialog/cssdialog.cpp
dialog/debug.cpp
dialog/desktop-tracker.cpp
dialog/dialog-manager.cpp
@@ -99,6 +100,7 @@ set(ui_SRC
dialog/print-colors-preview-dialog.cpp
dialog/print.cpp
dialog/spellcheck.cpp
+ dialog/styledialog.cpp
dialog/svg-fonts-dialog.cpp
dialog/swatches.cpp
dialog/symbols.cpp
@@ -195,6 +197,7 @@ set(ui_SRC
dialog/calligraphic-profile-rename.h
dialog/clonetiler.h
dialog/color-item.h
+ dialog/cssdialog.h
dialog/debug.h
dialog/desktop-tracker.h
dialog/dialog-manager.h
@@ -239,6 +242,7 @@ set(ui_SRC
dialog/print-colors-preview-dialog.h
dialog/print.h
dialog/spellcheck.h
+ dialog/styledialog.h
dialog/svg-fonts-dialog.h
dialog/swatches.h
dialog/symbols.h
diff --git a/src/ui/dialog/cssdialog.cpp b/src/ui/dialog/cssdialog.cpp
new file mode 100644
index 000000000..fa266b012
--- /dev/null
+++ b/src/ui/dialog/cssdialog.cpp
@@ -0,0 +1,125 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "cssdialog.h"
+#include "ui/widget/addtoicon.h"
+#include "widgets/icon.h"
+#include "verbs.h"
+#include "sp-object.h"
+#include "selection.h"
+#include "xml/attribute-record.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * @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 = sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, iconName);
+ gtk_widget_show(child);
+ btn.add(*manage(Glib::wrap(child)));
+ btn.set_relief(Gtk::RELIEF_NONE);
+ btn.set_tooltip_text(tooltip);
+}
+
+/**
+ * Constructor
+ * A treeview whose each row corresponds to a CSS property of selector selected.
+ * New CSS property can be added by clicking '+' at bottom of the CSS pane. '-'
+ * in front of the CSS property row can be clicked to delete the CSS property.
+ * Besides clicking on an already selected property row makes the property editable
+ * and clicking 'Enter' updates the property with changes reflected in the
+ * drawing.
+ */
+CssDialog::CssDialog():
+ UI::Widget::Panel("", "/dialogs/css", SP_VERB_DIALOG_CSS),
+ _desktop(0)
+{
+ set_size_request(20, 15);
+ _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET);
+ _treeView.set_headers_visible(false);
+ _scrolledWindow.add(_treeView);
+ _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
+ _store = Gtk::ListStore::create(_cssColumns);
+ _treeView.set_model(_store);
+
+ Inkscape::UI::Widget::AddToIcon * addRenderer = manage(new Inkscape::UI::
+ Widget::AddToIcon());
+ addRenderer->property_active() = false;
+
+ int addCol = _treeView.append_column("Unset Property", *addRenderer) - 1;
+ Gtk::TreeViewColumn *col = _treeView.get_column(addCol);
+ if (col) {
+ col->add_attribute(addRenderer->property_active(), _cssColumns._colUnsetProp);
+ }
+ _textRenderer = Gtk::manage(new Gtk::CellRendererText());
+ _textRenderer->property_editable() = true;
+
+ int nameColNum = _treeView.append_column("Property", *_textRenderer) - 1;
+ _propCol = _treeView.get_column(nameColNum);
+
+ Gtk::Button* create = manage(new Gtk::Button());
+ _styleButton(*create, "list-add", "Add a new property");
+
+ _mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK);
+ _buttonBox.pack_start(*create, Gtk::PACK_SHRINK);
+
+ _getContents()->pack_start(_mainBox, Gtk::PACK_EXPAND_WIDGET);
+
+ _targetDesktop = getDesktop();
+ setDesktop(_targetDesktop);
+
+ create->signal_clicked().connect(sigc::mem_fun(*this, &CssDialog::_addProperty));
+}
+
+/**
+ * @brief CssDialog::~CssDialog
+ * Class destructor
+ */
+CssDialog::~CssDialog()
+{
+ setDesktop(NULL);
+}
+
+/**
+ * @brief CssDialog::setDesktop
+ * @param desktop
+ * This function sets the 'desktop' for the CSS pane.
+ */
+void CssDialog::setDesktop(SPDesktop* desktop)
+{
+ _desktop = desktop;
+}
+
+/**
+ * @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. _newProperty is set to true in which case the value is appended.
+ */
+void CssDialog::_addProperty()
+{
+ _propRow = *(_store->append());
+ _newProperty = true;
+}
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
diff --git a/src/ui/dialog/cssdialog.h b/src/ui/dialog/cssdialog.h
new file mode 100644
index 000000000..3bbab5031
--- /dev/null
+++ b/src/ui/dialog/cssdialog.h
@@ -0,0 +1,75 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef CSSDIALOG_H
+#define CSSDIALOG_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"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * @brief The CssDialog class
+ * This dialog allows to add, delete and modify CSS properties for selectors
+ * created in Style Dialog. Double clicking any selector in Style dialog, a list
+ * of CSS properties will show up in this dialog (if any exist), else new properties
+ * can be added and each new property forms a new row in this pane.
+ */
+class CssDialog : public UI::Widget::Panel
+{
+public:
+ CssDialog();
+ ~CssDialog();
+
+ static CssDialog &getInstance() { return *new CssDialog(); }
+ void setDesktop(SPDesktop* desktop);
+
+ class CssColumns : public Gtk::TreeModel::ColumnRecord
+ {
+ public:
+ CssColumns()
+ { add(_colUnsetProp); add(_propertyLabel); }
+ Gtk::TreeModelColumn<bool> _colUnsetProp;
+ Gtk::TreeModelColumn<Glib::ustring> _propertyLabel;
+ };
+
+ SPDesktop* _desktop;
+ SPDesktop* _targetDesktop;
+ CssColumns _cssColumns;
+ Gtk::VBox _mainBox;
+ Gtk::HBox _buttonBox;
+ Gtk::TreeView _treeView;
+ Glib::RefPtr<Gtk::ListStore> _store;
+ Gtk::ScrolledWindow _scrolledWindow;
+ Gtk::TreeModel::Row _propRow;
+ Gtk::CellRendererText *_textRenderer;
+ Gtk::TreeViewColumn *_propCol;
+ Glib::ustring _editedProp;
+ bool _newProperty;
+
+ void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip);
+ void _addProperty();
+};
+
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+#endif // CSSDIALOG_H
diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp
index c53112656..b407f8200 100644
--- a/src/ui/dialog/dialog-manager.cpp
+++ b/src/ui/dialog/dialog-manager.cpp
@@ -57,6 +57,8 @@
#include "ui/dialog/svg-fonts-dialog.h"
#include "ui/dialog/objects.h"
#include "ui/dialog/tags.h"
+#include "ui/dialog/styledialog.h"
+#include "ui/dialog/cssdialog.h"
namespace Inkscape {
namespace UI {
@@ -125,6 +127,8 @@ DialogManager::DialogManager() {
registerFactory("Swatches", &create<SwatchesPanel, FloatingBehavior>);
registerFactory("TileDialog", &create<ArrangeDialog, FloatingBehavior>);
registerFactory("Symbols", &create<SymbolsDialog, FloatingBehavior>);
+ registerFactory("StyleDialog", &create<StyleDialog, FloatingBehavior>);
+ registerFactory("CssDialog", &create<CssDialog, FloatingBehavior>);
#if HAVE_POTRACE
registerFactory("Trace", &create<TraceDialog, FloatingBehavior>);
@@ -164,6 +168,8 @@ DialogManager::DialogManager() {
registerFactory("Swatches", &create<SwatchesPanel, DockBehavior>);
registerFactory("TileDialog", &create<ArrangeDialog, DockBehavior>);
registerFactory("Symbols", &create<SymbolsDialog, DockBehavior>);
+ registerFactory("StyleDialog", &create<StyleDialog, DockBehavior>);
+ registerFactory("CssDialog", &create<CssDialog, DockBehavior>);
#if HAVE_POTRACE
registerFactory("Trace", &create<TraceDialog, DockBehavior>);
diff --git a/src/ui/dialog/styledialog.cpp b/src/ui/dialog/styledialog.cpp
new file mode 100644
index 000000000..5246290b4
--- /dev/null
+++ b/src/ui/dialog/styledialog.cpp
@@ -0,0 +1,1116 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "styledialog.h"
+#include "ui/widget/addtoicon.h"
+#include "widgets/icon.h"
+#include "verbs.h"
+#include "sp-object.h"
+#include "selection.h"
+#include "xml/attribute-record.h"
+#include <glibmm/regex.h>
+
+using Inkscape::Util::List;
+using Inkscape::XML::AttributeRecord;
+
+/**
+ * This macro is used to remove spaces around selectors or any strings when
+ * parsing is done to update XML style element or row labels in this dialog.
+ */
+#define REMOVE_SPACES(x) x.erase(0, x.find_first_not_of(' ')); \
+ x.erase(x.find_last_not_of(' ') + 1);
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * @brief StyleDialog::_styleButton
+ * @param btn
+ * @param iconName
+ * @param tooltip
+ * This function sets the style of '+' and '-' buttons at the bottom of dialog.
+ */
+void StyleDialog::_styleButton(Gtk::Button& btn, char const* iconName,
+ char const* tooltip)
+{
+ GtkWidget *child = sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, iconName);
+ gtk_widget_show(child);
+ btn.add(*manage(Glib::wrap(child)));
+ btn.set_relief(Gtk::RELIEF_NONE);
+ btn.set_tooltip_text (tooltip);
+}
+
+/**
+ * Constructor
+ * A treeview and a set of two buttons are added to the dialog. _addSelector
+ * adds selectors to treeview. _delSelector deletes the selector from the dialog.
+ * Any addition/deletion of the selectors updates XML style element accordingly.
+ */
+StyleDialog::StyleDialog() :
+ UI::Widget::Panel("", "/dialogs/style", SP_VERB_DIALOG_STYLE),
+ _desktop(0)
+{
+ set_size_request(200, 200);
+
+ _paned.pack1(_mainBox, Gtk::SHRINK);
+ _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET);
+ _treeView.set_headers_visible(false);
+ _scrolledWindow.add(_treeView);
+ _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
+ _store = Gtk::TreeStore::create(_mColumns);
+ _treeView.set_model(_store);
+
+ Inkscape::UI::Widget::AddToIcon * addRenderer = manage(
+ new Inkscape::UI::Widget::AddToIcon() );
+ addRenderer->property_active() = true;
+
+ int addCol = _treeView.append_column("type", *addRenderer) - 1;
+
+ Gtk::TreeViewColumn *col = _treeView.get_column(addCol);
+ if ( col ) {
+ col->add_attribute( addRenderer->property_active(), _mColumns._colAddRemove );
+ }
+
+ _treeView.append_column("Selector Name", _mColumns._selectorLabel);
+ _treeView.set_expander_column(*(_treeView.get_column(1)));
+
+ create = manage( new Gtk::Button() );
+ _styleButton(*create, "list-add", "Add a new CSS Selector");
+ create->signal_clicked().connect(sigc::mem_fun(*this,
+ &StyleDialog::_addSelector));
+
+ del = manage( new Gtk::Button() );
+ _styleButton(*del, "list-remove", "Remove a CSS Selector");
+ del->signal_clicked().connect(sigc::mem_fun(*this,
+ &StyleDialog::_delSelector));
+ del->set_sensitive(false);
+
+ _mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK);
+
+ _buttonBox.pack_start(*create, Gtk::PACK_SHRINK);
+ _buttonBox.pack_start(*del, Gtk::PACK_SHRINK);
+
+ _getContents()->pack_start(_paned, Gtk::PACK_EXPAND_WIDGET);
+
+ _targetDesktop = getDesktop();
+ setDesktop(_targetDesktop);
+
+ /**
+ * @brief document
+ * If an existing document is opened, its XML representation is obtained
+ * and is then used to populate the treeview with the already existing
+ * selectors in the style element.
+ */
+ _styleExists = false;
+ _document = _targetDesktop->doc();
+ _selectorValue = _populateTree(_getSelectorVec());
+
+ _treeView.signal_button_press_event().connect(sigc::mem_fun(*this,
+ &StyleDialog::
+ _handleButtonEvent),
+ false);
+
+ _treeView.signal_button_press_event().connect_notify(sigc::mem_fun
+ (*this, &StyleDialog::
+ _buttonEventsSelectObjs),
+ false);
+
+ _cssPane = new CssDialog;
+
+ _treeView.get_selection()->signal_changed().connect(sigc::mem_fun(*this,
+ &StyleDialog::
+ _selChanged));
+}
+
+/**
+ * @brief StyleDialog::~StyleDialog
+ * Class destructor
+ */
+StyleDialog::~StyleDialog()
+{
+ setDesktop(NULL);
+}
+
+/**
+ * @brief StyleDialog::setDesktop
+ * @param desktop
+ * This function sets the 'desktop' for the Style Dialog.
+ */
+void StyleDialog::setDesktop( SPDesktop* desktop )
+{
+ Panel::setDesktop(desktop);
+ _desktop = Panel::getDesktop();
+ _desktop->getSelection()->connectChanged(sigc::mem_fun(*this, &StyleDialog::
+ _selectRow));
+}
+
+/**
+ * @brief StyleDialog::_addSelector
+ * This function is the slot to the signal emitted when '+' at the bottom of
+ * the dialog is clicked.
+ */
+void StyleDialog::_addSelector()
+{
+ Gtk::TreeModel::Row row = *(_store->append());
+
+ /**
+ * On clicking '+' button, an entrybox with default text opens up. If an
+ * object is already selected, a selector with value in the entry
+ * is added to a new style element.
+ */
+ Gtk::Dialog *textDialogPtr = new Gtk::Dialog();
+ Gtk::Entry *textEditPtr = manage ( new Gtk::Entry() );
+ textDialogPtr->add_button("Add", Gtk::RESPONSE_OK);
+ textDialogPtr->get_vbox()->pack_start(*textEditPtr, Gtk::PACK_SHRINK);
+
+ /**
+ * By default, the entrybox contains 'Class1' as text. However, if object(s)
+ * is(are) selected and user clicks '+' at the bottom of dialog, the
+ * entrybox will have the id(s) of the selected objects as text.
+ */
+ if (_desktop->getSelection()->isEmpty()) {
+ textEditPtr->set_text("Class1");
+ }
+ else {
+ Inkscape::Selection* selection = _desktop->getSelection();
+ std::vector<SPObject*> selected = std::vector<SPObject *>(selection
+ ->objects().begin(),
+ selection->
+ objects().end());
+ textEditPtr->set_text(_setClassAttribute(selected));
+ }
+
+ textDialogPtr->set_size_request(200, 100);
+ textDialogPtr->show_all();
+ int result = textDialogPtr->run();
+
+ /**
+ * @brief selectorName
+ * This string stores selector name. The text from entrybox is saved as name
+ * for selector. If the entrybox is empty, the text (thus selectorName) is
+ * set to ".Class1"
+ */
+ if (!textEditPtr->get_text().empty()) {
+ _selectorName = textEditPtr->get_text();
+ }
+ else {
+ _selectorName = ".Class1";
+ }
+
+ del->set_sensitive(true);
+
+ /**
+ * The selector name objects is set to the text that the user sets in the
+ * entrybox. If the attribute does not exist, it is
+ * created. In case the attribute already has a value, the new value entered
+ * is appended to the values. If a style attribute does not exist, it is
+ * created with an empty value. Also if a class selector is added, then
+ * class attribute for the selected object is set too.
+ */
+ std::vector<SPObject *> objVec;
+
+ bool objExists = false;
+ if (!_desktop->getSelection()->isEmpty()) {
+ for (auto& obj: _desktop->getSelection()->objects()) {
+ objExists = true;
+ if (!obj->getRepr()->attribute("style")) {
+ obj->getRepr()->setAttribute("style", NULL);
+ }
+
+ if (obj->getAttribute("style") == NULL) {
+ _selectorValue = _selectorName + "{" + "}" + "\n";
+ }
+ else {
+ _selectorValue = _selectorName + "{"
+ + obj->getAttribute("style") + "}" + "\n";
+ }
+
+ if (_selectorName[0] == '.') {
+ if (!obj->getRepr()->attribute("class")) {
+ obj->getRepr()->setAttribute("class", textEditPtr->get_text()
+ .erase(0,1));
+ }
+ else {
+ obj->getRepr()->setAttribute("class", std::string(obj->
+ getRepr()->
+ attribute("class"))
+ + " " + textEditPtr->get_text()
+ .erase(0,1));
+ }
+ }
+ }
+ }
+ else {
+ _selectorValue = _selectorName + "{" + "}" + "\n";
+ objExists = false;
+ }
+
+ switch (result) {
+ case Gtk::RESPONSE_OK:
+ textDialogPtr->hide();
+ row[_mColumns._selectorLabel] = _selectorName;
+ row[_mColumns._colAddRemove] = true;
+ if (objExists) {
+ Inkscape::Selection* selection = _desktop->getSelection();
+ row[_mColumns._colObj] = std::vector<SPObject *>(selection->objects()
+ .begin(), selection
+ ->objects().end());
+ objVec = row[_mColumns._colObj];
+ }
+ break;
+ default:
+ break;
+ }
+
+ /**
+ * A new style element is added to the document with value obtained
+ * from selectorValue above. If style element already exists, then
+ * the new selector's content is appended to its previous content.
+ */
+ inkSelector._selector = _selectorName;
+ inkSelector._matchingObjs = objVec;
+ inkSelector._xmlContent = _selectorValue;
+ _selectorVec.push_back(inkSelector);
+
+ if (_styleElementNode()) {
+ _styleChild = _styleElementNode();
+ _updateStyleContent();
+ }
+ else if (_styleExists && !_newDrawing) {
+ _updateStyleContent();
+ }
+ else if (!_styleExists) {
+ Inkscape::XML::Node *root = _document->getReprDoc()->root();
+ Inkscape::XML::Node *newChild = _document->getReprDoc()
+ ->createElement("svg:style");
+ Inkscape::XML::Node *smallChildren = _document->getReprDoc()
+ ->createTextNode(_selectorValue.c_str());
+
+ newChild->appendChild(smallChildren);
+ Inkscape::GC::release(smallChildren);
+
+ root->addChild(newChild, NULL);
+ Inkscape::GC::release(newChild);
+ _styleChild = newChild;
+ }
+ _selAdd(row);
+}
+
+/**
+ * @brief StyleDialog::_updateStyleContent
+ * This function updates the content in style element as new selectors (or
+ * objects) are added/removed.
+ */
+void StyleDialog::_updateStyleContent()
+{
+ std::string styleContent = "";
+ for (unsigned i = 0; i < _selectorVec.size(); ++i) {
+ styleContent = styleContent + _selectorVec[i]._xmlContent;
+ }
+ _styleChild->firstChild()->setContent(styleContent.c_str());
+}
+
+/**
+ * @brief StyleDialog::_delSelector
+ * This function deletes selector when '-' at the bottom is clicked. The index
+ * of selected row is obtained and the corresponding selector and its values are
+ * deleted from the selector vector. If a row has no parent, it is directly
+ * erased from the vector along with its child rows. The style element is updated
+ * accordingly.
+ */
+void StyleDialog::_delSelector()
+{
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ std::string sel, key, value;
+ std::vector<InkSelector>::iterator it;
+ for (it = _selectorVec.begin(); it != _selectorVec.end();) {
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel];
+ std::string matchSelector = selectedRowLabel;
+ REMOVE_SPACES(matchSelector);
+ if (key == matchSelector) {
+ it = _selectorVec.erase(it);
+ _store->erase(row);
+ }
+ else {
+ ++it;
+ }
+
+ /**
+ * The _stylechild is obtained which contains the style element and
+ * the content in style element is updated. If _selectorVec is
+ * empty, the style element is removed from the XML repr else
+ * the content is updated simply using _updateStyleContent().
+ */
+ _styleChild = _styleElementNode();
+ if (_store->children().empty()) {
+ _document->getReprRoot()->removeChild(_styleChild);
+ _styleExists = false;
+ }
+ else {
+ _updateStyleContent();
+ }
+ }
+ }
+}
+
+/**
+ * @brief StyleDialog::_styleElementNode
+ * @return
+ * This function returns the node containing style element. The document's
+ * children are iterated and the repr of the style element that occurs is
+ * obtained.
+ */
+Inkscape::XML::Node* StyleDialog::_styleElementNode()
+{
+ for (unsigned i = 0; i < _document->getReprRoot()->childCount(); ++i) {
+ if (std::string(_document->getReprRoot()->nthChild(i)->name())
+ == "svg:style") {
+ _styleExists = true;
+ _newDrawing = true;
+ return _document->getReprRoot()->nthChild(i);
+ }
+ }
+ return NULL;
+}
+
+/**
+ * @brief StyleDialog::_setClassAttribute
+ * @param sel
+ * @return This function returns the ids of objects selected which are passed
+ * to entrybox.
+ */
+std::string StyleDialog::_setClassAttribute(std::vector<SPObject*> sel)
+{
+ std::string str = "";
+ for ( unsigned i = 0; i < sel.size(); ++i ) {
+ SPObject *obj = sel.at(i);
+ str = str + "#" + std::string(obj->getId()) + " ";
+ }
+ return str;
+}
+
+/**
+ * @brief StyleDialog::_getSelectorVec
+ * @return selVec
+ * This function returns a vector whose key is the style selector name and value
+ * is the style properties. All style selectors are extracted from svg:style
+ * element. _newDrawing is flag is set to false check if an existing drawing is
+ * opened.
+ */
+std::vector<StyleDialog::InkSelector> StyleDialog::_getSelectorVec()
+{
+ for (unsigned i = 0; i < _document->getReprRoot()->childCount(); ++i) {
+ if (std::string(_document->getReprRoot()->nthChild(i)->name()) == "svg:style") {
+ _styleExists = true;
+ _newDrawing = false;
+ _styleChild = _document->getReprRoot()->nthChild(i);
+
+ // Get content from first style element.
+ std::string content = _styleChild->firstChild()->content();
+
+ // Remove end-of-lines (check it works on Windoze).
+ content.erase(std::remove(content.begin(), content.end(), '\n'), content.end());
+
+ // First split into selector/value chunks.
+ // An attempt to use Glib::Regex failed. A C++11 version worked but
+ // reportedly has problems on Windows. Using split_simple() is simpler
+ // and probably faster.
+ //
+ // Glib::RefPtr<Glib::Regex> regex1 =
+ // Glib::Regex::create("([^\\{]+)\\{([^\\{]+)\\}");
+ //
+ // Glib::MatchInfo minfo;
+ // regex1->match(content, minfo);
+
+ // Split on curly brackets. Even tokens are selectors, odd are values.
+ std::vector<std::string> tokens = Glib::Regex::split_simple("[}{]", content);
+
+ for (unsigned i = 0; i < tokens.size()-1; i += 2) {
+ std::string selectors = tokens[i];
+ REMOVE_SPACES(selectors); // Remove leading/trailing spaces
+
+ /** Make a list of all objects that selector matches. This is
+ * currently limited to simple id, class, and element selectors.
+ * Expanding this would take integrating a true CSS parser.
+ */
+ std::vector<SPObject *>objVec;
+
+ // Split selector string into individual selectors (which are comma separated).
+ std::vector<std::string> tokens2 = Glib::Regex::split_simple
+ ("\\s*,\\s*", selectors );
+
+ for(unsigned i = 0; i < tokens2.size(); ++i) {
+ std::string token2 = tokens2[i];
+
+ // Find objects that match class selector
+ if (token2[0] == '.') {
+ token2.erase(0,1);
+ std::vector<SPObject *> objects = _document
+ ->getObjectsByClass(token2);
+ objVec.insert(objVec.end(), objects.begin(), objects.end());
+ }
+
+ // Find objects that match id selector
+ else if (token2[0] == '#') {
+ token2.erase(0,1);
+ SPObject * object = _document->getObjectById(token2);
+ if (object) {
+ objVec.push_back(object);
+ }
+ }
+
+ // Find objects that match element selector
+ else {
+ std::vector<SPObject *> objects = _document->
+ getObjectsByElement(token2);
+ objVec.insert(objVec.end(), objects.begin(), objects.end());
+ }
+ }
+
+ std::string values;
+ // Check to make sure we do have a value to match selector.
+ if ((i+1) < tokens.size()) {
+ values = tokens[i+1];
+ } else {
+ std::cerr << "StyleDialog::_getSelectorVec: Missing values "
+ "for last selector!" << std::endl;
+ }
+
+ _selectorValue = selectors + "{" + values + "}\n";
+ inkSelector._selector = selectors;
+ inkSelector._matchingObjs = objVec;
+ inkSelector._xmlContent = _selectorValue;
+ _selectorVec.push_back(inkSelector);
+ }
+ }
+ }
+ return _selectorVec;
+}
+
+/**
+ * @brief StyleDialog::_populateTree
+ * @param _selVec
+ * This function populates the treeview with selectors available in the
+ * stylesheet.
+ */
+std::string StyleDialog::_populateTree(std::vector<InkSelector> _selVec)
+{
+ _selectorVec = _selVec;
+ std::string selectorValue;
+
+ for(unsigned it = 0; it < _selectorVec.size(); ++it) {
+ Gtk::TreeModel::Row row = *(_store->append());
+ row[_mColumns._selectorLabel] = _selectorVec[it]._selector;
+ row[_mColumns._colAddRemove] = true;
+ row[_mColumns._colObj] = _selectorVec[it]._matchingObjs;
+ std::string selValue = _selectorVec[it]._xmlContent;
+ selectorValue.append(selValue.c_str());
+ }
+
+ if (_selectorVec.size() > 0) {
+ del->set_sensitive(true);
+ }
+
+ return selectorValue;
+}
+
+/**
+ * @brief StyleDialog::_handleButtonEvent
+ * @param event
+ * @return
+ * This function handles the event when '+' button in front of a selector name
+ * is clicked. The selected objects (if any) is added to the selector as a child
+ * in the treeview.
+ */
+bool StyleDialog::_handleButtonEvent(GdkEventButton *event)
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+ Gtk::TreeViewColumn *col = 0;
+ Gtk::TreeModel::Path path;
+ int x = static_cast<int>(event->x);
+ int y = static_cast<int>(event->y);
+ int x2 = 0;
+ int y2 = 0;
+ if (_treeView.get_path_at_pos(x, y, path, col, x2, y2)) {
+ if (col == _treeView.get_column(0)) {
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection =
+ _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->
+ get_selected();
+ Gtk::TreeModel::Row row = *iter;
+
+ /**
+ * This adds child rows to selected rows. If the parent row is
+ * a class selector, then the class attribute of object added
+ * to child row is appended with class in the parent row. The
+ * else below deletes objects from selectors when 'delete' button
+ * in front of child row is clicked. The class attribute is updated
+ * by removing the parent row's class selector name.
+ */
+ if (!row.parent()) {
+ _selAdd(row);
+ }
+
+ else {
+ std::string sel, key, value;
+ std::vector<InkSelector>::iterator it;
+ Gtk::TreeModel::Row parentRow = *(row).parent();
+ Glib::ustring parentKey = parentRow[_mColumns._selectorLabel];
+
+ for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) {
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ /**
+ * @brief matchSelector
+ * For id selectors, whenever any child row is deleted,
+ * the row label is updated and so is the entry for the
+ * selector in style element.
+ */
+ std::string matchSelector = parentKey;
+ REMOVE_SPACES(matchSelector);
+ if (key == matchSelector) {
+ if (key[0] == '#') {
+ std::string s = parentKey;
+ Glib::ustring toDelRow = row[_mColumns._selectorLabel];
+ std::string toDelKey = toDelRow;
+ std::size_t idFound = s.find(toDelKey);
+ if (idFound != std::string::npos) {
+ if (idFound == 0) {
+ s.erase(idFound, toDelKey.length()+1);
+ parentKey = s;
+ parentRow[_mColumns._selectorLabel] = parentKey;
+ (*it)._xmlContent.erase(idFound, toDelKey.length());
+ }
+ else {
+ s.erase(idFound-2, toDelKey.length()+2);
+ parentKey = s;
+ parentRow[_mColumns._selectorLabel] = parentKey;
+ (*it)._xmlContent.erase(idFound-2, toDelKey.
+ length()+2);
+ }
+ }
+ }
+ }
+
+ if (parentKey[0] == '.') {
+ std::vector<SPObject *> objVec = row[_mColumns._colObj];
+ for (unsigned i = 0; i < objVec.size(); ++i) {
+ SPObject *obj = objVec[i];
+ std::string classAttr = std::string(obj->getRepr()
+ ->attribute("class"));
+ std::size_t found = classAttr.find(parentKey.erase(0,1));
+ if (found != std::string::npos) {
+ classAttr.erase(found, parentKey.length()+1);
+ obj->getRepr()->setAttribute("class", classAttr);
+ }
+ }
+ }
+
+ if (parentKey.empty()) {
+ (*it)._xmlContent = "";
+ }
+ }
+
+ if (_styleChild) {
+ _updateStyleContent();
+ }
+ else {
+ _styleChild = _styleElementNode();
+ _updateStyleContent();
+ }
+
+ _store->erase(row);
+
+ /**
+ * On continuous deletion of objects (child rows) from the
+ * selector (parent row), if the parent row has no child, then
+ * the row is erased from the _store. Further if there is no
+ * row left in _store, which implies there is no content in
+ * XML style element, then the 'svg:style' element is also
+ * removed.
+ */
+ if (parentKey.empty()) {
+ _store->erase(parentRow);
+ }
+
+ if (parentKey.empty() && _store->children().empty()) {
+ _document->getReprRoot()->removeChild(_styleChild);
+ _styleExists = false;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * @brief StyleDialog::_selAdd
+ * @param row
+ * This routine is called when an object is added to a selector by clicking on
+ * '+' in front of the row with selector's label.
+ */
+void StyleDialog::_selAdd(Gtk::TreeModel::Row row)
+{
+ Glib::ustring selectorName;
+ Gtk::TreeModel::Row childrow;
+ Inkscape::Selection* selection = _desktop->getSelection();
+ std::vector<SPObject *> sel = std::vector<SPObject *>
+ (selection->objects().begin(), selection->objects().end());
+ for (auto& obj: selection->objects()) {
+ if (*row) {
+ if (_selectorVec.size() != 0) {
+ childrow = *(_store->append(row->children()));
+ childrow[_mColumns._selectorLabel] = "#" +
+ std::string(obj->getId());
+ childrow[_mColumns._colAddRemove] = false;
+ childrow[_mColumns._colObj] = sel;
+ Glib::ustring key = row[_mColumns._selectorLabel];
+ if (key[0] == '.') {
+ if (!obj->getRepr()->attribute("class")) {
+ obj->setAttribute("class", key.erase(0,1));
+ }
+ else {
+ if (obj->getRepr()->attribute("class") != key
+ .erase(0,1)) {
+ obj->setAttribute("class", std::string
+ (obj->getRepr()->
+ attribute("class"))
+ + " " + key
+ .erase(0,1));
+ }
+ }
+ }
+ }
+ selectorName = row[_mColumns._selectorLabel];
+ }
+
+ /**
+ * If the object's parent row is a class selector, then
+ * there are no changes in style element except the class
+ * attribute is updated. For the id selector cases, XML
+ * content's style element is updated.
+ */
+ REMOVE_SPACES(selectorName);
+ std::vector<InkSelector>::iterator it;
+ for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) {
+ std::string sel, key, value;
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel];
+ std::string matchSelector = selectedRowLabel;
+ REMOVE_SPACES(matchSelector);
+ if (key == matchSelector) {
+ REMOVE_SPACES((*it)._selector);
+ if (selectorName[0] == '#') {
+ if ("#" + std::string(obj->getId()) != selectorName) {
+ inkSelector._selector = (*it)._selector;
+ inkSelector._selector.append(", #" + std::string(obj->getId()));
+ inkSelector._xmlContent = inkSelector._selector + "{" + value + "}\n";
+ row[_mColumns._selectorLabel] = selectorName + ", " +
+ childrow[_mColumns._selectorLabel];
+ }
+ }
+ else if (selectorName[0] == '.') {
+ inkSelector._xmlContent = (*it)._selector + "{" + value + "}\n";
+ }
+
+ it = _selectorVec.erase(it);
+ it = _selectorVec.insert(it, inkSelector);
+ }
+ }
+ }
+ if (_styleElementNode()) {
+ _styleChild = _styleElementNode();
+ _updateStyleContent();
+ }
+ else if (_styleExists && !_newDrawing) {
+ _updateStyleContent();
+ }
+}
+
+/**
+ * @brief StyleDialog::_selectObjects
+ * @param event
+ * This function detects single or double click on a selector in any row. Single
+ * click on a selector selects the matching objects. A double click on any
+ * selector selects the matching objects as well as will open CSS dialog. It
+ * calls _selectObjects to add objects to selection.
+ */
+void StyleDialog::_buttonEventsSelectObjs(GdkEventButton* event )
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+ int x = static_cast<int>(event->x);
+ int y = static_cast<int>(event->y);
+ _selectObjects(x, y);
+ }
+ else if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
+ int x = static_cast<int>(event->x);
+ int y = static_cast<int>(event->y);
+ _selectObjects(x, y);
+
+ //Open CSS dialog here.
+ if (!_cssPane->get_visible()) {
+ _paned.pack2(*_cssPane, Gtk::SHRINK);
+ _cssPane->show_all();
+ }
+
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ std::string sel, key, value;
+ std::vector<InkSelector>::iterator it;
+ for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) {
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel];
+ std::string matchSelector = selectedRowLabel;
+ REMOVE_SPACES(matchSelector);
+
+ if (key == matchSelector) {
+ _cssPane->_store->clear();
+ std::stringstream ss(value);
+ std::string token;
+ std::size_t found = value.find(";");
+ if (found!=std::string::npos) {
+ while(std::getline(ss, token, ';')) {
+ REMOVE_SPACES(token);
+ if (!token.empty()) {
+ _cssPane->_propRow = *(_cssPane->_store->append());
+ _cssPane->_propRow[_cssPane->_cssColumns._colUnsetProp] = false;
+ _cssPane->_propRow[_cssPane->_cssColumns._propertyLabel] = token;
+ _cssPane->_propCol->add_attribute(_cssPane->_textRenderer
+ ->property_text(),
+ _cssPane->_cssColumns
+ ._propertyLabel);
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ _cssPane->_store->clear();
+ _cssPane->hide();
+ }
+
+ _cssPane->_textRenderer->signal_edited().connect(sigc::mem_fun(*this,
+ &StyleDialog::
+ _handleEdited));
+ _cssPane->_treeView.signal_button_press_event().connect(sigc::mem_fun
+ (*this, &StyleDialog::
+ _delProperty),
+ false);
+ }
+}
+
+/**
+ * @brief StyleDialog::_selChanged
+ * When no row in _treeView of Style Dialog is selected, the _cssPane is hidden.
+ */
+void StyleDialog::_selChanged() {
+ if (_treeView.get_selection()->count_selected_rows() == 0) {
+ _cssPane->hide();
+ }
+}
+
+/**
+ * @brief StyleDialog::_handleEdited
+ * @param path
+ * @param new_text
+ * This function edits CSS properties of the selector chosen. new_text is used
+ * to update the property in XML repr. The value from selected selector is
+ * obtained and modified as per value of new_text. If a new property is added,
+ * value is appended with new_text. Later _updateStyleContent() is called to
+ * update XML repr and hence changes are reflected in the drawing too.
+ */
+void StyleDialog::_handleEdited(const Glib::ustring& path, const Glib::ustring& new_text)
+{
+ Gtk::TreeModel::iterator iterCss = _cssPane->_treeView.get_model()->get_iter(path);
+ if (iterCss) {
+ Gtk::TreeModel::Row row = *iterCss;
+ row[_cssPane->_cssColumns._propertyLabel] = new_text;
+ _cssPane->_editedProp = new_text;
+ }
+
+ // Selected selector row is obtained here to get corresponding key and value.
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ std::string sel, key, value;
+ std::vector<InkSelector>::iterator it;
+ for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) {
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel];
+ std::string matchSelector = selectedRowLabel;
+ REMOVE_SPACES(matchSelector);
+
+ if (key == matchSelector) {
+ /** If a new property is added, existing value is appended with new
+ * property, else replacements in value are done in the 'else' block.
+ */
+ if (_cssPane->_newProperty) {
+ if (!new_text.empty()) {
+ value.append((new_text + ";").c_str());
+ _cssPane->_propCol->add_attribute(_cssPane->_textRenderer
+ ->property_text(),
+ _cssPane->_cssColumns
+ ._propertyLabel);
+ _cssPane->_newProperty = false;
+ }
+ }
+ else {
+ std::stringstream ss(value);
+ std::string token, editedToken;
+ std::size_t found = value.find(";");
+ if (found!=std::string::npos) {
+ while(std::getline(ss, token, ';')) {
+ REMOVE_SPACES(token);
+ if (!token.empty()) {
+ if (token.substr(0, token.find(":")) == _cssPane
+ ->_editedProp.substr(0, _cssPane->_editedProp
+ .find(":"))) {
+ editedToken = _cssPane->_editedProp;
+ size_t startPos = value.find(token);
+ value.replace(startPos, token.length(), editedToken);
+ }
+ }
+ }
+ }
+ }
+ value.erase(std::remove(value.begin(), value.end(), '\n'), value.end());
+ (*it)._xmlContent = key + "{" + value + "}\n";
+ _updateStyleContent();
+ }
+ }
+ }
+}
+
+/**
+ * @brief StyleDialog::_delProperty
+ * @param event
+ * @return
+ * This function deletes property when '-' in front of property in CSS panel is
+ * clicked. The property row is deleted from CSS panel and XML repr is updated.
+ * toDelProperty is the property to be deleted which is looked in 'value' and is
+ * erased from 'value'.
+ */
+bool StyleDialog::_delProperty(GdkEventButton *event)
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+ Gtk::TreeViewColumn *col = 0;
+ Gtk::TreeModel::Path path;
+ int x = static_cast<int>(event->x);
+ int y = static_cast<int>(event->y);
+ int x2 = 0;
+ int y2 = 0;
+ Gtk::TreeModel::Row cssRow;
+ Glib::ustring toDelProperty;
+ if (_cssPane->_treeView.get_path_at_pos(x, y, path, col, x2, y2)) {
+ if (col == _cssPane->_treeView.get_column(0)) {
+ Gtk::TreeModel::iterator cssIter = _cssPane->_treeView.get_selection()
+ ->get_selected();
+ if (cssIter) {
+ cssRow = *cssIter;
+ toDelProperty = cssRow[_cssPane->_cssColumns._propertyLabel];
+ }
+
+ Gtk::TreeModel::iterator iter = _treeView.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ std::string sel, key, value;
+ std::vector<InkSelector>::iterator it;
+ for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) {
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel];
+ std::string matchSelector = selectedRowLabel;
+ REMOVE_SPACES(matchSelector);
+
+ if (key == matchSelector) {
+ std::size_t found = value.find(toDelProperty);
+ if (found!=std::string::npos) {
+ if (!toDelProperty.empty()) {
+ value.erase(found, toDelProperty.length()+1);
+ (*it)._xmlContent = key + "{" + value + "}\n";
+ _updateStyleContent();
+ _cssPane->_store->erase(cssRow);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * @brief StyleDialog::_selectObjects
+ * @param eventX
+ * @param eventY
+ * This function selects objects in the drawing corresponding to the selector
+ * selected in the treeview.
+ */
+void StyleDialog::_selectObjects(int eventX, int eventY)
+{
+ _desktop->selection->clear();
+ Gtk::TreeViewColumn *col = _treeView.get_column(1);
+ Gtk::TreeModel::Path path;
+ int x = eventX;
+ int y = eventY;
+ int x2 = 0;
+ int y2 = 0;
+ if (_treeView.get_path_at_pos(x, y, path, col, x2, y2)) {
+ if (col == _treeView.get_column(1)) {
+ Gtk::TreeModel::iterator iter = _store->get_iter(path);
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ Gtk::TreeModel::Children children = row.children();
+ std::vector<SPObject *> objVec = row[_mColumns._colObj];
+ for (unsigned i = 0; i < objVec.size(); ++i) {
+ SPObject *obj = objVec[i];
+ _desktop->selection->add(obj);
+ }
+ if (children) {
+ _checkAllChildren(children);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * @brief StyleDialog::_checkAllChildren
+ * @param children
+ * This function iterates children of the row selected in treeview and selects
+ * the objects corresponding to any selector in child rows.
+ */
+void StyleDialog::_checkAllChildren(Gtk::TreeModel::Children& children)
+{
+ for (Gtk::TreeModel::Children::iterator iter = children.begin();
+ iter!= children.end(); ++iter) {
+ Gtk::TreeModel::Row childrow = *iter;
+ std::vector<SPObject *> objVec = childrow[_mColumns._colObj];
+ for (unsigned i = 0; i < objVec.size(); ++i) {
+ SPObject *obj = objVec[i];
+ _desktop->selection->add(obj);
+ }
+ }
+}
+
+/**
+ * @brief StyleDialog::_selectRow
+ * This function selects the rows in treeview corresponding to an object selected
+ * in the drawing.
+ */
+void StyleDialog::_selectRow(Selection */*sel*/)
+{
+ SPObject *obj = NULL;
+ if (!_desktop->selection->isEmpty()) {
+ Inkscape::Selection* selection = _desktop->getSelection();
+ std::vector<SPObject*> selected = std::vector<SPObject *>
+ (selection->objects().begin(), selection->objects().end());
+ obj = selected.back();
+ }
+
+ /**
+ * If obj has some SPObject, then it is added to desktop's selection. If a
+ * row in treeview has children, those rows are checked too against selected
+ * object's id. If an object which is not present in any selector is selected,
+ * the treeview's selections are unselected.
+ */
+ if (obj != NULL) {
+ Gtk::TreeModel::Children children = _store->children();
+ for(Gtk::TreeModel::Children::iterator iter = children.begin();
+ iter != children.end(); ++iter) {
+ Gtk::TreeModel::Row row = *iter;
+ std::vector<SPObject *> objVec = row[_mColumns._colObj];
+ for (unsigned i = 0; i < objVec.size(); ++i) {
+ if (obj->getId() == objVec[i]->getId()) {
+ _treeView.get_selection()->select(row);
+ }
+ }
+ }
+ }
+ else {
+ _treeView.get_selection()->unselect_all();
+ }
+}
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
diff --git a/src/ui/dialog/styledialog.h b/src/ui/dialog/styledialog.h
new file mode 100644
index 000000000..b03a1d2e1
--- /dev/null
+++ b/src/ui/dialog/styledialog.h
@@ -0,0 +1,117 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef STYLEDIALOG_H
+#define STYLEDIALOG_H
+
+#include <ui/widget/panel.h>
+#include <gtkmm/treeview.h>
+#include <gtkmm/treestore.h>
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/dialog.h>
+#include <gtkmm/treeselection.h>
+#include <gtkmm/paned.h>
+
+#include "desktop.h"
+#include "document.h"
+#include "ui/dialog/cssdialog.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * @brief The StyleDialog class
+ * A list of CSS selectors will show up in this dialog. This dialog allows to
+ * add and delete selectors. Objects can be added to and removed from the selectors
+ * in the dialog. Besides, selection of any selector row selects the matching
+ * objects in the drawing and vice-versa.
+ */
+typedef std::pair<std::pair<std::string, std::vector<SPObject *> >, std::string>
+_selectorVecType;
+
+class StyleDialog : public UI::Widget::Panel
+{
+public:
+ StyleDialog();
+ ~StyleDialog();
+
+ static StyleDialog &getInstance() { return *new StyleDialog(); }
+ void setDesktop(SPDesktop* desktop);
+
+private:
+ void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip);
+ std::string _setClassAttribute(std::vector<SPObject *>);
+
+ class InkSelector {
+ public:
+ std::string _selector;
+ std::vector<SPObject *> _matchingObjs;
+ std::string _xmlContent;
+ };
+
+ InkSelector inkSelector;
+ std::vector<InkSelector> _selectorVec;
+ std::vector<InkSelector> _getSelectorVec();
+ std::string _populateTree(std::vector<InkSelector>);
+ bool _handleButtonEvent(GdkEventButton *event);
+ void _buttonEventsSelectObjs(GdkEventButton *event);
+ void _selectObjects(int, int);
+ void _checkAllChildren(Gtk::TreeModel::Children& children);
+ Inkscape::XML::Node *_styleElementNode();
+ void _updateStyleContent();
+ void _selectRow(Selection *);
+
+ class ModelColumns : public Gtk::TreeModel::ColumnRecord
+ {
+ public:
+ ModelColumns()
+ { add(_selectorLabel); add(_colAddRemove); add(_colObj); }
+ Gtk::TreeModelColumn<Glib::ustring> _selectorLabel;
+ Gtk::TreeModelColumn<bool> _colAddRemove;
+ Gtk::TreeModelColumn<std::vector<SPObject *> > _colObj;
+ };
+
+ SPDesktop* _desktop;
+ SPDesktop* _targetDesktop;
+ ModelColumns _mColumns;
+ Gtk::VPaned _paned;
+ Gtk::VBox _mainBox;
+ Gtk::HBox _buttonBox;
+ Gtk::TreeView _treeView;
+ Glib::RefPtr<Gtk::TreeStore> _store;
+ Gtk::ScrolledWindow _scrolledWindow;
+ Gtk::Button* del;
+ Gtk::Button* create;
+ SPDocument* _document;
+ bool _styleExists;
+ Inkscape::XML::Node *_styleChild;
+ std::string _selectorName;
+ std::string _selectorValue;
+ bool _newDrawing;
+ CssDialog *_cssPane;
+ void _selAdd(Gtk::TreeModel::Row row);
+
+ // Signal handlers
+ void _addSelector();
+ void _delSelector();
+ void _selChanged();
+
+ // Signal handler for CssDialog
+ void _handleEdited(const Glib::ustring& path, const Glib::ustring& new_text);
+ bool _delProperty(GdkEventButton *event);
+};
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+#endif // STYLEDIALOG_H
diff --git a/src/verbs.cpp b/src/verbs.cpp
index aeb742105..fce823408 100644
--- a/src/verbs.cpp
+++ b/src/verbs.cpp
@@ -2157,6 +2157,12 @@ void DialogVerb::perform(SPAction *action, void *data)
case SP_VERB_DIALOG_PRINT_COLORS_PREVIEW:
dt->_dlg_mgr->showDialog("PrintColorsPreviewDialog");
break;
+ case SP_VERB_DIALOG_STYLE:
+ dt->_dlg_mgr->showDialog("StyleDialog");
+ break;
+ case SP_VERB_DIALOG_CSS:
+ dt->_dlg_mgr->showDialog("CssDialog");
+ break;
default:
break;
@@ -3005,6 +3011,10 @@ Verb *Verb::_base_verbs[] = {
N_("View Objects"), INKSCAPE_ICON("dialog-layers")),
new DialogVerb(SP_VERB_DIALOG_TAGS, "DialogTags", N_("Selection se_ts..."),
N_("View Tags"), INKSCAPE_ICON("edit-select-all-layers")),
+ new DialogVerb(SP_VERB_DIALOG_STYLE, "DialogStyle", N_("Style Dialog..."),
+ N_("View Style Dialog"), NULL),
+ new DialogVerb(SP_VERB_DIALOG_CSS, "DialogCss", N_("Css Dialog..."),
+ N_("View Css Dialog"), NULL),
new DialogVerb(SP_VERB_DIALOG_LIVE_PATH_EFFECT, "DialogLivePathEffect", N_("Path E_ffects ..."),
N_("Manage, edit, and apply path effects"), INKSCAPE_ICON("dialog-path-effects")),
new DialogVerb(SP_VERB_DIALOG_FILTER_EFFECTS, "DialogFilterEffects", N_("Filter _Editor..."),
diff --git a/src/verbs.h b/src/verbs.h
index 76a7d19a6..21e9b5547 100644
--- a/src/verbs.h
+++ b/src/verbs.h
@@ -317,6 +317,8 @@ enum {
SP_VERB_DIALOG_LAYERS,
SP_VERB_DIALOG_OBJECTS,
SP_VERB_DIALOG_TAGS,
+ SP_VERB_DIALOG_STYLE,
+ SP_VERB_DIALOG_CSS,
SP_VERB_DIALOG_LIVE_PATH_EFFECT,
SP_VERB_DIALOG_FILTER_EFFECTS,
SP_VERB_DIALOG_SVG_FONTS,