diff options
| author | Tavmjong Bah <tavmjong@free.fr> | 2017-02-04 10:22:35 +0000 |
|---|---|---|
| committer | tavmjong-free <tavmjong@free.fr> | 2017-02-04 10:22:35 +0000 |
| commit | 2a84c87bd1c6850a9bb9ad5b39f82d1dacb4b94d (patch) | |
| tree | 96d622a4e1cb20e4e86b8a7fc38dd8d72cc9356c /src | |
| parent | Disable rotate guides in doc rotation (diff) | |
| parent | Add some more comments to improve code understanding (diff) | |
| download | inkscape-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.cpp | 54 | ||||
| -rw-r--r-- | src/document.h | 3 | ||||
| -rw-r--r-- | src/menus-skeleton.h | 1 | ||||
| -rw-r--r-- | src/path-chemistry.cpp | 6 | ||||
| -rw-r--r-- | src/sp-flowtext.cpp | 2 | ||||
| -rw-r--r-- | src/sp-object.cpp | 4 | ||||
| -rw-r--r-- | src/sp-style-elem.cpp | 25 | ||||
| -rw-r--r-- | src/style-internal.cpp | 200 | ||||
| -rw-r--r-- | src/style-internal.h | 36 | ||||
| -rw-r--r-- | src/style.cpp | 160 | ||||
| -rw-r--r-- | src/style.h | 7 | ||||
| -rw-r--r-- | src/ui/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/ui/dialog/cssdialog.cpp | 125 | ||||
| -rw-r--r-- | src/ui/dialog/cssdialog.h | 75 | ||||
| -rw-r--r-- | src/ui/dialog/dialog-manager.cpp | 6 | ||||
| -rw-r--r-- | src/ui/dialog/styledialog.cpp | 1116 | ||||
| -rw-r--r-- | src/ui/dialog/styledialog.h | 117 | ||||
| -rw-r--r-- | src/verbs.cpp | 10 | ||||
| -rw-r--r-- | src/verbs.h | 2 |
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, |
