summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorShlomi Fish <shlomif@shlomifish.org>2017-02-23 14:39:39 +0000
committerShlomi Fish <shlomif@shlomifish.org>2017-02-23 14:39:39 +0000
commitd82d8e6de61b5a3da73af7d7003b8281720c3dc1 (patch)
tree50a151920b32f95afd0fa0c04f175322657ebb18 /src
parentMerged. (diff)
parentDisplay style attribute properties when object row selected. Allow their dele... (diff)
downloadinkscape-d82d8e6de61b5a3da73af7d7003b8281720c3dc1.tar.gz
inkscape-d82d8e6de61b5a3da73af7d7003b8281720c3dc1.zip
Merged.
(bzr r15369.1.19)
Diffstat (limited to 'src')
-rw-r--r--src/2geom/CMakeLists.txt4
-rw-r--r--src/attribute-rel-svg.cpp23
-rw-r--r--src/attribute-rel-svg.h1
-rw-r--r--src/debug/log-display-config.cpp41
-rw-r--r--src/display/sp-canvas.cpp29
-rw-r--r--src/document.cpp53
-rw-r--r--src/document.h3
-rw-r--r--src/extension/extension.cpp36
-rw-r--r--src/extension/extension.h2
-rw-r--r--src/extension/implementation/script.cpp3
-rw-r--r--src/extension/internal/bitmap/imagemagick.cpp1
-rw-r--r--src/extension/internal/cdr-input.cpp8
-rw-r--r--src/extension/internal/filter/color.h2
-rw-r--r--src/extension/internal/filter/protrusions.h2
-rw-r--r--src/extension/internal/vsd-input.cpp8
-rw-r--r--src/extension/internal/wmf-inout.cpp10
-rw-r--r--src/extension/internal/wmf-print.cpp23
-rw-r--r--src/extension/internal/wpg-input.cpp7
-rw-r--r--src/extension/param/bool.cpp25
-rw-r--r--src/extension/param/bool.h11
-rw-r--r--src/extension/param/color.cpp20
-rw-r--r--src/extension/param/color.h11
-rw-r--r--src/extension/param/description.cpp66
-rw-r--r--src/extension/param/description.h7
-rw-r--r--src/extension/param/enum.cpp26
-rw-r--r--src/extension/param/enum.h12
-rw-r--r--src/extension/param/float.cpp39
-rw-r--r--src/extension/param/float.h21
-rw-r--r--src/extension/param/int.cpp39
-rw-r--r--src/extension/param/int.h20
-rw-r--r--src/extension/param/notebook.cpp42
-rw-r--r--src/extension/param/notebook.h11
-rw-r--r--src/extension/param/parameter.cpp62
-rw-r--r--src/extension/param/parameter.h22
-rw-r--r--src/extension/param/radiobutton.cpp34
-rw-r--r--src/extension/param/radiobutton.h3
-rw-r--r--src/extension/param/string.cpp25
-rw-r--r--src/extension/param/string.h13
-rw-r--r--src/extension/prefdialog.cpp14
-rw-r--r--src/file-update.cpp3
-rw-r--r--src/gradient-drag.cpp6
-rw-r--r--src/gradient-drag.h2
-rw-r--r--src/id-clash.cpp4
-rw-r--r--src/knot.cpp4
-rw-r--r--src/knot.h1
-rw-r--r--src/libcola/gradient_projection.cpp4
-rw-r--r--src/libcroco/cr-parser.c2
-rw-r--r--src/libcroco/cr-parser.h2
-rw-r--r--src/libcroco/cr-selector.c25
-rw-r--r--src/live_effects/effect.cpp3
-rw-r--r--src/live_effects/lpe-clone-original.cpp4
-rw-r--r--src/live_effects/lpe-copy_rotate.cpp26
-rw-r--r--src/live_effects/lpe-fill-between-many.cpp37
-rw-r--r--src/live_effects/lpe-fill-between-many.h7
-rw-r--r--src/live_effects/lpe-fill-between-strokes.cpp63
-rw-r--r--src/live_effects/lpe-fill-between-strokes.h7
-rw-r--r--src/live_effects/lpe-measure-line.cpp12
-rw-r--r--src/live_effects/lpe-mirror_symmetry.cpp162
-rw-r--r--src/live_effects/parameter/originalpath.cpp2
-rw-r--r--src/live_effects/parameter/originalpatharray.cpp2
-rw-r--r--src/main.cpp18
-rw-r--r--src/selection-chemistry.cpp2
-rw-r--r--src/sp-namedview.cpp64
-rw-r--r--src/sp-object.cpp5
-rw-r--r--src/sp-path.cpp7
-rw-r--r--src/splivarot.cpp62
-rw-r--r--src/style-internal.h6
-rw-r--r--src/ui/dialog/aboutbox.cpp2
-rw-r--r--src/ui/dialog/cssdialog.cpp59
-rw-r--r--src/ui/dialog/cssdialog.h35
-rw-r--r--src/ui/dialog/styledialog.cpp1681
-rw-r--r--src/ui/dialog/styledialog.h139
-rw-r--r--src/ui/dialog/symbols.cpp52
-rw-r--r--src/widgets/text-toolbar.cpp2
74 files changed, 1952 insertions, 1339 deletions
diff --git a/src/2geom/CMakeLists.txt b/src/2geom/CMakeLists.txt
index aa51d51bd..d2c1ea50e 100644
--- a/src/2geom/CMakeLists.txt
+++ b/src/2geom/CMakeLists.txt
@@ -1,3 +1,7 @@
+# Override error flag just for this folder
+if (CMAKE_BUILD_TYPE MATCHES Strict)
+ set(CMAKE_CXX_FLAGS_STRICT "${CMAKE_CXX_FLAGS_STRICT} -Wno-error=deprecated-declarations")
+endif()
set(2geom_SRC
affine.cpp
diff --git a/src/attribute-rel-svg.cpp b/src/attribute-rel-svg.cpp
index afa578061..1f4bee1b3 100644
--- a/src/attribute-rel-svg.cpp
+++ b/src/attribute-rel-svg.cpp
@@ -28,6 +28,27 @@ SPAttributeRelSVG * SPAttributeRelSVG::instance = NULL;
bool SPAttributeRelSVG::foundFile = false;
/*
+ * This function returns true if element is an SVG element.
+ */
+bool SPAttributeRelSVG::isSVGElement(Glib::ustring element)
+{
+ if (SPAttributeRelSVG::instance == NULL) {
+ SPAttributeRelSVG::instance = new SPAttributeRelSVG();
+ }
+
+ // Always valid if data file not found!
+ if( !foundFile ) return true;
+
+ // Strip off "svg:" from the element's name
+ Glib::ustring temp = element;
+ if ( temp.find("svg:") != std::string::npos ) {
+ temp.erase( temp.find("svg:"), 4 );
+ }
+
+ return (SPAttributeRelSVG::instance->attributesOfElements.count(temp) > 0);
+}
+
+/*
* This functions checks whether an element -> attribute pair is allowed or not
*/
bool SPAttributeRelSVG::findIfValid(Glib::ustring attribute, Glib::ustring element)
@@ -39,7 +60,7 @@ bool SPAttributeRelSVG::findIfValid(Glib::ustring attribute, Glib::ustring eleme
// Always valid if data file not found!
if( !foundFile ) return true;
- // Strip of "svg:" from the element's name
+ // Strip off "svg:" from the element's name
Glib::ustring temp = element;
if ( temp.find("svg:") != std::string::npos ) {
temp.erase( temp.find("svg:"), 4 );
diff --git a/src/attribute-rel-svg.h b/src/attribute-rel-svg.h
index 74c6d3b60..c0a9cd215 100644
--- a/src/attribute-rel-svg.h
+++ b/src/attribute-rel-svg.h
@@ -22,6 +22,7 @@ typedef std::map<Glib::ustring, std::set<Glib::ustring> > hashList;
*/
class SPAttributeRelSVG {
public:
+ static bool isSVGElement(Glib::ustring element);
static bool findIfValid(Glib::ustring attribute, Glib::ustring element);
private:
diff --git a/src/debug/log-display-config.cpp b/src/debug/log-display-config.cpp
index ecc05b7b7..1f436a708 100644
--- a/src/debug/log-display-config.cpp
+++ b/src/debug/log-display-config.cpp
@@ -27,9 +27,20 @@ typedef SimpleEvent<Event::CONFIGURATION> ConfigurationEvent;
class Monitor : public ConfigurationEvent {
public:
- Monitor(GdkScreen *screen, gint monitor) : ConfigurationEvent("monitor") {
+#if GTK_CHECK_VERSION(3,22,0)
+ Monitor(GdkMonitor *monitor)
+#else
+ Monitor(GdkScreen *screen, gint monitor)
+#endif
+ : ConfigurationEvent("monitor") {
GdkRectangle area;
+
+#if GTK_CHECK_VERSION(3,22,0)
+ gdk_monitor_get_geometry(monitor, &area);
+#else
gdk_screen_get_monitor_geometry(screen, monitor, &area);
+#endif
+
_addProperty("x", area.x);
_addProperty("y", area.y);
_addProperty("width", area.width);
@@ -37,10 +48,13 @@ public:
}
};
+#if !GTK_CHECK_VERSION(3,22,0)
+// We don't need this in newer Gtk+ versions as GdkMonitor information is now
+// returned directly from GdkDisplay rather than needing GdkScreen first
class Screen : public ConfigurationEvent {
public:
Screen(GdkScreen *s) : ConfigurationEvent("screen"), screen(s) {
- _addProperty("width", gdk_screen_get_width(screen));
+ _addProperty("width", gdk_screen_get_width(screen));
_addProperty("height", gdk_screen_get_height(screen));
}
void generateChildEvents() const {
@@ -53,21 +67,28 @@ public:
private:
GdkScreen *screen;
};
+#endif
class Display : public ConfigurationEvent {
public:
Display() : ConfigurationEvent("display") {}
void generateChildEvents() const {
GdkDisplay *display=gdk_display_get_default();
-#if GTK_CHECK_VERSION(3,10,0)
- GdkScreen *screen = gdk_display_get_screen(display, 0);
- Logger::write<Screen>(screen);
-#else
- gint n_screens = gdk_display_get_n_screens(display);
- for ( gint i = 0 ; i < n_screens ; i++ ) {
- GdkScreen *screen = gdk_display_get_screen(display, i);
- Logger::write<Screen>(screen);
+
+#if GTK_CHECK_VERSION(3,22,0)
+ gint const n_monitors = gdk_display_get_n_monitors(display);
+
+ // Loop through all monitors and log their details
+ for (gint i_monitor = 0; i_monitor < n_monitors; ++i_monitor) {
+ GdkMonitor *monitor = gdk_display_get_monitor(display, i_monitor);
+ Logger::write<Monitor>(monitor);
}
+#else
+ // We used to find the number of screens, and log info for
+ // each of them. However, the number of screens is always
+ // one in Gtk+ 3
+ GdkScreen *screen = gdk_display_get_default_screen(display);
+ Logger::write<Screen>(screen);
#endif
}
};
diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp
index 68eae0a65..623e417c6 100644
--- a/src/display/sp-canvas.cpp
+++ b/src/display/sp-canvas.cpp
@@ -48,6 +48,10 @@
#include <iomanip>
#include <glibmm/i18n.h>
+#if GTK_CHECK_VERSION(3,20,0)
+# include <gdkmm/seat.h>
+#endif
+
using Inkscape::Debug::GdkEventLatencyTracker;
// gtk_check_version returns non-NULL on failure
@@ -72,8 +76,14 @@ struct SPCanvasGroupClass {
static void ungrab_default_client_pointer(guint32 const time = GDK_CURRENT_TIME)
{
auto const display = Gdk::Display::get_default();
+
+#if GTK_CHECK_VERSION(3,20,0)
+ auto const seat = display->get_default_seat();
+ auto const device = seat->get_pointer();
+#else
auto const dm = display->get_device_manager();
auto const device = dm->get_client_pointer();
+#endif
device->ungrab(time);
}
@@ -625,9 +635,16 @@ int sp_canvas_item_grab(SPCanvasItem *item, guint event_mask, GdkCursor *cursor,
// fixme: Top hack (Lauris)
// fixme: If we add key masks to event mask, Gdk will abort (Lauris)
- // fixme: But Canvas actualle does get key events, so all we need is routing these here
- auto dm = gdk_display_get_device_manager(gdk_display_get_default());
+ // fixme: But Canvas actually does get key events, so all we need is routing these here
+ auto display = gdk_display_get_default();
+#if GTK_CHECK_VERSION(3,20,0)
+ auto seat = gdk_display_get_default_seat(display);
+ auto device = gdk_seat_get_pointer(seat);
+#else
+ auto dm = gdk_display_get_device_manager(display);
auto device = gdk_device_manager_get_client_pointer(dm);
+#endif
+
gdk_device_grab(device,
getWindow(item->canvas),
GDK_OWNERSHIP_NONE,
@@ -1034,7 +1051,7 @@ void SPCanvas::handle_realize(GtkWidget *widget)
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
- attributes.visual = gdk_visual_get_system();
+ attributes.visual = gdk_screen_get_system_visual(gdk_screen_get_default());
attributes.event_mask = (gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK |
@@ -1694,8 +1711,14 @@ bool SPCanvas::paintRect(int xx0, int yy0, int xx1, int yy1)
gint x, y;
auto const display = Gdk::Display::get_default();
+
+#if GTK_CHECK_VERSION(3,20,0)
+ auto const seat = display->get_default_seat();
+ auto const device = seat->get_pointer();
+#else
auto const dm = display->get_device_manager();
auto const device = dm->get_client_pointer();
+#endif
gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(this)),
device->gobj(),
diff --git a/src/document.cpp b/src/document.cpp
index b69508751..c7115f906 100644
--- a/src/document.cpp
+++ b/src/document.cpp
@@ -62,6 +62,11 @@
#include "sp-symbol.h"
#include "xml/rebase-hrefs.h"
+#include "libcroco/cr-sel-eng.h"
+#include "libcroco/cr-selector.h"
+#include "libcroco/cr-parser.h"
+#include "src/xml/croco-node-iface.h"
+
using Inkscape::DocumentUndo;
using Inkscape::Util::unit_table;
@@ -1119,6 +1124,50 @@ std::vector<SPObject *> SPDocument::getObjectsByElement(Glib::ustring const &ele
return objects;
}
+void _getObjectsBySelectorRecursive(SPObject *parent,
+ CRSelEng *sel_eng, CRSimpleSel *simple_sel,
+ std::vector<SPObject *> &objects)
+{
+ if (parent) {
+ gboolean result = false;
+ cr_sel_eng_matches_node( sel_eng, simple_sel, parent->getRepr(), &result );
+ if (result) {
+ objects.push_back(parent);
+ }
+
+ // Check children
+ for (auto& child : parent->children) {
+ _getObjectsBySelectorRecursive(&child, sel_eng, simple_sel, objects);
+ }
+ }
+}
+
+std::vector<SPObject *> SPDocument::getObjectsBySelector(Glib::ustring const &selector) const
+{
+ // std::cout << "\nSPDocument::getObjectsBySelector: " << selector << std::endl;
+
+ std::vector<SPObject *> objects;
+ g_return_val_if_fail(!selector.empty(), objects);
+
+ static CRSelEng *sel_eng = NULL;
+ if (!sel_eng) {
+ sel_eng = cr_sel_eng_new();
+ cr_sel_eng_set_node_iface(sel_eng, &Inkscape::XML::croco_node_iface);
+ }
+
+ Glib::ustring my_selector = selector + " {"; // Parsing fails sometimes without '{'. Fix me
+ CRSelector *cr_selector = cr_selector_parse_from_buf ((guchar*)my_selector.c_str(), CR_UTF_8);
+ // char * cr_string = (char*)cr_selector_to_string( cr_selector );
+ // std::cout << " selector: |" << (cr_string?cr_string:"Empty") << "|" << std::endl;
+ CRSelector const *cur = NULL;
+ for (cur = cr_selector; cur; cur = cur->next) {
+ if (cur->simple_sel ) {
+ _getObjectsBySelectorRecursive(root, sel_eng, cur->simple_sel, objects);
+ }
+ }
+ return objects;
+}
+
void SPDocument::bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object)
{
if (object) {
@@ -1474,7 +1523,9 @@ static SPItem *find_group_at_point(unsigned int dkey, SPGroup *group, Geom::Poin
if (SP_IS_GROUP(&o) && SP_GROUP(&o)->effectiveLayerMode(dkey) != SPGroup::LAYER ) {
SPItem *child = SP_ITEM(&o);
Inkscape::DrawingItem *arenaitem = child->get_arenaitem(dkey);
- arenaitem->drawing().update();
+ if (arenaitem) {
+ arenaitem->drawing().update();
+ }
// seen remembers the last (topmost) of groups pickable at this point
if (arenaitem && arenaitem->pick(p, delta, 1) != NULL) {
diff --git a/src/document.h b/src/document.h
index 142eb5000..db50f1717 100644
--- a/src/document.h
+++ b/src/document.h
@@ -199,7 +199,8 @@ public:
std::vector<SPObject *> getObjectsByClass(Glib::ustring const &klass) const;
std::vector<SPObject *> getObjectsByElement(Glib::ustring const &element) const;
-
+ std::vector<SPObject *> getObjectsBySelector(Glib::ustring const &selector) const;
+
void bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object);
SPObject *getObjectByRepr(Inkscape::XML::Node *repr) const;
diff --git a/src/extension/extension.cpp b/src/extension/extension.cpp
index c6dee1b70..8b5a687ef 100644
--- a/src/extension/extension.cpp
+++ b/src/extension/extension.cpp
@@ -1,6 +1,6 @@
/** \file
*
- * Inkscape::Extension::Extension:
+ * Inkscape::Extension::Extension:
* the ability to have features that are more modular so that they
* can be added and removed easily. This is the basis for defining
* those actions.
@@ -144,15 +144,15 @@ Extension::~Extension (void)
delete timer;
timer = NULL;
/** \todo Need to do parameters here */
-
- // delete parameters:
+
+ // delete parameters:
for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
Parameter * param = reinterpret_cast<Parameter *>(list->data);
delete param;
}
g_slist_free(parameters);
-
-
+
+
for (unsigned int i = 0 ; i < _deps.size(); i++) {
delete _deps[i];
}
@@ -330,7 +330,7 @@ Extension::get_repr (void)
}
/**
- \return bool
+ \return bool
\brief Whether this extension should hide the "working, please wait" dialog
*/
bool
@@ -444,7 +444,7 @@ Extension::get_param_enum (const gchar * name, const SPDocument * doc, const Ink
/**
* This is useful to find out, if a given string \c value is selectable in a ComboBox named \cname.
- *
+ *
* @param name The name of the enum parameter to get.
* @param doc The document to look in for document specific parameters.
* @param node The node to look in for a specific parameter.
@@ -692,9 +692,10 @@ public:
* @param widg Widget to add.
* @param tooltip Tooltip for the widget.
*/
- void addWidget(Gtk::Widget *widg, gchar const *tooltip) {
+ void addWidget(Gtk::Widget *widg, gchar const *tooltip, int indent) {
if (widg) {
- this->pack_start(*widg, false, false, 2);
+ widg->set_margin_left(indent * Parameter::GUI_INDENTATION);
+ this->pack_start(*widg, false, false, 0);
if (tooltip) {
widg->set_tooltip_text(_(tooltip));
} else {
@@ -713,7 +714,7 @@ public:
a Gtk::VBox, which is then returned to the calling function.
If there are no visible parameters, this function just returns NULL.
- If all parameters are gui_visible = false NULL is returned as well.
+ If all parameters are gui_visible = false NULL is returned as well.
*/
Gtk::Widget *
Extension::autogui (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
@@ -721,6 +722,8 @@ Extension::autogui (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<v
if (!_gui || param_visible_count() == 0) return NULL;
AutoGUI * agui = Gtk::manage(new AutoGUI());
+ agui->set_border_width(Parameter::GUI_BOX_MARGIN);
+ agui->set_spacing(Parameter::GUI_BOX_SPACING);
//go through the list of parameters to see if there are any non-hidden ones
for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
@@ -728,9 +731,10 @@ Extension::autogui (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<v
if (param->get_gui_hidden()) continue; //Ignore hidden parameters
Gtk::Widget * widg = param->get_widget(doc, node, changeSignal);
gchar const * tip = param->get_tooltip();
- agui->addWidget(widg, tip);
- }
-
+ int indent = param->get_indent();
+ agui->addWidget(widg, tip, indent);
+ }
+
agui->show();
return agui;
};
@@ -780,7 +784,7 @@ void Extension::add_val(Glib::ustring labelstr, Glib::ustring valuestr, Gtk::Gri
Gtk::Label * label;
Gtk::Label * value;
- (*row)++;
+ (*row)++;
label = Gtk::manage(new Gtk::Label(labelstr));
value = Gtk::manage(new Gtk::Label(valuestr));
@@ -824,13 +828,13 @@ Extension::get_params_widget(void)
return retval;
}
-unsigned int Extension::param_visible_count ( )
+unsigned int Extension::param_visible_count ( )
{
unsigned int _visible_count = 0;
for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
Parameter * param = reinterpret_cast<Parameter *>(list->data);
if (!param->get_gui_hidden()) _visible_count++;
- }
+ }
return _visible_count;
}
diff --git a/src/extension/extension.h b/src/extension/extension.h
index cd29e1636..bbd6d068b 100644
--- a/src/extension/extension.h
+++ b/src/extension/extension.h
@@ -176,7 +176,7 @@ public:
private:
void make_param (Inkscape::XML::Node * paramrepr);
-
+
/**
* This function looks through the linked list for a parameter
* structure with the name of the passed in name.
diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp
index d2319c2e0..b1058d415 100644
--- a/src/extension/implementation/script.cpp
+++ b/src/extension/implementation/script.cpp
@@ -963,6 +963,9 @@ void Script::checkStderr (const Glib::ustring &data,
warning.run();
+ delete textview;
+ delete scrollwindow;
+
return;
}
diff --git a/src/extension/internal/bitmap/imagemagick.cpp b/src/extension/internal/bitmap/imagemagick.cpp
index 472c2db91..cc5b3d1bc 100644
--- a/src/extension/internal/bitmap/imagemagick.cpp
+++ b/src/extension/internal/bitmap/imagemagick.cpp
@@ -213,6 +213,7 @@ ImageMagick::effect (Inkscape::Extension::Effect *module, Inkscape::UI::View::Vi
dc->_nodes[i]->setAttribute("xlink:href", dc->_caches[i], true);
dc->_nodes[i]->setAttribute("sodipodi:absref", NULL, true);
+ delete blob;
}
catch (Magick::Exception &error_) {
printf("Caught exception: %s \n", error_.what());
diff --git a/src/extension/internal/cdr-input.cpp b/src/extension/internal/cdr-input.cpp
index 0435f1396..dbe88c668 100644
--- a/src/extension/internal/cdr-input.cpp
+++ b/src/extension/internal/cdr-input.cpp
@@ -218,11 +218,13 @@ SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u
// RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows
// therefore attempt to convert uri to the system codepage
// even if this is not possible the alternate short (8.3) file name will be used if available
- uri = g_win32_locale_filename_from_utf8(uri);
+ gchar * converted_uri = g_win32_locale_filename_from_utf8(uri);
+ RVNGFileStream input(converted_uri);
+ g_free(converted_uri);
+ #else
+ RVNGFileStream input(uri);
#endif
- RVNGFileStream input(uri);
-
if (!libcdr::CDRDocument::isSupported(&input)) {
return NULL;
}
diff --git a/src/extension/internal/filter/color.h b/src/extension/internal/filter/color.h
index a6b777d20..6324dafa9 100644
--- a/src/extension/internal/filter/color.h
+++ b/src/extension/internal/filter/color.h
@@ -1480,7 +1480,7 @@ public:
static void init (void) {
Inkscape::Extension::build_from_mem(
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
- "<name>" N_("Quadritone fantasy") "</name>\n"
+ "<name>" N_("Quadritone Fantasy") "</name>\n"
"<id>org.inkscape.effect.filter.Quadritone</id>\n"
"<param name=\"dist\" _gui-text=\"" N_("Hue distribution (°)") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">280</param>\n"
"<param name=\"colors\" _gui-text=\"" N_("Colors") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">100</param>\n"
diff --git a/src/extension/internal/filter/protrusions.h b/src/extension/internal/filter/protrusions.h
index ad75d8896..d6fd315db 100644
--- a/src/extension/internal/filter/protrusions.h
+++ b/src/extension/internal/filter/protrusions.h
@@ -45,7 +45,7 @@ public:
static void init (void) {
Inkscape::Extension::build_from_mem(
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
- "<name>" N_("Snow crest") "</name>\n"
+ "<name>" N_("Snow Crest") "</name>\n"
"<id>org.inkscape.effect.filter.snow</id>\n"
"<param name=\"drift\" _gui-text=\"" N_("Drift Size") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"20.0\">3.5</param>\n"
"<effect>\n"
diff --git a/src/extension/internal/vsd-input.cpp b/src/extension/internal/vsd-input.cpp
index 78783aa2d..85698387a 100644
--- a/src/extension/internal/vsd-input.cpp
+++ b/src/extension/internal/vsd-input.cpp
@@ -220,11 +220,13 @@ SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u
// RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows
// therefore attempt to convert uri to the system codepage
// even if this is not possible the alternate short (8.3) file name will be used if available
- uri = g_win32_locale_filename_from_utf8(uri);
+ gchar * converted_uri = g_win32_locale_filename_from_utf8(uri);
+ RVNGFileStream input(converted_uri);
+ g_free(converted_uri);
+ #else
+ RVNGFileStream input(uri);
#endif
- RVNGFileStream input(uri);
-
if (!libvisio::VisioDocument::isSupported(&input)) {
return NULL;
}
diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp
index a79af5ec1..42ec2f2bb 100644
--- a/src/extension/internal/wmf-inout.cpp
+++ b/src/extension/internal/wmf-inout.cpp
@@ -1765,11 +1765,20 @@ std::cout << "BEFORE DRAW"
<< " test0 " << ( d->mask & U_DRAW_VISIBLE)
<< " test1 " << ( d->mask & U_DRAW_FORCE)
<< " test2 " << (wmr_mask & U_DRAW_ALTERS)
+ << " test2.5 " << ((d->mask & U_DRAW_NOFILL) != (wmr_mask & U_DRAW_NOFILL) )
<< " test3 " << (wmr_mask & U_DRAW_VISIBLE)
<< " test4 " << !(d->mask & U_DRAW_ONLYTO)
<< " test5 " << ((d->mask & U_DRAW_ONLYTO) && !(wmr_mask & U_DRAW_ONLYTO) )
<< std::endl;
*/
+ /* spurious moveto records should not affect the drawing. However, they set the NOFILL
+ bit and that messes up the logic about when to emit a path. So prune out any
+ stray moveto records. That is those which were never followed by a lineto.
+ */
+ if((d->mask & U_DRAW_NOFILL) && !(d->mask & U_DRAW_VISIBLE) &&
+ !(wmr_mask & U_DRAW_ONLYTO) && (wmr_mask & U_DRAW_VISIBLE)){
+ d->mask ^= U_DRAW_NOFILL;
+ }
if(
(wmr_mask != U_WMR_INVALID) && // next record is valid type
@@ -1777,6 +1786,7 @@ std::cout << "BEFORE DRAW"
(
(d->mask & U_DRAW_FORCE) || // This draw is forced by STROKE/FILL/STROKEANDFILL PATH
(wmr_mask & U_DRAW_ALTERS) || // Next record would alter the drawing environment in some way
+ ((d->mask & U_DRAW_NOFILL) != (wmr_mask & U_DRAW_NOFILL)) || // Fill<->!Fill requires a draw between
( (wmr_mask & U_DRAW_VISIBLE) && // Next record is visible...
(
( !(d->mask & U_DRAW_ONLYTO) ) || // Non *TO records cannot be followed by any Visible
diff --git a/src/extension/internal/wmf-print.cpp b/src/extension/internal/wmf-print.cpp
index 3d913bf1e..6dd9d895e 100644
--- a/src/extension/internal/wmf-print.cpp
+++ b/src/extension/internal/wmf-print.cpp
@@ -598,7 +598,28 @@ int PrintWmf::create_pen(SPStyle const *style, const Geom::Affine &transform)
if (!style->stroke_dasharray.values.empty()) {
if (!FixPPTDashLine) { // if this is set code elsewhere will break dots/dashes into many smaller lines.
- penstyle = U_PS_DASH;// userstyle not supported apparently, for now map all Inkscape dot/dash to just dash
+ int n_dash = style->stroke_dasharray.values.size();
+ /* options are dash, dot, dashdot and dashdotdot. Try to pick the closest one. */
+ int mark_short=INT_MAX;
+ int mark_long =0;
+ int i;
+ for (i=0;i<n_dash;i++) {
+ int mark = style->stroke_dasharray.values[i];
+ if (mark>mark_long) { mark_long = mark; }
+ if (mark<mark_short) { mark_short = mark; }
+ }
+ if(mark_long == mark_short){ // only one mark size
+ penstyle = U_PS_DOT;
+ }
+ else if (n_dash==2) {
+ penstyle = U_PS_DASH;
+ }
+ else if (n_dash==4) {
+ penstyle = U_PS_DASHDOT;
+ }
+ else {
+ penstyle = U_PS_DASHDOTDOT;
+ }
}
}
diff --git a/src/extension/internal/wpg-input.cpp b/src/extension/internal/wpg-input.cpp
index 12457791b..299614c94 100644
--- a/src/extension/internal/wpg-input.cpp
+++ b/src/extension/internal/wpg-input.cpp
@@ -85,10 +85,13 @@ SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u
// RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows
// therefore attempt to convert uri to the system codepage
// even if this is not possible the alternate short (8.3) file name will be used if available
- uri = g_win32_locale_filename_from_utf8(uri);
+ gchar * converted_uri = g_win32_locale_filename_from_utf8(uri);
+ RVNGInputStream* input = new RVNGFileStream(converted_uri);
+ g_free(converted_uri);
+ #else
+ RVNGInputStream* input = new RVNGFileStream(uri);
#endif
- RVNGInputStream* input = new RVNGFileStream(uri);
#if WITH_LIBWPG03
if (input->isStructured()) {
RVNGInputStream* olestream = input->getSubStreamByName("PerfectOffice_MAIN");
diff --git a/src/extension/param/bool.cpp b/src/extension/param/bool.cpp
index ca61d8c51..9f8d3dcc0 100644
--- a/src/extension/param/bool.cpp
+++ b/src/extension/param/bool.cpp
@@ -24,9 +24,17 @@
namespace Inkscape {
namespace Extension {
-ParamBool::ParamBool(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) :
- Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext),
- _value(false), _indent(0)
+ParamBool::ParamBool(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext)
+ , _value(false)
{
const char * defaultval = NULL;
if (xml->firstChild() != NULL) {
@@ -39,11 +47,6 @@ ParamBool::ParamBool(const gchar * name, const gchar * guitext, const gchar * de
_value = false;
}
- const char * indent = xml->attribute("indent");
- if (indent != NULL) {
- _indent = atoi(indent) * 12;
- }
-
gchar * pref_name = this->pref_name();
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
_value = prefs->getBool(extension_pref_root + pref_name, _value);
@@ -66,7 +69,7 @@ bool ParamBool::set( bool in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node
bool ParamBool::get(const SPDocument * /*doc*/, const Inkscape::XML::Node * /*node*/) const
{
- return _value;
+ return _value;
}
/**
@@ -130,7 +133,7 @@ Gtk::Widget *ParamBool::get_widget(SPDocument * doc, Inkscape::XML::Node * node,
return NULL;
}
- auto hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4));
+ auto hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, Parameter::GUI_PARAM_WIDGETS_SPACING));
hbox->set_homogeneous(false);
Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START));
@@ -139,7 +142,7 @@ Gtk::Widget *ParamBool::get_widget(SPDocument * doc, Inkscape::XML::Node * node,
ParamBoolCheckButton * checkbox = Gtk::manage(new ParamBoolCheckButton(this, doc, node, changeSignal));
checkbox->show();
- hbox->pack_start(*checkbox, false, false, _indent);
+ hbox->pack_start(*checkbox, false, false);
hbox->show();
diff --git a/src/extension/param/bool.h b/src/extension/param/bool.h
index 5d190b9d3..0af12b105 100644
--- a/src/extension/param/bool.h
+++ b/src/extension/param/bool.h
@@ -32,7 +32,15 @@ public:
/**
* Use the superclass' allocator and set the \c _value.
*/
- ParamBool(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml);
+ ParamBool(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
/**
* Returns the current state/value.
@@ -69,7 +77,6 @@ public:
private:
/** Internal value. */
bool _value;
- int _indent;
};
} // namespace Extension
diff --git a/src/extension/param/color.cpp b/src/extension/param/color.cpp
index 0b58c5011..fd88e9adb 100644
--- a/src/extension/param/color.cpp
+++ b/src/extension/param/color.cpp
@@ -52,9 +52,17 @@ guint32 ParamColor::set( guint32 in, SPDocument * /*doc*/, Inkscape::XML::Node *
return in;
}
-ParamColor::ParamColor (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) :
- Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext),
- _changeSignal(0)
+ParamColor::ParamColor(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext)
+ , _changeSignal(0)
{
const char * defaulthex = NULL;
if (xml->firstChild() != NULL)
@@ -86,7 +94,7 @@ Gtk::Widget *ParamColor::get_widget( SPDocument * /*doc*/, Inkscape::XML::Node *
{
using Inkscape::UI::Widget::ColorNotebook;
- if (_gui_hidden) return NULL;
+ if (_gui_hidden) return NULL;
if (changeSignal) {
_changeSignal = new sigc::signal<void>(*changeSignal);
@@ -98,9 +106,9 @@ Gtk::Widget *ParamColor::get_widget( SPDocument * /*doc*/, Inkscape::XML::Node *
_color_changed.block(false);
}
- Gtk::HBox *hbox = Gtk::manage(new Gtk::HBox(false, 4));
+ Gtk::HBox *hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING));
Gtk::Widget *selector = Gtk::manage(new ColorNotebook(_color));
- hbox->pack_start (*selector, true, true, 0);
+ hbox->pack_start(*selector, true, true, 0);
selector->show();
hbox->show();
return hbox;
diff --git a/src/extension/param/color.h b/src/extension/param/color.h
index ed2e57ceb..d6e9d1fbe 100644
--- a/src/extension/param/color.h
+++ b/src/extension/param/color.h
@@ -31,8 +31,15 @@ private:
Inkscape::UI::SelectedColor _color;
sigc::connection _color_changed;
public:
- ParamColor(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml);
-
+ ParamColor(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
virtual ~ParamColor(void);
/** Returns \c _value, with a \i const to protect it. */
diff --git a/src/extension/param/description.cpp b/src/extension/param/description.cpp
index 07aaa07cc..7cf818280 100644
--- a/src/extension/param/description.cpp
+++ b/src/extension/param/description.cpp
@@ -16,6 +16,7 @@
#include <gtkmm/box.h>
#include <gtkmm/label.h>
#include <glibmm/i18n.h>
+#include <glibmm/markup.h>
#include "xml/node.h"
#include "extension/extension.h"
@@ -26,17 +27,19 @@ namespace Extension {
/** \brief Initialize the object, to do that, copy the data. */
-ParamDescription::ParamDescription (const gchar * name,
- const gchar * guitext,
- const gchar * desc,
- const Parameter::_scope_t scope,
- bool gui_hidden,
- const gchar * gui_tip,
- Inkscape::Extension::Extension * ext,
- Inkscape::XML::Node * xml,
- AppearanceMode mode) :
- Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext),
- _value(NULL), _mode(mode), _indent(0)
+ParamDescription::ParamDescription(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode)
+ : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext)
+ , _value(NULL)
+ , _mode(mode)
{
// printf("Building Description\n");
const char * defaultval = NULL;
@@ -50,11 +53,6 @@ ParamDescription::ParamDescription (const gchar * name,
_context = xml->attribute("msgctxt");
- const char * indent = xml->attribute("indent");
- if (indent != NULL) {
- _indent = atoi(indent) * 12;
- }
-
return;
}
@@ -76,23 +74,37 @@ ParamDescription::get_widget (SPDocument * /*doc*/, Inkscape::XML::Node * /*node
} else {
newguitext = _(_value);
}
-
- Gtk::Label * label;
- int padding = 12 + _indent;
+
+ Gtk::Label * label = Gtk::manage(new Gtk::Label());
if (_mode == HEADER) {
- label = Gtk::manage(new Gtk::Label(Glib::ustring("<b>") +newguitext + Glib::ustring("</b>"), Gtk::ALIGN_START));
- label->set_margin_top(5);
- label->set_margin_bottom(5);
- label->set_use_markup(true);
- padding = _indent;
+ label->set_markup(Glib::ustring("<b>") + Glib::Markup::escape_text(newguitext) + Glib::ustring("</b>"));
+ label->set_margin_top(5);
+ label->set_margin_bottom(5);
+ } else if (_mode == URL) {
+ Glib::ustring escaped_url = Glib::Markup::escape_text(newguitext);
+ label->set_markup(Glib::ustring::compose("<a href='%1'>%1</a>", escaped_url));
} else {
- label = Gtk::manage(new Gtk::Label(newguitext, Gtk::ALIGN_START));
+ label->set_text(newguitext);
}
label->set_line_wrap();
+ //label->set_xalign(0); // requires gtkmm 3.16
+ label->set_alignment(Gtk::ALIGN_START);
+
+ // TODO: Ugly "fix" for gtk3 width/height calculation of labels.
+ // - If not applying any limits long labels will make the window grow horizontally until it uses up
+ // most of the available space (i.e. most of the screen area) which is ridicously wide
+ // - By using "set_default_size(0,0)" in prefidalog.cpp we tell the window to shrink as much as possible,
+ // however this can result in a much to narrow dialog instead and much unnecessary wrapping
+ // - Here we set a lower limit of GUI_MAX_LINE_LENGTH characters per line that long texts will always use
+ // This means texts can not shrink anymore (they can still grow, though) and it's also necessary
+ // to prevent https://bugzilla.gnome.org/show_bug.cgi?id=773572
+ int len = newguitext.length();
+ label->set_width_chars(len > Parameter::GUI_MAX_LINE_LENGTH ? Parameter::GUI_MAX_LINE_LENGTH : len);
+
label->show();
- Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4));
- hbox->pack_start(*label, true, true, padding);
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox());
+ hbox->pack_start(*label, true, true);
hbox->show();
return hbox;
diff --git a/src/extension/param/description.h b/src/extension/param/description.h
index 3a72c4112..c6c5f4013 100644
--- a/src/extension/param/description.h
+++ b/src/extension/param/description.h
@@ -27,23 +27,24 @@ namespace Extension {
class ParamDescription : public Parameter {
public:
enum AppearanceMode {
- DESC, HEADER
+ DESC, HEADER, URL
};
ParamDescription(const gchar * name,
const gchar * guitext,
const gchar * desc,
const Parameter::_scope_t scope,
bool gui_hidden,
- const gchar * gui_tip,
+ const gchar * gui_tip,
+ int indent,
Inkscape::Extension::Extension * ext,
Inkscape::XML::Node * xml,
AppearanceMode mode);
+
Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal);
private:
/** \brief Internal value. */
gchar * _value;
AppearanceMode _mode;
- int _indent;
const gchar* _context;
};
diff --git a/src/extension/param/enum.cpp b/src/extension/param/enum.cpp
index 8bc0fbda7..a26458c35 100644
--- a/src/extension/param/enum.cpp
+++ b/src/extension/param/enum.cpp
@@ -46,12 +46,17 @@ public:
};
-ParamComboBox::ParamComboBox(const gchar *name, const gchar *guitext, const gchar *desc,
- const Parameter::_scope_t scope, bool gui_hidden, const gchar *gui_tip,
- Inkscape::Extension::Extension *ext, Inkscape::XML::Node *xml)
- : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext)
+ParamComboBox::ParamComboBox(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext)
, _value(NULL)
- , _indent(0)
, choices(NULL)
{
const char *xmlval = NULL; // the value stored in XML
@@ -94,17 +99,12 @@ ParamComboBox::ParamComboBox(const gchar *name, const gchar *guitext, const gcha
}
}
}
-
+
// Initialize _value with the default value from xml
// for simplicity : default to the contents of the first xml-child
if (xml->firstChild() && xml->firstChild()->firstChild()) {
xmlval = xml->firstChild()->attribute("value");
}
-
- const char *indent = xml->attribute("indent");
- if (indent != NULL) {
- _indent = atoi(indent) * 12;
- }
}
gchar * pref_name = this->pref_name();
@@ -250,10 +250,10 @@ Gtk::Widget *ParamComboBox::get_widget(SPDocument * doc, Inkscape::XML::Node * n
return NULL;
}
- Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4));
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING));
Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START));
label->show();
- hbox->pack_start(*label, false, false, _indent);
+ hbox->pack_start(*label, false, false);
ParamComboBoxEntry * combo = Gtk::manage(new ParamComboBoxEntry(this, doc, node, changeSignal));
// add choice strings:
diff --git a/src/extension/param/enum.h b/src/extension/param/enum.h
index 52e018469..f9d3d75f7 100644
--- a/src/extension/param/enum.h
+++ b/src/extension/param/enum.h
@@ -34,12 +34,20 @@ private:
been allocated in memory. And should be free'd.
It is the value of the current selected string */
gchar * _value;
- int _indent;
GSList * choices; /**< A table to store the choice strings */
public:
- ParamComboBox(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml);
+ ParamComboBox(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
virtual ~ParamComboBox(void);
+
Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal);
// Explicitly call superclass version to avoid method being hidden.
diff --git a/src/extension/param/float.cpp b/src/extension/param/float.cpp
index 23a03ea8f..e3fdba826 100644
--- a/src/extension/param/float.cpp
+++ b/src/extension/param/float.cpp
@@ -27,17 +27,21 @@ namespace Extension {
/** Use the superclass' allocator and set the \c _value. */
-ParamFloat::ParamFloat (const gchar * name,
- const gchar * guitext,
- const gchar * desc,
- const Parameter::_scope_t scope,
- bool gui_hidden,
- const gchar * gui_tip,
- Inkscape::Extension::Extension * ext,
- Inkscape::XML::Node * xml,
- AppearanceMode mode) :
- Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext),
- _value(0.0), _mode(mode), _indent(0), _min(0.0), _max(10.0)
+ParamFloat::ParamFloat(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode)
+ : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext)
+ , _value(0.0)
+ , _mode(mode)
+ , _min(0.0)
+ , _max(10.0)
{
const gchar * defaultval = NULL;
if (xml->firstChild() != NULL) {
@@ -69,11 +73,6 @@ ParamFloat::ParamFloat (const gchar * name,
_min = 0.0;
}
- const char * indent = xml->attribute("indent");
- if (indent != NULL) {
- _indent = atoi(indent) * 12;
- }
-
gchar * pref_name = this->pref_name();
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
_value = prefs->getDouble(extension_pref_root + pref_name, _value);
@@ -175,24 +174,24 @@ Gtk::Widget * ParamFloat::get_widget(SPDocument * doc, Inkscape::XML::Node * nod
return NULL;
}
- Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4));
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING));
auto pfa = new ParamFloatAdjustment(this, doc, node, changeSignal);
Glib::RefPtr<Gtk::Adjustment> fadjust(pfa);
-
+
if (_mode == FULL) {
UI::Widget::SpinScale *scale = new UI::Widget::SpinScale(_(_text), fadjust, _precision);
scale->set_size_request(400, -1);
scale->show();
- hbox->pack_start(*scale, false, false);
+ hbox->pack_start(*scale, true, true);
}
else if (_mode == MINIMAL) {
Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START));
label->show();
- hbox->pack_start(*label, true, true, _indent);
+ hbox->pack_start(*label, true, true);
auto spin = Gtk::manage(new Inkscape::UI::Widget::SpinButton(fadjust, 0.1, _precision));
spin->show();
diff --git a/src/extension/param/float.h b/src/extension/param/float.h
index 42b1698b1..7fd86cea7 100644
--- a/src/extension/param/float.h
+++ b/src/extension/param/float.h
@@ -29,15 +29,17 @@ public:
enum AppearanceMode {
FULL, MINIMAL
};
- ParamFloat (const gchar * name,
- const gchar * guitext,
- const gchar * desc,
- const Parameter::_scope_t scope,
- bool gui_hidden,
- const gchar * gui_tip,
- Inkscape::Extension::Extension * ext,
- Inkscape::XML::Node * xml,
- AppearanceMode mode);
+ ParamFloat(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode);
+
/** Returns \c _value. */
float get(const SPDocument * /*doc*/, const Inkscape::XML::Node * /*node*/) const { return _value; }
@@ -60,7 +62,6 @@ private:
/** Internal value. */
float _value;
AppearanceMode _mode;
- int _indent;
float _min;
float _max;
int _precision;
diff --git a/src/extension/param/int.cpp b/src/extension/param/int.cpp
index 222d4f243..538ddc08d 100644
--- a/src/extension/param/int.cpp
+++ b/src/extension/param/int.cpp
@@ -27,17 +27,21 @@ namespace Extension {
/** Use the superclass' allocator and set the \c _value. */
-ParamInt::ParamInt (const gchar * name,
- const gchar * guitext,
- const gchar * desc,
- const Parameter::_scope_t scope,
- bool gui_hidden,
- const gchar * gui_tip,
- Inkscape::Extension::Extension * ext,
- Inkscape::XML::Node * xml,
- AppearanceMode mode) :
- Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext),
- _value(0), _mode(mode), _indent(0), _min(0), _max(10)
+ParamInt::ParamInt(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode)
+ : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext)
+ , _value(0)
+ , _mode(mode)
+ , _min(0)
+ , _max(10)
{
const char * defaultval = NULL;
if (xml->firstChild() != NULL) {
@@ -62,11 +66,6 @@ ParamInt::ParamInt (const gchar * name,
_min = 0;
}
- const char * indent = xml->attribute("indent");
- if (indent != NULL) {
- _indent = atoi(indent) * 12;
- }
-
gchar *pref_name = this->pref_name();
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
_value = prefs->getInt(extension_pref_root + pref_name, _value);
@@ -156,7 +155,7 @@ ParamInt::get_widget (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal
return NULL;
}
- Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4));
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING));
auto pia = new ParamIntAdjustment(this, doc, node, changeSignal);
Glib::RefPtr<Gtk::Adjustment> fadjust(pia);
@@ -166,13 +165,13 @@ ParamInt::get_widget (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal
UI::Widget::SpinScale *scale = new UI::Widget::SpinScale(_(_text), fadjust, 0);
scale->set_size_request(400, -1);
scale->show();
- hbox->pack_start(*scale, false, false);
+ hbox->pack_start(*scale, true, true);
}
else if (_mode == MINIMAL) {
-
+
Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START));
label->show();
- hbox->pack_start(*label, true, true, _indent);
+ hbox->pack_start(*label, true, true);
auto spin = Gtk::manage(new Inkscape::UI::Widget::SpinButton(fadjust, 1.0, 0));
spin->show();
diff --git a/src/extension/param/int.h b/src/extension/param/int.h
index 3fd6cea9b..40c0395ec 100644
--- a/src/extension/param/int.h
+++ b/src/extension/param/int.h
@@ -29,15 +29,16 @@ public:
enum AppearanceMode {
FULL, MINIMAL
};
- ParamInt (const gchar * name,
- const gchar * guitext,
- const gchar * desc,
- const Parameter::_scope_t scope,
- bool gui_hidden,
- const gchar * gui_tip,
- Inkscape::Extension::Extension * ext,
- Inkscape::XML::Node * xml,
- AppearanceMode mode);
+ ParamInt(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode);
/** Returns \c _value. */
int get(const SPDocument * /*doc*/, const Inkscape::XML::Node * /*node*/) const { return _value; }
@@ -59,7 +60,6 @@ private:
/** Internal value. */
int _value;
AppearanceMode _mode;
- int _indent;
int _min;
int _max;
};
diff --git a/src/extension/param/notebook.cpp b/src/extension/param/notebook.cpp
index 957d12d06..8ef48926e 100644
--- a/src/extension/param/notebook.cpp
+++ b/src/extension/param/notebook.cpp
@@ -54,8 +54,16 @@ private:
public:
static ParamNotebookPage * makepage (Inkscape::XML::Node * in_repr, Inkscape::Extension::Extension * in_ext);
- ParamNotebookPage(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml);
+ ParamNotebookPage(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
~ParamNotebookPage(void);
+
Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal);
void paramString (std::list <std::string> &list);
gchar * get_guitext (void) {return _text;};
@@ -63,8 +71,15 @@ public:
}; /* class ParamNotebookPage */
-ParamNotebookPage::ParamNotebookPage (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) :
- Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext)
+ParamNotebookPage::ParamNotebookPage(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, /*indent*/ 0, ext)
{
parameters = NULL;
@@ -199,16 +214,19 @@ Gtk::Widget * ParamNotebookPage::get_widget(SPDocument * doc, Inkscape::XML::Nod
}
Gtk::VBox * vbox = Gtk::manage(new Gtk::VBox);
- vbox->set_border_width(5);
+ vbox->set_border_width(Parameter::GUI_BOX_MARGIN);
+ vbox->set_spacing(Parameter::GUI_BOX_SPACING);
// add parameters onto page (if any)
for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
Parameter * param = reinterpret_cast<Parameter *>(list->data);
Gtk::Widget * widg = param->get_widget(doc, node, changeSignal);
if (widg) {
+ int indent = param->get_indent();
+ widg->set_margin_left(indent * Parameter::GUI_INDENTATION);
+ vbox->pack_start(*widg, false, false, 0);
+
gchar const * tip = param->get_tooltip();
- // printf("Tip: '%s'\n", tip);
- vbox->pack_start(*widg, false, false, 2);
if (tip) {
widg->set_tooltip_text(_(tip));
} else {
@@ -224,8 +242,16 @@ Gtk::Widget * ParamNotebookPage::get_widget(SPDocument * doc, Inkscape::XML::Nod
}
-ParamNotebook::ParamNotebook (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) :
- Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext)
+ParamNotebook::ParamNotebook(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext)
{
pages = NULL;
diff --git a/src/extension/param/notebook.h b/src/extension/param/notebook.h
index 3c90964fd..278c4de12 100644
--- a/src/extension/param/notebook.h
+++ b/src/extension/param/notebook.h
@@ -41,8 +41,17 @@ private:
This only gets created if there are pages in this
notebook */
public:
- ParamNotebook(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml);
+ ParamNotebook(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
virtual ~ParamNotebook(void);
+
Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal);
/**
diff --git a/src/extension/param/parameter.cpp b/src/extension/param/parameter.cpp
index a5632a39a..0eb491078 100644
--- a/src/extension/param/parameter.cpp
+++ b/src/extension/param/parameter.cpp
@@ -50,10 +50,17 @@ Parameter *Parameter::make(Inkscape::XML::Node *in_repr, Inkscape::Extension::Ex
const char *name = in_repr->attribute("name");
const char *type = in_repr->attribute("type");
- // In this case we just don't have enough information
- if (!name || !type) {
+ // we can't create a parameter without type
+ if (!type) {
return NULL;
}
+ // also require name unless it's a pure UI element that does not store its value
+ if (!name) {
+ static std::vector<std::string> ui_elements = {"description"};
+ if (std::find(ui_elements.begin(), ui_elements.end(), type) == ui_elements.end()) {
+ return NULL;
+ }
+ }
const char *guitext = in_repr->attribute("gui-text");
if (guitext == NULL) {
@@ -78,6 +85,17 @@ Parameter *Parameter::make(Inkscape::XML::Node *in_repr, Inkscape::Extension::Ex
/* else stays false */
}
}
+ int indent = 0;
+ {
+ const char *indent_attr = in_repr->attribute("indent");
+ if (indent_attr != NULL) {
+ if (strcmp(indent_attr, "true") == 0) {
+ indent = 1;
+ } else {
+ indent = atoi(indent_attr);
+ }
+ }
+ }
const gchar* appearance = in_repr->attribute("appearance");
Parameter::_scope_t scope = Parameter::SCOPE_USER;
@@ -96,44 +114,48 @@ Parameter *Parameter::make(Inkscape::XML::Node *in_repr, Inkscape::Extension::Ex
Parameter * param = NULL;
if (!strcmp(type, "boolean")) {
- param = new ParamBool(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr);
+ param = new ParamBool(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr);
} else if (!strcmp(type, "int")) {
if (appearance && !strcmp(appearance, "full")) {
- param = new ParamInt(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamInt::FULL);
+ param = new ParamInt(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamInt::FULL);
} else {
- param = new ParamInt(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamInt::MINIMAL);
+ param = new ParamInt(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamInt::MINIMAL);
}
} else if (!strcmp(type, "float")) {
if (appearance && !strcmp(appearance, "full")) {
- param = new ParamFloat(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamFloat::FULL);
+ param = new ParamFloat(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamFloat::FULL);
} else {
- param = new ParamFloat(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamFloat::MINIMAL);
+ param = new ParamFloat(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamFloat::MINIMAL);
}
} else if (!strcmp(type, "string")) {
- param = new ParamString(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr);
+ param = new ParamString(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr);
gchar const * max_length = in_repr->attribute("max_length");
if (max_length != NULL) {
ParamString * ps = dynamic_cast<ParamString *>(param);
ps->setMaxLength(atoi(max_length));
}
} else if (!strcmp(type, "description")) {
- if (appearance && !strcmp(appearance, "header")) {
- param = new ParamDescription(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamDescription::HEADER);
- } else {
- param = new ParamDescription(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamDescription::DESC);
- }
+ ParamDescription::AppearanceMode appearance_mode = ParamDescription::DESC;
+ if (appearance) {
+ if (!strcmp(appearance, "header")) {
+ appearance_mode = ParamDescription::HEADER;
+ } else if (!strcmp(appearance, "url")) {
+ appearance_mode = ParamDescription::URL;
+ }
+ }
+ param = new ParamDescription(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, appearance_mode);
} else if (!strcmp(type, "enum")) {
- param = new ParamComboBox(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr);
+ param = new ParamComboBox(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr);
} else if (!strcmp(type, "notebook")) {
- param = new ParamNotebook(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr);
+ param = new ParamNotebook(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr);
} else if (!strcmp(type, "optiongroup")) {
if (appearance && !strcmp(appearance, "minimal")) {
- param = new ParamRadioButton(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamRadioButton::MINIMAL);
+ param = new ParamRadioButton(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamRadioButton::MINIMAL);
} else {
- param = new ParamRadioButton(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamRadioButton::FULL);
+ param = new ParamRadioButton(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamRadioButton::FULL);
}
} else if (!strcmp(type, "color")) {
- param = new ParamColor(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr);
+ param = new ParamColor(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr);
}
// Note: param could equal NULL
@@ -280,12 +302,13 @@ Parameter::set_color (guint32 in, SPDocument * doc, Inkscape::XML::Node * node)
/** Oop, now that we need a parameter, we need it's name. */
-Parameter::Parameter(gchar const * name, gchar const * guitext, gchar const * desc, const Parameter::_scope_t scope, bool gui_hidden, gchar const * gui_tip, Inkscape::Extension::Extension * ext) :
+Parameter::Parameter(gchar const * name, gchar const * guitext, gchar const * desc, const Parameter::_scope_t scope, bool gui_hidden, gchar const * gui_tip, int indent, Inkscape::Extension::Extension * ext) :
_desc(0),
_scope(scope),
_text(0),
_gui_hidden(gui_hidden),
_gui_tip(0),
+ _indent(indent),
extension(ext),
_name(0)
{
@@ -316,6 +339,7 @@ Parameter::Parameter (gchar const * name, gchar const * guitext, Inkscape::Exten
_text(0),
_gui_hidden(false),
_gui_tip(0),
+ _indent(0),
extension(ext),
_name(0)
{
diff --git a/src/extension/param/parameter.h b/src/extension/param/parameter.h
index 5e1e3897f..c107f24f9 100644
--- a/src/extension/param/parameter.h
+++ b/src/extension/param/parameter.h
@@ -37,7 +37,7 @@ class Extension;
*/
extern Glib::ustring const extension_pref_root;
-/**
+/**
* A class to represent the parameter of an extension.
*
* This is really a super class that allows them to abstract all
@@ -62,6 +62,7 @@ public:
const Parameter::_scope_t scope,
bool gui_hidden,
gchar const *gui_tip,
+ int indent,
Inkscape::Extension::Extension * ext);
Parameter(gchar const *name,
@@ -144,6 +145,9 @@ public:
/** Indicates if the GUI for this parameter is hidden or not */
bool get_gui_hidden() const { return _gui_hidden; }
+ /** Indentation level of the parameter */
+ int get_indent() const { return _indent; }
+
virtual void string(std::list <std::string> &list) const;
/**
@@ -155,6 +159,19 @@ public:
/** All the code in Notebook::get_param to get the notebook content. */
virtual Parameter *get_param(gchar const *name);
+
+ /** Recommended margin of boxes containing multiple Parameters (in px) */
+ const static int GUI_BOX_MARGIN = 10;
+ /** Recommended spacing between multiple Parameters packed into a box (in px) */
+ const static int GUI_BOX_SPACING = 4;
+ /** Recommended spacing between the widgets making up a signle Parameter (e.g. label and input) (in px) */
+ const static int GUI_PARAM_WIDGETS_SPACING = 4;
+ /** Recommended indentation width of parameters (in px) */
+ const static int GUI_INDENTATION = 12;
+ /** Recommended maximum line lenght for wrapping textual parameters (in chars) */
+ const static int GUI_MAX_LINE_LENGTH = 60;
+
+
protected:
/** Description of the parameter. */
gchar * _desc;
@@ -171,6 +188,9 @@ protected:
/** A tip for the GUI if there is one. */
gchar * _gui_tip;
+ /** Indentation level of the parameter. */
+ int _indent;
+
/* **** funcs **** */
diff --git a/src/extension/param/radiobutton.cpp b/src/extension/param/radiobutton.cpp
index c54cc0ec3..178dc20e8 100644
--- a/src/extension/param/radiobutton.cpp
+++ b/src/extension/param/radiobutton.cpp
@@ -57,17 +57,20 @@ public:
Glib::ustring * guitext;
};
-ParamRadioButton::ParamRadioButton (const gchar * name,
- const gchar * guitext,
- const gchar * desc,
- const Parameter::_scope_t scope,
- bool gui_hidden,
- const gchar * gui_tip,
- Inkscape::Extension::Extension * ext,
- Inkscape::XML::Node * xml,
- AppearanceMode mode) :
- Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext),
- _value(0), _mode(mode), _indent(0), choices(0)
+ParamRadioButton::ParamRadioButton(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode)
+ : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext)
+ , _value(0)
+ , _mode(mode)
+ , choices(0)
{
// Read XML tree to add enumeration items:
// printf("Extension Constructor: ");
@@ -118,11 +121,6 @@ ParamRadioButton::ParamRadioButton (const gchar * name,
defaultval = (static_cast<optionentry*> (choices->data))->value->c_str();
}
- const char *indent = xml ? xml->attribute("indent") : NULL;
- if (indent != NULL) {
- _indent = atoi(indent) * 12;
- }
-
gchar * pref_name = this->pref_name();
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name);
@@ -303,14 +301,14 @@ Gtk::Widget * ParamRadioButton::get_widget(SPDocument * doc, Inkscape::XML::Node
return NULL;
}
- auto hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4));
+ auto hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, Parameter::GUI_PARAM_WIDGETS_SPACING));
hbox->set_homogeneous(false);
auto vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0));
vbox->set_homogeneous(false);
Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START, Gtk::ALIGN_START));
label->show();
- hbox->pack_start(*label, false, false, _indent);
+ hbox->pack_start(*label, false, false);
Gtk::ComboBoxText* cbt = 0;
bool comboSet = false;
diff --git a/src/extension/param/radiobutton.h b/src/extension/param/radiobutton.h
index 0e0f643c9..5e15dcb57 100644
--- a/src/extension/param/radiobutton.h
+++ b/src/extension/param/radiobutton.h
@@ -40,10 +40,12 @@ public:
const Parameter::_scope_t scope,
bool gui_hidden,
const gchar * gui_tip,
+ int indent,
Inkscape::Extension::Extension * ext,
Inkscape::XML::Node * xml,
AppearanceMode mode);
virtual ~ParamRadioButton(void);
+
Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal);
// Explicitly call superclass version to avoid method being hidden.
@@ -63,7 +65,6 @@ private:
It is the value of the current selected string */
gchar * _value;
AppearanceMode _mode;
- int _indent;
GSList * choices; /**< A table to store the choice strings */
}; /* class ParamRadioButton */
diff --git a/src/extension/param/string.cpp b/src/extension/param/string.cpp
index 1d9205502..0c5238f99 100644
--- a/src/extension/param/string.cpp
+++ b/src/extension/param/string.cpp
@@ -24,7 +24,7 @@
namespace Inkscape {
namespace Extension {
-
+
/** Free the allocated data. */
ParamString::~ParamString(void)
@@ -76,20 +76,23 @@ void ParamString::string(std::string &string) const
}
/** Initialize the object, to do that, copy the data. */
-ParamString::ParamString (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) :
- Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext),
- _value(NULL), _indent(0)
+ParamString::ParamString(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext)
+ , _value(NULL)
{
const char * defaultval = NULL;
if (xml->firstChild() != NULL) {
defaultval = xml->firstChild()->content();
}
- const char * indent = xml->attribute("indent");
- if (indent != NULL) {
- _indent = atoi(indent) * 12;
- }
-
gchar * pref_name = this->pref_name();
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name);
@@ -165,10 +168,10 @@ Gtk::Widget * ParamString::get_widget(SPDocument * doc, Inkscape::XML::Node * no
return NULL;
}
- Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4));
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING));
Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START));
label->show();
- hbox->pack_start(*label, false, false, _indent);
+ hbox->pack_start(*label, false, false);
ParamStringEntry * textbox = new ParamStringEntry(this, doc, node, changeSignal);
textbox->show();
diff --git a/src/extension/param/string.h b/src/extension/param/string.h
index 3b137aeac..f5412ebcc 100644
--- a/src/extension/param/string.h
+++ b/src/extension/param/string.h
@@ -19,12 +19,19 @@ private:
/** \brief Internal value. This should point to a string that has
been allocated in memory. And should be free'd. */
gchar * _value;
- /** \brief Internal value. This indicates the maximum leght of the string. Zero meaning unlimited.
+ /** \brief Internal value. This indicates the maximum lenth of the string. Zero meaning unlimited.
*/
- int _indent;
gint _max_length;
public:
- ParamString(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml);
+ ParamString(const gchar * name,
+ const gchar * guitext,
+ const gchar * desc,
+ const Parameter::_scope_t scope,
+ bool gui_hidden,
+ const gchar * gui_tip,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
virtual ~ParamString(void);
/** \brief Returns \c _value, with a \i const to protect it. */
diff --git a/src/extension/prefdialog.cpp b/src/extension/prefdialog.cpp
index fcc88853d..2bdbb081e 100644
--- a/src/extension/prefdialog.cpp
+++ b/src/extension/prefdialog.cpp
@@ -50,6 +50,8 @@ PrefDialog::PrefDialog (Glib::ustring name, gchar const * help, Gtk::Widget * co
_effect(effect),
_exEnv(NULL)
{
+ this->set_default_size(0,0); // we want the window to be as small as possible instead of clobbering up space
+
Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox());
if (controls == NULL) {
if (_effect == NULL) {
@@ -59,11 +61,10 @@ PrefDialog::PrefDialog (Glib::ustring name, gchar const * help, Gtk::Widget * co
controls = _effect->get_imp()->prefs_effect(_effect, SP_ACTIVE_DESKTOP, &_signal_param_change, NULL);
_signal_param_change.connect(sigc::mem_fun(this, &PrefDialog::param_change));
}
-
- hbox->pack_start(*controls, true, true, 6);
+ hbox->pack_start(*controls, true, true, 0);
hbox->show();
- this->get_content_area()->pack_start(*hbox, true, true, 6);
+ this->get_content_area()->pack_start(*hbox, true, true, 0);
/*
Gtk::Button * help_button = add_button(Gtk::Stock::HELP, Gtk::RESPONSE_HELP);
@@ -88,15 +89,16 @@ PrefDialog::PrefDialog (Glib::ustring name, gchar const * help, Gtk::Widget * co
auto sep = Gtk::manage(new Gtk::Separator());
sep->show();
- this->get_content_area()->pack_start(*sep, true, true, 4);
+ this->get_content_area()->pack_start(*sep, false, false, Parameter::GUI_BOX_SPACING);
hbox = Gtk::manage(new Gtk::HBox());
+ hbox->set_border_width(Parameter::GUI_BOX_MARGIN);
_button_preview = _param_preview->get_widget(NULL, NULL, &_signal_preview);
_button_preview->show();
- hbox->pack_start(*_button_preview, true, true,6);
+ hbox->pack_start(*_button_preview, true, true, 0);
hbox->show();
- this->get_content_area()->pack_start(*hbox, true, true, 6);
+ this->get_content_area()->pack_start(*hbox, false, false, 0);
Gtk::Box * hbox = dynamic_cast<Gtk::Box *>(_button_preview);
if (hbox != NULL) {
diff --git a/src/file-update.cpp b/src/file-update.cpp
index 40a8bc834..2275ab1c3 100644
--- a/src/file-update.cpp
+++ b/src/file-update.cpp
@@ -19,6 +19,7 @@
#include "style.h"
#include "document.h"
#include <string>
+#include <clocale>
#include "text-editing.h"
using namespace std;
@@ -149,10 +150,12 @@ void fix_update(SPObject *o) {
void sp_file_convert_text_baseline_spacing(SPDocument *doc)
{
+ setlocale(LC_NUMERIC,"C");
sp_file_text_run_recursive(fix_blank_line, doc->getRoot());
sp_file_text_run_recursive(fix_line_spacing, doc->getRoot());
sp_file_text_run_recursive(fix_font_size, doc->getRoot());
sp_file_text_run_recursive(fix_update, doc->getRoot());
+ setlocale(LC_NUMERIC,"");
}
void sp_file_convert_font_name(SPDocument *doc)
diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp
index a928cac0d..006e2f790 100644
--- a/src/gradient-drag.cpp
+++ b/src/gradient-drag.cpp
@@ -1067,7 +1067,7 @@ static void gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const &
-static void gr_knot_grabbed_handler(SPKnot */*knot*/, unsigned int /*state*/, gpointer data)
+static void gr_knot_mousedown_handler(SPKnot */*knot*/, unsigned int /*state*/, gpointer data)
{
GrDragger *dragger = (GrDragger *) data;
GrDrag *drag = dragger->parent;
@@ -1688,7 +1688,7 @@ GrDragger::GrDragger(GrDrag *parent, Geom::Point p, GrDraggable *draggable)
this->sizeUpdatedConn = ControlManager::getManager().connectCtrlSizeChanged(sigc::mem_fun(*this, &GrDragger::updateControlSizes));
this->_clicked_connection = this->knot->click_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_clicked_handler), this));
this->_doubleclicked_connection = this->knot->doubleclicked_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_doubleclicked_handler), this));
- this->_grabbed_connection = this->knot->grabbed_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_grabbed_handler), this));
+ this->_mousedown_connection = this->knot->mousedown_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_mousedown_handler), this));
this->_ungrabbed_connection = this->knot->ungrabbed_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_ungrabbed_handler), this));
// add the initial draggable
@@ -1712,7 +1712,7 @@ GrDragger::~GrDragger()
this->_moved_connection.disconnect();
this->_clicked_connection.disconnect();
this->_doubleclicked_connection.disconnect();
- this->_grabbed_connection.disconnect();
+ this->_mousedown_connection.disconnect();
this->_ungrabbed_connection.disconnect();
/* unref should call destroy */
diff --git a/src/gradient-drag.h b/src/gradient-drag.h
index b4a64c78d..1fe2fb3f0 100644
--- a/src/gradient-drag.h
+++ b/src/gradient-drag.h
@@ -130,7 +130,7 @@ private:
sigc::connection _moved_connection;
sigc::connection _clicked_connection;
sigc::connection _doubleclicked_connection;
- sigc::connection _grabbed_connection;
+ sigc::connection _mousedown_connection;
sigc::connection _ungrabbed_connection;
};
diff --git a/src/id-clash.cpp b/src/id-clash.cpp
index c284843b8..162cb9e89 100644
--- a/src/id-clash.cpp
+++ b/src/id-clash.cpp
@@ -373,9 +373,9 @@ void rename_id(SPObject *elem, Glib::ustring const &new_name)
gchar *id = g_strdup(new_name.c_str()); //id is not empty here as new_name is check to be not empty
g_strcanon (id, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.:", '_');
Glib::ustring new_name2 = id; //will not fail as id can not be NULL, see length check on new_name
- g_free (id);
if (!isalnum (new_name2[0])) {
g_message("Invalid Id, will not change.");
+ g_free (id);
return;
}
@@ -396,7 +396,7 @@ void rename_id(SPObject *elem, Glib::ustring const &new_name)
break;
}
}
-
+ g_free (id);
// Change to the new ID
elem->getRepr()->setAttribute("id", new_name2);
// Make a note of this change, if we need to fix up refs to it
diff --git a/src/knot.cpp b/src/knot.cpp
index c04206dee..50fa73391 100644
--- a/src/knot.cpp
+++ b/src/knot.cpp
@@ -214,7 +214,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot
if ((event->button.button == 1) && knot->desktop && knot->desktop->event_context && !knot->desktop->event_context->space_panning) {
Geom::Point const p = knot->desktop->w2d(Geom::Point(event->button.x, event->button.y));
knot->startDragging(p, (gint) event->button.x, (gint) event->button.y, event->button.time);
- knot->grabbed_signal.emit(knot, event->button.state);
+ knot->mousedown_signal.emit(knot, event->button.state);
consumed = TRUE;
}
break;
@@ -241,7 +241,6 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot
if (moved) {
knot->setFlag(SP_KNOT_DRAGGING, FALSE);
-
knot->ungrabbed_signal.emit(knot, event->button.state);
} else {
knot->click_signal.emit(knot, event->button.state);
@@ -277,6 +276,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot
if (!moved) {
knot->setFlag(SP_KNOT_DRAGGING, TRUE);
+ knot->grabbed_signal.emit(knot, event->button.state);
}
sp_event_context_snap_delay_handler(knot->desktop->event_context, NULL, knot, (GdkEventMotion *)event, Inkscape::UI::Tools::DelayedSnapEvent::KNOT_HANDLER);
diff --git a/src/knot.h b/src/knot.h
index 954ddd8a6..0a0ce73d2 100644
--- a/src/knot.h
+++ b/src/knot.h
@@ -79,6 +79,7 @@ public:
// be able to figure that out
sigc::signal<void, SPKnot *, unsigned int> click_signal;
sigc::signal<void, SPKnot*, unsigned int> doubleclicked_signal;
+ sigc::signal<void, SPKnot*, unsigned int> mousedown_signal;
sigc::signal<void, SPKnot*, unsigned int> grabbed_signal;
sigc::signal<void, SPKnot *, unsigned int> ungrabbed_signal;
sigc::signal<void, SPKnot *, Geom::Point const &, unsigned int> moved_signal;
diff --git a/src/libcola/gradient_projection.cpp b/src/libcola/gradient_projection.cpp
index c8488a545..c079c3149 100644
--- a/src/libcola/gradient_projection.cpp
+++ b/src/libcola/gradient_projection.cpp
@@ -50,7 +50,7 @@ unsigned GradientProjection::solve(double * b) {
//cerr << "in gradient projection: n=" << n << endl;
for (i=0;i<n;i++) {
assert(!IS_NAN(place[i]));
- assert(!isinf(place[i]));
+ assert(IS_FINITE(place[i]));
vars[i]->desiredPosition=place[i];
}
try {
@@ -98,7 +98,7 @@ unsigned GradientProjection::solve(double * b) {
for (i=0; i<n; i++) {
place[i]-=alpha*g[i];
assert(!IS_NAN(place[i]));
- assert(!isinf(place[i]));
+ assert(IS_FINITE(place[i]));
vars[i]->desiredPosition=place[i];
}
for (DummyVars::iterator it=dummy_vars.begin();it!=dummy_vars.end();++it){
diff --git a/src/libcroco/cr-parser.c b/src/libcroco/cr-parser.c
index 544b35ab0..4e50b5402 100644
--- a/src/libcroco/cr-parser.c
+++ b/src/libcroco/cr-parser.c
@@ -2002,7 +2002,7 @@ cr_parser_parse_simple_sels (CRParser * a_this,
*Returns CR_OK upon successful completion, an error
*code otherwise.
*/
-static enum CRStatus
+enum CRStatus
cr_parser_parse_selector (CRParser * a_this,
CRSelector ** a_selector)
{
diff --git a/src/libcroco/cr-parser.h b/src/libcroco/cr-parser.h
index 6dce9439e..24cf5dfe8 100644
--- a/src/libcroco/cr-parser.h
+++ b/src/libcroco/cr-parser.h
@@ -97,6 +97,8 @@ enum CRStatus cr_parser_set_default_sac_handler (CRParser *a_this) ;
enum CRStatus cr_parser_parse_term (CRParser *a_this, CRTerm **a_term) ;
+enum CRStatus cr_parser_parse_selector (CRParser * a_this, CRSelector ** a_selector) ;
+
enum CRStatus cr_parser_parse_expr (CRParser *a_this, CRTerm **a_expr) ;
enum CRStatus cr_parser_parse_prio (CRParser *a_this, CRString **a_prio) ;
diff --git a/src/libcroco/cr-selector.c b/src/libcroco/cr-selector.c
index c56c43fda..f9193feda 100644
--- a/src/libcroco/cr-selector.c
+++ b/src/libcroco/cr-selector.c
@@ -51,6 +51,19 @@ cr_selector_new (CRSimpleSel * a_simple_sel)
return result;
}
+/**
+ * cr_selector_parse_from_buf:
+ *
+ *@a_char_buf: the buffer to parse.
+ *@a_enc: the encoding of the input buffer a_char_buf.
+ *
+ *Parses a buf for selectors.
+ *
+ *Fix Me: parsing will fail for some cases if buf does not end with '{'.
+ *
+ *Returns the newly built instance of #CRSelector, or
+ *NULL in case of failure.
+ */
CRSelector *
cr_selector_parse_from_buf (const guchar * a_char_buf, enum CREncoding a_enc)
{
@@ -62,7 +75,17 @@ cr_selector_parse_from_buf (const guchar * a_char_buf, enum CREncoding a_enc)
a_enc, FALSE);
g_return_val_if_fail (parser, NULL);
- return NULL;
+ CRSelector *selector = NULL;
+ enum CRStatus status = CR_OK;
+ status = cr_parser_parse_selector (parser, &selector);
+
+ if (status != CR_OK) {
+ if (selector) {
+ cr_selector_unref (selector);
+ selector = NULL;
+ }
+ }
+ return selector;
}
/**
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index 50e2aa353..f664870ec 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -430,6 +430,9 @@ void
Effect::processObjects(LpeAction lpe_action)
{
SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if (!document) {
+ return;
+ }
for (std::vector<const char *>::iterator el_it = items.begin();
el_it != items.end(); ++el_it) {
const char * id = *el_it;
diff --git a/src/live_effects/lpe-clone-original.cpp b/src/live_effects/lpe-clone-original.cpp
index 04db6171e..440af6f9c 100644
--- a/src/live_effects/lpe-clone-original.cpp
+++ b/src/live_effects/lpe-clone-original.cpp
@@ -64,6 +64,9 @@ void
LPECloneOriginal::cloneAttrbutes(SPObject *origin, SPObject *dest, bool live, const char * attributes, const char * style_attributes, bool root)
{
SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if (!document) {
+ return;
+ }
if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
std::vector< SPObject * > childs = origin->childList(true);
size_t index = 0;
@@ -344,7 +347,6 @@ void
LPECloneOriginal::transform_multiply(Geom::Affine const& postmul, bool set)
{
if (linked_item.linksToItem()) {
- bool changed = false;
linked_item.getObject()->requestModified(SP_OBJECT_MODIFIED_FLAG);
}
}
diff --git a/src/live_effects/lpe-copy_rotate.cpp b/src/live_effects/lpe-copy_rotate.cpp
index 42e055062..9ba680cd4 100644
--- a/src/live_effects/lpe-copy_rotate.cpp
+++ b/src/live_effects/lpe-copy_rotate.cpp
@@ -92,11 +92,13 @@ LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem)
{
if (split_items) {
SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if (!document) {
+ return;
+ }
items.clear();
container = dynamic_cast<SPObject *>(sp_lpe_item->parent);
- SPDocument * doc = SP_ACTIVE_DOCUMENT;
Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot();
- Inkscape::XML::Node *root_origin = doc->getReprRoot();
+ Inkscape::XML::Node *root_origin = document->getReprRoot();
if (root_origin != root) {
return;
}
@@ -168,15 +170,15 @@ LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem)
processObjects(LPE_ERASE);
items.clear();
}
-
- std::cout << previous_num_copies << "previous_num_copies\n";
- std::cout << num_copies << "num_copies\n";
}
void
LPECopyRotate::cloneD(SPObject *origin, SPObject *dest, bool root, bool reset)
{
SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if (!document) {
+ return;
+ }
Inkscape::XML::Document *xml_doc = document->getReprDoc();
if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
std::vector< SPObject * > childs = origin->childList(true);
@@ -190,7 +192,7 @@ LPECopyRotate::cloneD(SPObject *origin, SPObject *dest, bool root, bool reset)
}
SPShape * shape = SP_SHAPE(origin);
SPPath * path = SP_PATH(dest);
- if (!path && !SP_IS_GROUP(dest)) {
+ if (shape && !path) {
Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0);
dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL);
path = SP_PATH(dest);
@@ -219,6 +221,9 @@ void
LPECopyRotate::toItem(Geom::Affine transform, size_t i, bool reset)
{
SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if (!document) {
+ return;
+ }
Inkscape::XML::Document *xml_doc = document->getReprDoc();
const char * elemref_id = g_strdup(Glib::ustring("rotated-").append(std::to_string(i)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str());
items.push_back(elemref_id);
@@ -377,11 +382,11 @@ LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem)
num_copies.param_set_increments(1.0, 10.0);
}
- if (dist_angle_handle < 1.0) {
- dist_angle_handle = 1.0;
- }
A = Point(boundingbox_X.min(), boundingbox_Y.middle());
B = Point(boundingbox_X.middle(), boundingbox_Y.middle());
+ if (Geom::are_near(A, B, 0.01)) {
+ B += Geom::Point(1.0, 0.0);
+ }
dir = unit_vector(B - A);
// I first suspected the minus sign to be a bug in 2geom but it is
// likely due to SVG's choice of coordinate system orientation (max)
@@ -394,6 +399,9 @@ LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem)
dist_angle_handle = L2(starting_point - origin);
}
}
+ if (dist_angle_handle < 1.0) {
+ dist_angle_handle = 1.0;
+ }
start_pos = origin + dir * Rotate(-rad_from_deg(starting_angle)) * dist_angle_handle;
rot_pos = origin + dir * Rotate(-rad_from_deg(rotation_angle+starting_angle)) * dist_angle_handle;
near = Geom::are_near(start_pos, (Geom::Point)starting_point, 0.01);
diff --git a/src/live_effects/lpe-fill-between-many.cpp b/src/live_effects/lpe-fill-between-many.cpp
index 2690ce160..2e1fe0dc1 100644
--- a/src/live_effects/lpe-fill-between-many.cpp
+++ b/src/live_effects/lpe-fill-between-many.cpp
@@ -4,13 +4,13 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-#include <gtkmm/box.h>
#include "live_effects/lpe-fill-between-many.h"
#include "display/curve.h"
#include "sp-shape.h"
#include "sp-text.h"
+#include "svg/svg.h"
// TODO due to internal breakage in glibmm headers, this must be last:
#include <glibmm/i18n.h>
@@ -20,11 +20,17 @@ namespace LivePathEffect {
LPEFillBetweenMany::LPEFillBetweenMany(LivePathEffectObject *lpeobject) :
Effect(lpeobject),
linked_paths(_("Linked path:"), _("Paths from which to take the original path data"), "linkedpaths", &wr, this),
- fuse(_("Fuse coincident points"), _("Fuse coincident points"), "fuse", &wr, this)
+ fuse(_("Fuse coincident points"), _("Fuse coincident points"), "fuse", &wr, this, false),
+ allow_transforms(_("Allow transforms"), _("Allow transforms"), "allow_transforms", &wr, this, false),
+ join(_("Join subpaths"), _("Join subpaths"), "join", &wr, this, true),
+ close(_("Close"), _("Close path"), "close", &wr, this, true)
{
registerParameter( dynamic_cast<Parameter *>(&linked_paths) );
registerParameter( dynamic_cast<Parameter *>(&fuse) );
- //perceived_path = true;
+ registerParameter( dynamic_cast<Parameter *>(&allow_transforms) );
+ registerParameter( dynamic_cast<Parameter *>(&join) );
+ registerParameter( dynamic_cast<Parameter *>(&close) );
+ transformmultiply = false;
}
LPEFillBetweenMany::~LPEFillBetweenMany()
@@ -46,7 +52,7 @@ void LPEFillBetweenMany::doEffect (SPCurve * curve)
linked_path = (*iter)->_pathvector.front();
}
- if (!res_pathv.empty()) {
+ if (!res_pathv.empty() && join) {
linked_path = linked_path * SP_ITEM(obj)->getRelativeTransform(firstObj);
if (!are_near(res_pathv.front().finalPoint(), linked_path.initialPoint(), 0.01) || !fuse) {
res_pathv.front().appendNew<Geom::LineSegment>(linked_path.initialPoint());
@@ -56,19 +62,40 @@ void LPEFillBetweenMany::doEffect (SPCurve * curve)
res_pathv.front().append(linked_path);
} else {
firstObj = SP_ITEM(obj);
+ if (close && !join) {
+ linked_path.close();
+ }
res_pathv.push_back(linked_path);
}
}
}
- if (!res_pathv.empty()) {
+ if (!res_pathv.empty() && close) {
res_pathv.front().close();
}
if (res_pathv.empty()) {
res_pathv = curve->get_pathvector();
}
+ if(!allow_transforms && !transformmultiply) {
+ Geom::Affine affine = Geom::identity();
+ sp_svg_transform_read(SP_ITEM(sp_lpe_item)->getAttribute("transform"), &affine);
+ res_pathv *= affine.inverse();
+ }
+ if(transformmultiply) {
+ transformmultiply = false;
+ }
curve->set_pathvector(res_pathv);
}
+void
+LPEFillBetweenMany::transform_multiply(Geom::Affine const& postmul, bool set)
+{
+ if(!allow_transforms && sp_lpe_item) {
+ SP_ITEM(sp_lpe_item)->transform *= postmul.inverse();
+ transformmultiply = true;
+ sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
+ }
+}
+
} // namespace LivePathEffect
} /* namespace Inkscape */
diff --git a/src/live_effects/lpe-fill-between-many.h b/src/live_effects/lpe-fill-between-many.h
index 552c8037d..fe824e936 100644
--- a/src/live_effects/lpe-fill-between-many.h
+++ b/src/live_effects/lpe-fill-between-many.h
@@ -19,13 +19,16 @@ class LPEFillBetweenMany : public Effect {
public:
LPEFillBetweenMany(LivePathEffectObject *lpeobject);
virtual ~LPEFillBetweenMany();
-
+ virtual void transform_multiply(Geom::Affine const& postmul, bool set);
virtual void doEffect (SPCurve * curve);
private:
OriginalPathArrayParam linked_paths;
BoolParam fuse;
-
+ BoolParam allow_transforms;
+ BoolParam join;
+ BoolParam close;
+ bool transformmultiply;
private:
LPEFillBetweenMany(const LPEFillBetweenMany&);
LPEFillBetweenMany& operator=(const LPEFillBetweenMany&);
diff --git a/src/live_effects/lpe-fill-between-strokes.cpp b/src/live_effects/lpe-fill-between-strokes.cpp
index 8dc55357f..43fef4288 100644
--- a/src/live_effects/lpe-fill-between-strokes.cpp
+++ b/src/live_effects/lpe-fill-between-strokes.cpp
@@ -8,6 +8,7 @@
#include "display/curve.h"
#include "sp-shape.h"
#include "sp-text.h"
+#include "svg/svg.h"
// TODO due to internal breakage in glibmm headers, this must be last:
#include <glibmm/i18n.h>
@@ -19,15 +20,19 @@ LPEFillBetweenStrokes::LPEFillBetweenStrokes(LivePathEffectObject *lpeobject) :
linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this),
second_path(_("Second path:"), _("Second path from which to take the original path data"), "secondpath", &wr, this),
reverse_second(_("Reverse Second"), _("Reverses the second path order"), "reversesecond", &wr, this),
- close(_("Close path"), _("Close path"), "close", &wr, this),
- fuse(_("Fuse coincident points"), _("Fuse coincident points"), "fuse", &wr, this)
+ fuse(_("Fuse coincident points"), _("Fuse coincident points"), "fuse", &wr, this, false),
+ allow_transforms(_("Allow transforms"), _("Allow transforms"), "allow_transforms", &wr, this, false),
+ join(_("Join subpaths"), _("Join subpaths"), "join", &wr, this, true),
+ close(_("Close"), _("Close path"), "close", &wr, this, true)
{
registerParameter( dynamic_cast<Parameter *>(&linked_path) );
registerParameter( dynamic_cast<Parameter *>(&second_path) );
registerParameter( dynamic_cast<Parameter *>(&reverse_second) );
- registerParameter( dynamic_cast<Parameter *>(&close) );
registerParameter( dynamic_cast<Parameter *>(&fuse) );
- //perceived_path = true;
+ registerParameter( dynamic_cast<Parameter *>(&allow_transforms) );
+ registerParameter( dynamic_cast<Parameter *>(&join) );
+ registerParameter( dynamic_cast<Parameter *>(&close) );
+ transformmultiply = false;
}
LPEFillBetweenStrokes::~LPEFillBetweenStrokes()
@@ -38,6 +43,13 @@ LPEFillBetweenStrokes::~LPEFillBetweenStrokes()
void LPEFillBetweenStrokes::doEffect (SPCurve * curve)
{
if (curve) {
+ Geom::Affine affine = Geom::identity();
+ if(!allow_transforms && !transformmultiply) {
+ sp_svg_transform_read(SP_ITEM(sp_lpe_item)->getAttribute("transform"), &affine);
+ }
+ if(transformmultiply) {
+ transformmultiply = false;
+ }
if ( linked_path.linksToPath() && second_path.linksToPath() && linked_path.getObject() && second_path.getObject() ) {
Geom::PathVector linked_pathv = linked_path.get_pathvector();
Geom::PathVector second_pathv = second_path.get_pathvector();
@@ -58,19 +70,30 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve)
if (reverse_second.get_value()) {
result_second_pathv.front() = result_second_pathv.front().reversed();
}
- if (!are_near(result_linked_pathv.front().finalPoint(), result_second_pathv.front().initialPoint(),0.01) || !fuse) {
- result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().initialPoint());
+ if (join) {
+ if (!are_near(result_linked_pathv.front().finalPoint(), result_second_pathv.front().initialPoint(),0.01) || !fuse) {
+ result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().initialPoint());
+ } else {
+ result_second_pathv.front().setInitial(result_linked_pathv.front().finalPoint());
+ }
+ result_linked_pathv.front().append(result_second_pathv.front());
+ if (close) {
+ result_linked_pathv.front().close();
+ }
} else {
- result_second_pathv.front().setInitial(result_linked_pathv.front().finalPoint());
- }
- result_linked_pathv.front().append(result_second_pathv.front());
- if (close) {
- result_linked_pathv.front().close();
+ if (close) {
+ result_linked_pathv.front().close();
+ result_second_pathv.front().close();
+ }
+ result_linked_pathv.push_back(result_second_pathv.front());
}
+ result_linked_pathv *= affine.inverse();
curve->set_pathvector(result_linked_pathv);
} else if ( !result_linked_pathv.empty() ) {
+ result_linked_pathv *= affine.inverse();
curve->set_pathvector(result_linked_pathv);
} else if ( !result_second_pathv.empty() ) {
+ result_second_pathv *= affine.inverse();
curve->set_pathvector(result_second_pathv);
}
}
@@ -83,6 +106,10 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve)
result_pathv.push_back((*iter));
}
if ( !result_pathv.empty() ) {
+ result_pathv *= affine.inverse();
+ if (close) {
+ result_pathv.front().close();
+ }
curve->set_pathvector(result_pathv);
}
}
@@ -95,12 +122,26 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve)
result_pathv.push_back((*iter));
}
if ( !result_pathv.empty() ) {
+ result_pathv *= affine.inverse();
+ if (close) {
+ result_pathv.front().close();
+ }
curve->set_pathvector(result_pathv);
}
}
}
}
+void
+LPEFillBetweenStrokes::transform_multiply(Geom::Affine const& postmul, bool set)
+{
+ if(!allow_transforms && sp_lpe_item) {
+ SP_ITEM(sp_lpe_item)->transform *= postmul.inverse();
+ transformmultiply = true;
+ sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
+ }
+}
+
} // namespace LivePathEffect
} /* namespace Inkscape */
diff --git a/src/live_effects/lpe-fill-between-strokes.h b/src/live_effects/lpe-fill-between-strokes.h
index 14eb9167c..5bbd6e7da 100644
--- a/src/live_effects/lpe-fill-between-strokes.h
+++ b/src/live_effects/lpe-fill-between-strokes.h
@@ -19,15 +19,18 @@ class LPEFillBetweenStrokes : public Effect {
public:
LPEFillBetweenStrokes(LivePathEffectObject *lpeobject);
virtual ~LPEFillBetweenStrokes();
-
+ virtual void transform_multiply(Geom::Affine const& postmul, bool set);
virtual void doEffect (SPCurve * curve);
private:
OriginalPathParam linked_path;
OriginalPathParam second_path;
BoolParam reverse_second;
- BoolParam close;
BoolParam fuse;
+ BoolParam allow_transforms;
+ BoolParam join;
+ BoolParam close;
+ bool transformmultiply;
private:
LPEFillBetweenStrokes(const LPEFillBetweenStrokes&);
diff --git a/src/live_effects/lpe-measure-line.cpp b/src/live_effects/lpe-measure-line.cpp
index ef87be81c..625db150b 100644
--- a/src/live_effects/lpe-measure-line.cpp
+++ b/src/live_effects/lpe-measure-line.cpp
@@ -171,6 +171,9 @@ void
LPEMeasureLine::createArrowMarker(const char * mode)
{
SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if (!document) {
+ return;
+ }
Inkscape::XML::Document *xml_doc = document->getReprDoc();
SPObject *elemref = NULL;
Inkscape::XML::Node *arrow = NULL;
@@ -232,6 +235,9 @@ void
LPEMeasureLine::createTextLabel(Geom::Point pos, double length, Geom::Coord angle, bool remove, bool valid)
{
SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if (!document) {
+ return;
+ }
Inkscape::XML::Document *xml_doc = document->getReprDoc();
Inkscape::XML::Node *rtext = NULL;
double doc_w = document->getRoot()->width.value;
@@ -376,6 +382,9 @@ void
LPEMeasureLine::createLine(Geom::Point start,Geom::Point end, const char * id, bool main, bool overflow, bool remove, bool arrows)
{
SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if (!document) {
+ return;
+ }
Inkscape::XML::Document *xml_doc = document->getReprDoc();
SPObject *elemref = NULL;
Inkscape::XML::Node *line = NULL;
@@ -500,6 +509,9 @@ LPEMeasureLine::doBeforeEffect (SPLPEItem const* lpeitem)
SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem);
sp_lpe_item->parent = dynamic_cast<SPObject *>(splpeitem->parent);
SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if (!document) {
+ return;
+ }
Inkscape::XML::Node *root = splpeitem->document->getReprRoot();
Inkscape::XML::Node *root_origin = document->getReprRoot();
if (root_origin != root) {
diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp
index 7f0a93c52..c31ce7c01 100644
--- a/src/live_effects/lpe-mirror_symmetry.cpp
+++ b/src/live_effects/lpe-mirror_symmetry.cpp
@@ -87,11 +87,14 @@ LPEMirrorSymmetry::~LPEMirrorSymmetry()
void
LPEMirrorSymmetry::doAfterEffect (SPLPEItem const* lpeitem)
{
+ SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if (!document) {
+ return;
+ }
if (split_items && !discard_orig_path) {
container = dynamic_cast<SPObject *>(sp_lpe_item->parent);
- SPDocument * doc = SP_ACTIVE_DOCUMENT;
Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot();
- Inkscape::XML::Node *root_origin = doc->getReprRoot();
+ Inkscape::XML::Node *root_origin = document->getReprRoot();
if (root_origin != root) {
return;
}
@@ -161,20 +164,24 @@ LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem)
}
} else if ( mode == MT_V){
SPDocument * document = SP_ACTIVE_DOCUMENT;
- Geom::Affine transform = i2anc_affine(SP_OBJECT(lpeitem), NULL).inverse();
- Geom::Point sp = Geom::Point(document->getWidth().value("px")/2.0, 0) * transform;
- start_point.param_setValue(sp, true);
- Geom::Point ep = Geom::Point(document->getWidth().value("px")/2.0, document->getHeight().value("px")) * transform;
- end_point.param_setValue(ep, true);
- center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point), true);
+ if (document) {
+ Geom::Affine transform = i2anc_affine(SP_OBJECT(lpeitem), NULL).inverse();
+ Geom::Point sp = Geom::Point(document->getWidth().value("px")/2.0, 0) * transform;
+ start_point.param_setValue(sp, true);
+ Geom::Point ep = Geom::Point(document->getWidth().value("px")/2.0, document->getHeight().value("px")) * transform;
+ end_point.param_setValue(ep, true);
+ center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point), true);
+ }
} else { //horizontal page
SPDocument * document = SP_ACTIVE_DOCUMENT;
- Geom::Affine transform = i2anc_affine(SP_OBJECT(lpeitem), NULL).inverse();
- Geom::Point sp = Geom::Point(0, document->getHeight().value("px")/2.0) * transform;
- start_point.param_setValue(sp, true);
- Geom::Point ep = Geom::Point(document->getWidth().value("px"), document->getHeight().value("px")/2.0) * transform;
- end_point.param_setValue(ep, true);
- center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point), true);
+ if (document) {
+ Geom::Affine transform = i2anc_affine(SP_OBJECT(lpeitem), NULL).inverse();
+ Geom::Point sp = Geom::Point(0, document->getHeight().value("px")/2.0) * transform;
+ start_point.param_setValue(sp, true);
+ Geom::Point ep = Geom::Point(document->getWidth().value("px"), document->getHeight().value("px")/2.0) * transform;
+ end_point.param_setValue(ep, true);
+ center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point), true);
+ }
}
previous_center = center_point;
}
@@ -183,6 +190,9 @@ void
LPEMirrorSymmetry::cloneD(SPObject *origin, SPObject *dest, bool live, bool root)
{
SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if (!document) {
+ return;
+ }
Inkscape::XML::Document *xml_doc = document->getReprDoc();
if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
std::vector< SPObject * > childs = origin->childList(true);
@@ -196,7 +206,7 @@ LPEMirrorSymmetry::cloneD(SPObject *origin, SPObject *dest, bool live, bool root
}
SPShape * shape = SP_SHAPE(origin);
SPPath * path = SP_PATH(dest);
- if (!path && !SP_IS_GROUP(dest)) {
+ if (shape && !path) {
Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0);
dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL);
path = SP_PATH(dest);
@@ -226,67 +236,69 @@ void
LPEMirrorSymmetry::toMirror(Geom::Affine transform)
{
SPDocument * document = SP_ACTIVE_DOCUMENT;
- Inkscape::XML::Document *xml_doc = document->getReprDoc();
- const char * id_origin_char = id_origin.param_getSVGValue();
- const char * elemref_id = g_strdup(Glib::ustring("mirror-").append(id_origin_char).c_str());
- items.clear();
- items.push_back(elemref_id);
- SPObject *elemref= NULL;
- Inkscape::XML::Node *phantom = NULL;
- if (elemref = document->getObjectById(elemref_id)) {
- phantom = elemref->getRepr();
- } else {
- phantom = sp_lpe_item->getRepr()->duplicate(xml_doc);
- std::vector<const char *> attrs;
- attrs.push_back("inkscape:path-effect");
- attrs.push_back("inkscape:original-d");
- attrs.push_back("sodipodi:type");
- attrs.push_back("sodipodi:rx");
- attrs.push_back("sodipodi:ry");
- attrs.push_back("sodipodi:cx");
- attrs.push_back("sodipodi:cy");
- attrs.push_back("sodipodi:end");
- attrs.push_back("sodipodi:start");
- attrs.push_back("inkscape:flatsided");
- attrs.push_back("inkscape:randomized");
- attrs.push_back("inkscape:rounded");
- attrs.push_back("sodipodi:arg1");
- attrs.push_back("sodipodi:arg2");
- attrs.push_back("sodipodi:r1");
- attrs.push_back("sodipodi:r2");
- attrs.push_back("sodipodi:sides");
- attrs.push_back("inkscape:randomized");
- attrs.push_back("sodipodi:argument");
- attrs.push_back("sodipodi:expansion");
- attrs.push_back("sodipodi:radius");
- attrs.push_back("sodipodi:revolution");
- attrs.push_back("sodipodi:t0");
- attrs.push_back("inkscape:randomized");
- attrs.push_back("inkscape:randomized");
- attrs.push_back("inkscape:randomized");
- attrs.push_back("x");
- attrs.push_back("y");
- attrs.push_back("rx");
- attrs.push_back("ry");
- attrs.push_back("width");
- attrs.push_back("height");
- for(const char * attr : attrs) {
- phantom->setAttribute(attr, NULL);
+ if (document) {
+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
+ const char * id_origin_char = id_origin.param_getSVGValue();
+ const char * elemref_id = g_strdup(Glib::ustring("mirror-").append(id_origin_char).c_str());
+ items.clear();
+ items.push_back(elemref_id);
+ SPObject *elemref= NULL;
+ Inkscape::XML::Node *phantom = NULL;
+ if (elemref = document->getObjectById(elemref_id)) {
+ phantom = elemref->getRepr();
+ } else {
+ phantom = sp_lpe_item->getRepr()->duplicate(xml_doc);
+ std::vector<const char *> attrs;
+ attrs.push_back("inkscape:path-effect");
+ attrs.push_back("inkscape:original-d");
+ attrs.push_back("sodipodi:type");
+ attrs.push_back("sodipodi:rx");
+ attrs.push_back("sodipodi:ry");
+ attrs.push_back("sodipodi:cx");
+ attrs.push_back("sodipodi:cy");
+ attrs.push_back("sodipodi:end");
+ attrs.push_back("sodipodi:start");
+ attrs.push_back("inkscape:flatsided");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("inkscape:rounded");
+ attrs.push_back("sodipodi:arg1");
+ attrs.push_back("sodipodi:arg2");
+ attrs.push_back("sodipodi:r1");
+ attrs.push_back("sodipodi:r2");
+ attrs.push_back("sodipodi:sides");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("sodipodi:argument");
+ attrs.push_back("sodipodi:expansion");
+ attrs.push_back("sodipodi:radius");
+ attrs.push_back("sodipodi:revolution");
+ attrs.push_back("sodipodi:t0");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("x");
+ attrs.push_back("y");
+ attrs.push_back("rx");
+ attrs.push_back("ry");
+ attrs.push_back("width");
+ attrs.push_back("height");
+ for(const char * attr : attrs) {
+ phantom->setAttribute(attr, NULL);
+ }
+ }
+ phantom->setAttribute("id", elemref_id);
+ if (!elemref) {
+ elemref = container->appendChildRepr(phantom);
+ Inkscape::GC::release(phantom);
+ }
+ cloneD(SP_OBJECT(sp_lpe_item), elemref, true, true);
+ elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform));
+ if (elemref->parent != container) {
+ Inkscape::XML::Node *copy = phantom->duplicate(xml_doc);
+ copy->setAttribute("id", elemref_id);
+ container->appendChildRepr(copy);
+ Inkscape::GC::release(copy);
+ elemref->deleteObject();
}
- }
- phantom->setAttribute("id", elemref_id);
- if (!elemref) {
- elemref = container->appendChildRepr(phantom);
- Inkscape::GC::release(phantom);
- }
- cloneD(SP_OBJECT(sp_lpe_item), elemref, true, true);
- elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform));
- if (elemref->parent != container) {
- Inkscape::XML::Node *copy = phantom->duplicate(xml_doc);
- copy->setAttribute("id", elemref_id);
- container->appendChildRepr(copy);
- Inkscape::GC::release(copy);
- elemref->deleteObject();
}
}
diff --git a/src/live_effects/parameter/originalpath.cpp b/src/live_effects/parameter/originalpath.cpp
index f7eb48b7a..1e78f7fe1 100644
--- a/src/live_effects/parameter/originalpath.cpp
+++ b/src/live_effects/parameter/originalpath.cpp
@@ -89,7 +89,7 @@ OriginalPathParam::linked_modified_callback(SPObject *linked_obj, guint /*flags*
{
SPCurve *curve = NULL;
if (SP_IS_SHAPE(linked_obj)) {
- curve = SP_SHAPE(linked_obj)->getCurveBeforeLPE();
+ curve = SP_SHAPE(linked_obj)->getCurve();
}
if (SP_IS_TEXT(linked_obj)) {
curve = SP_TEXT(linked_obj)->getNormalizedBpath();
diff --git a/src/live_effects/parameter/originalpatharray.cpp b/src/live_effects/parameter/originalpatharray.cpp
index 083abc94c..693821ed2 100644
--- a/src/live_effects/parameter/originalpatharray.cpp
+++ b/src/live_effects/parameter/originalpatharray.cpp
@@ -386,7 +386,7 @@ void OriginalPathArrayParam::setPathVector(SPObject *linked_obj, guint /*flags*/
}
SPCurve *curve = NULL;
if (SP_IS_SHAPE(linked_obj)) {
- curve = SP_SHAPE(linked_obj)->getCurveBeforeLPE();
+ curve = SP_SHAPE(linked_obj)->getCurve();
}
if (SP_IS_TEXT(linked_obj)) {
curve = SP_TEXT(linked_obj)->getNormalizedBpath();
diff --git a/src/main.cpp b/src/main.cpp
index 47cf43456..8be9e2e44 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -691,18 +691,22 @@ main(int argc, char **argv)
RegistryTool rt;
rt.setPathInfo();
}
-#elif defined(ENABLE_NLS)
-# ifdef ENABLE_BINRELOC
+
+ // disable "client side decorations" as they prevent window borders and titlebars to be drawn with native theming
+ // see also https://bugzilla.gnome.org/show_bug.cgi?id=778791
+ g_setenv("GTK_CSD", "0", FALSE);
+#endif
+
+#ifdef ENABLE_NLS
+# ifndef WIN32
+# ifdef ENABLE_BINRELOC
bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
-# else
+# else
bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
// needed by Python/Gettext
g_setenv("PACKAGE_LOCALE_DIR", PACKAGE_LOCALE_DIR, TRUE);
+# endif
# endif
-#endif
-
- // the bit below compiles regardless of platform
-#ifdef ENABLE_NLS
// Allow the user to override the locale directory by setting
// the environment variable INKSCAPE_LOCALEDIR.
char const *inkscape_localedir = g_getenv("INKSCAPE_LOCALEDIR");
diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp
index 70307579f..f2bdeba4e 100644
--- a/src/selection-chemistry.cpp
+++ b/src/selection-chemistry.cpp
@@ -1618,7 +1618,7 @@ void ObjectSet::applyAffine(Geom::Affine const &affine, bool set_i2d, bool compe
for (auto& itm: region.children) {
SPUse *use = dynamic_cast<SPUse *>(&itm);
if ( use ) {
- use->doWriteTransform(use->getRepr(), use->transform.inverse(), NULL, compensate);
+ use->doWriteTransform(use->getRepr(), item->transform.inverse(), NULL, compensate);
}
}
}
diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp
index d430f6321..166e90238 100644
--- a/src/sp-namedview.cpp
+++ b/src/sp-namedview.cpp
@@ -754,7 +754,7 @@ void SPNamedView::show(SPDesktop *desktop)
namespace {
-gint const MIN_ONSCREEN_DISTANCE = 50;
+gint const MIN_ONSCREEN_DISTANCE = 100;
gdouble const NEWDOC_X_SCALE = 0.75;
gdouble const NEWDOC_Y_SCALE = NEWDOC_X_SCALE;
@@ -774,12 +774,6 @@ Geom::Point calcAnchorPoint(gint const x, gint const y,
} // namespace
-void SPNamedView::writeNewGrid(SPDocument *document,int gridtype)
-{
- g_assert(this->getRepr() != NULL);
- Inkscape::CanvasGrid::writeNewGridToRepr(this->getRepr(),document,static_cast<Inkscape::GridType>(gridtype));
-}
-
/*
* Restores window geometry from the document settings or defaults in prefs
*/
@@ -788,22 +782,42 @@ void sp_namedview_window_from_document(SPDesktop *desktop)
SPNamedView *nv = desktop->namedview;
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
bool geometry_from_file = (1 == prefs->getInt("/options/savewindowgeometry/value", 0));
+ bool geometry_from_last = (2 == prefs->getInt("/options/savewindowgeometry/value", 0));
gint default_geometry = prefs->getInt("/options/defaultwindowsize/value", 1);
bool new_document = (nv->window_width <= 0) || (nv->window_height <= 0);
bool show_dialogs = true;
// restore window size and position stored with the document
- bool sizeSet = false;
-
- if ((geometry_from_file && nv->window_maximized) || (new_document && (default_geometry == 2))) {
+ if (geometry_from_last) {
+ // do nothing, as we already have code for that in interface.cpp
+ // TODO: Probably should not do similar things in two places
+ } else if ((geometry_from_file && nv->window_maximized) || (new_document && (default_geometry == 2))) {
Gtk::Window *win = desktop->getToplevel();
if (win) {
win->maximize();
}
- sizeSet = true;
- } else if (geometry_from_file && !nv->window_maximized) {
- gint w = MIN(gdk_screen_width(), nv->window_width);
- gint h = MIN(gdk_screen_height(), nv->window_height);
+ } else {
+ // gdk_screen_width() / gdk_screen_height() return the dimensions of all displays combined
+ // therefore we have to get the dimensions of one monitor explicitly (currently the primary monitor)
+ // TODO: account for multi-monitor setups (i.e. on which monitor do we want to display Inkscape?)
+ gint monitor_number;
+ GdkRectangle monitor_geometry;
+ monitor_number = gdk_screen_get_primary_monitor(gdk_screen_get_default());
+ gdk_screen_get_monitor_geometry(gdk_screen_get_default(), monitor_number, &monitor_geometry);
+
+ gint w = monitor_geometry.width;
+ gint h = monitor_geometry.height;
+ bool move_to_screen = false;
+ if (geometry_from_file and !new_document) {
+ w = MIN(w, nv->window_width);
+ h = MIN(h, nv->window_height);
+ move_to_screen = true;
+ } else if (default_geometry == 1) {
+ w *= NEWDOC_X_SCALE;
+ h *= NEWDOC_Y_SCALE;
+ } else if (default_geometry == 0) {
+ w = h = 0; // use the smallest possible window size; could be a factor like NEWDOC_X_SCALE in future
+ }
if ((w > 0) && (h > 0)) {
#ifndef WIN32
gint dx= 0;
@@ -817,22 +831,14 @@ void sp_namedview_window_from_document(SPDesktop *desktop)
show_dialogs = FALSE;
}
#endif
- Geom::Point origin = calcAnchorPoint(nv->window_x, nv->window_y, w, h, MIN_ONSCREEN_DISTANCE);
desktop->setWindowSize(w, h);
- desktop->setWindowPosition(origin);
- sizeSet = true;
+ if (move_to_screen) {
+ Geom::Point origin = calcAnchorPoint(nv->window_x, nv->window_y, w, h, MIN_ONSCREEN_DISTANCE);
+ desktop->setWindowPosition(origin);
+ }
}
}
- if (!sizeSet && new_document && (default_geometry == 1))
- {
- gint w = gdk_screen_width() * NEWDOC_X_SCALE;
- gint h = gdk_screen_height() * NEWDOC_Y_SCALE;
- Geom::Point origin = calcAnchorPoint(nv->window_x, nv->window_y, w, h, MIN_ONSCREEN_DISTANCE);
- desktop->setWindowSize(w, h);
- desktop->setWindowPosition(origin);
- }
-
// restore zoom and view
if (nv->zoom != 0 && nv->zoom != HUGE_VAL && !IS_NAN(nv->zoom)
&& nv->cx != HUGE_VAL && !IS_NAN(nv->cx)
@@ -850,6 +856,12 @@ void sp_namedview_window_from_document(SPDesktop *desktop)
}
}
+void SPNamedView::writeNewGrid(SPDocument *document,int gridtype)
+{
+ g_assert(this->getRepr() != NULL);
+ Inkscape::CanvasGrid::writeNewGridToRepr(this->getRepr(),document,static_cast<Inkscape::GridType>(gridtype));
+}
+
bool SPNamedView::getSnapGlobal() const
{
return this->snap_manager.snapprefs.getSnapEnabledGlobally();
diff --git a/src/sp-object.cpp b/src/sp-object.cpp
index 7807703f6..9f15935ac 100644
--- a/src/sp-object.cpp
+++ b/src/sp-object.cpp
@@ -1105,7 +1105,10 @@ Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML
* possibly we should write property attributes instead of a style
* attribute.
*/
- sp_style_unset_property_attrs (this);
+ // With the changes to preserves style source this is no longer needed
+ // and the above comment no longer applies. I leave it here until these
+ // change are well tested.
+ // sp_style_unset_property_attrs (this);
}
#ifdef OBJECT_TRACE
diff --git a/src/sp-path.cpp b/src/sp-path.cpp
index b593b7937..c6ec5559e 100644
--- a/src/sp-path.cpp
+++ b/src/sp-path.cpp
@@ -292,9 +292,14 @@ Geom::Affine SPPath::set_transform(Geom::Affine const &transform) {
// Transform the original-d path if this is a valid LPE this, other else the (ordinary) path
if (_curve_before_lpe && hasPathEffectRecursive()) {
- if (this->hasPathEffectOfType(Inkscape::LivePathEffect::CLONE_ORIGINAL) || this->hasPathEffectOfType(Inkscape::LivePathEffect::BEND_PATH)) {
+ if (this->hasPathEffectOfType(Inkscape::LivePathEffect::CLONE_ORIGINAL) ||
+ this->hasPathEffectOfType(Inkscape::LivePathEffect::BEND_PATH) ||
+ this->hasPathEffectOfType(Inkscape::LivePathEffect::FILL_BETWEEN_MANY) ||
+ this->hasPathEffectOfType(Inkscape::LivePathEffect::FILL_BETWEEN_STROKES) )
+ {
// if path has the CLONE_ORIGINAL LPE applied, don't write the transform to the pathdata, but write it 'unoptimized'
// also if the effect is type BEND PATH to fix bug #179842
+ this->adjust_livepatheffect(transform);
return transform;
} else {
_curve_before_lpe->transform(transform);
diff --git a/src/splivarot.cpp b/src/splivarot.cpp
index 531a48e44..22efe38ed 100644
--- a/src/splivarot.cpp
+++ b/src/splivarot.cpp
@@ -1196,13 +1196,19 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy)
// remember old stroke style, to be set on fill
SPStyle *i_style = item->style;
//Stroke - and markers
+ gchar const *opacity;
+ gchar const *filter;
+
SPCSSAttr *ncss = 0;
{
ncss = sp_css_attr_from_style(i_style, SP_STYLE_FLAG_ALWAYS);
gchar const *s_val = sp_repr_css_property(ncss, "stroke", NULL);
gchar const *s_opac = sp_repr_css_property(ncss, "stroke-opacity", NULL);
-
+ opacity = sp_repr_css_property(ncss, "opacity", NULL);
+ filter = sp_repr_css_property(ncss, "filter", NULL);
sp_repr_css_set_property(ncss, "stroke", "none");
+ sp_repr_css_set_property(ncss, "filter", NULL);
+ sp_repr_css_set_property(ncss, "opacity", NULL);
sp_repr_css_set_property(ncss, "stroke-opacity", "1.0");
sp_repr_css_set_property(ncss, "fill", s_val);
if ( s_opac ) {
@@ -1220,6 +1226,8 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy)
ncsf = sp_css_attr_from_style(i_style, SP_STYLE_FLAG_ALWAYS);
sp_repr_css_set_property(ncsf, "stroke", "none");
sp_repr_css_set_property(ncsf, "stroke-opacity", "1.0");
+ sp_repr_css_set_property(ncsf, "filter", NULL);
+ sp_repr_css_set_property(ncsf, "opacity", NULL);
sp_repr_css_unset_property(ncsf, "marker-start");
sp_repr_css_unset_property(ncsf, "marker-mid");
sp_repr_css_unset_property(ncsf, "marker-end");
@@ -1504,7 +1512,11 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy)
gchar const *paint_order = sp_repr_css_property(ncss, "paint-order", NULL);
SPIPaintOrder temp;
temp.read( paint_order );
- if (temp.layer[0] != SP_CSS_PAINT_ORDER_NORMAL && !legacy) {
+ bool unique = false;
+ if ((!fill && !markers) || (!fill && !stroke) || (!markers && !stroke)) {
+ unique = true;
+ }
+ if (temp.layer[0] != SP_CSS_PAINT_ORDER_NORMAL && !legacy && !unique) {
if (temp.layer[0] == SP_CSS_PAINT_ORDER_FILL) {
if (temp.layer[1] == SP_CSS_PAINT_ORDER_STROKE) {
@@ -1574,7 +1586,7 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy)
}
}
- } else {
+ } else if (!unique) {
if ( fill ) {
g_repr->appendChild(fill);
}
@@ -1588,35 +1600,41 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy)
if( fill || stroke || markers ) {
did = true;
}
+
Inkscape::XML::Node *out = NULL;
- if (!fill && !markers) {
+ if (!fill && !markers && did) {
out = stroke;
- parent->mergeFrom(g_repr, "");
- parent->removeChild(g_repr);
- } else if (!fill && !stroke) {
+ } else if (!fill && !stroke && did) {
out = markers;
- parent->mergeFrom(g_repr, "");
- parent->removeChild(g_repr);
- } else if (!markers && !stroke) {
+ } else if (!markers && !stroke && did) {
out = fill;
- parent->mergeFrom(g_repr, "");
- parent->removeChild(g_repr);
- } else {
+ } else if(did) {
out = g_repr;
}
-
+ SPCSSAttr *r_style = sp_repr_css_attr_new();
+ sp_repr_css_set_property(r_style, "opacity", opacity);
+ sp_repr_css_set_property(r_style, "filter", filter);
+ sp_repr_css_change(out, r_style, "style");
+ sp_repr_css_attr_unref(r_style);
+ if (unique) {
+ parent->appendChild(out);
+ parent->removeChild(g_repr);
+ out->setPosition(pos > 0 ? pos : 0);
+ }
+ out->setAttribute("transform", item->getRepr()->attribute("transform"));
//bug lp:1290573 : completely destroy the old object first
curve->unref();
//Check for recursive markers to path
- if( selection->includes(item) ){
- selection->remove(item);
- item->deleteObject(false);
- selection->add(out);
- } else {
- item->deleteObject(false);
+ if (did) {
+ if( selection->includes(item) ){
+ selection->remove(item);
+ item->deleteObject(false);
+ selection->add(out);
+ } else {
+ item->deleteObject(false);
+ }
+ Inkscape::GC::release(g_repr);
}
- Inkscape::GC::release(g_repr);
-
}
}
diff --git a/src/style-internal.h b/src/style-internal.h
index 69b03f503..154538833 100644
--- a/src/style-internal.h
+++ b/src/style-internal.h
@@ -121,7 +121,7 @@ public:
inherits(inherits),
set(false),
inherit(false),
- style_src(SP_STYLE_SRC_UNSET),
+ style_src(SP_STYLE_SRC_STYLE_PROP), // Default to property, see bug 1662285.
style(NULL)
{}
@@ -132,7 +132,9 @@ public:
virtual void readIfUnset( gchar const *str, SPStyleSrc const &source = SP_STYLE_SRC_STYLE_PROP ) {
if ( !set ) {
read( str );
- style_src = source;
+ if ( set ) {
+ style_src = source;
+ }
}
}
diff --git a/src/ui/dialog/aboutbox.cpp b/src/ui/dialog/aboutbox.cpp
index 40b948a23..805bfb562 100644
--- a/src/ui/dialog/aboutbox.cpp
+++ b/src/ui/dialog/aboutbox.cpp
@@ -115,7 +115,7 @@ AboutBox::AboutBox() : Gtk::Dialog(_("About Inkscape")) {
Gtk::Label *link = new Gtk::Label();
const gchar *website_link =
- "<a href=\"https://www.inkscape.org\"> https://www.inkscape.org</a>";
+ "<a href=\"https://www.inkscape.org\">https://www.inkscape.org</a>";
link->set_markup(website_link);
link->set_alignment(Gtk::ALIGN_END);
diff --git a/src/ui/dialog/cssdialog.cpp b/src/ui/dialog/cssdialog.cpp
index fa266b012..a5736513f 100644
--- a/src/ui/dialog/cssdialog.cpp
+++ b/src/ui/dialog/cssdialog.cpp
@@ -3,8 +3,10 @@
*/
/* Authors:
* Kamalpreet Kaur Grewal
+ * Tavmjong Bah
*
* Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr>
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -22,23 +24,6 @@ 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. '-'
@@ -53,18 +38,17 @@ CssDialog::CssDialog():
{
set_size_request(20, 15);
_mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET);
- _treeView.set_headers_visible(false);
+ _treeView.set_headers_visible(true);
_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());
+ Inkscape::UI::Widget::AddToIcon * addRenderer = manage(new Inkscape::UI::Widget::AddToIcon());
addRenderer->property_active() = false;
- int addCol = _treeView.append_column("Unset Property", *addRenderer) - 1;
+ int addCol = _treeView.append_column("", *addRenderer) - 1;
Gtk::TreeViewColumn *col = _treeView.get_column(addCol);
if (col) {
col->add_attribute(addRenderer->property_active(), _cssColumns._colUnsetProp);
@@ -72,8 +56,12 @@ CssDialog::CssDialog():
_textRenderer = Gtk::manage(new Gtk::CellRendererText());
_textRenderer->property_editable() = true;
- int nameColNum = _treeView.append_column("Property", *_textRenderer) - 1;
+ int nameColNum = _treeView.append_column("CSS Property", *_textRenderer) - 1;
_propCol = _treeView.get_column(nameColNum);
+ if (_propCol) {
+ _propCol->add_attribute(_textRenderer->property_text(),
+ _cssColumns._propertyLabel);
+ }
Gtk::Button* create = manage(new Gtk::Button());
_styleButton(*create, "list-add", "Add a new property");
@@ -83,12 +71,12 @@ CssDialog::CssDialog():
_getContents()->pack_start(_mainBox, Gtk::PACK_EXPAND_WIDGET);
- _targetDesktop = getDesktop();
- setDesktop(_targetDesktop);
+ setDesktop(getDesktop());
create->signal_clicked().connect(sigc::mem_fun(*this, &CssDialog::_addProperty));
}
+
/**
* @brief CssDialog::~CssDialog
* Class destructor
@@ -98,6 +86,7 @@ CssDialog::~CssDialog()
setDesktop(NULL);
}
+
/**
* @brief CssDialog::setDesktop
* @param desktop
@@ -108,16 +97,34 @@ void CssDialog::setDesktop(SPDesktop* desktop)
_desktop = desktop;
}
+
+/**
+ * @brief CssDialog::_styleButton
+ * @param btn
+ * @param iconName
+ * @param tooltip
+ * This function sets the style of '+'button at the bottom of dialog.
+ */
+void CssDialog::_styleButton(Gtk::Button& btn, char const* iconName,
+ char const* tooltip)
+{
+ GtkWidget *child = 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);
+}
+
+
/**
* @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.
+ * added.
*/
void CssDialog::_addProperty()
{
_propRow = *(_store->append());
- _newProperty = true;
}
} // namespace Dialog
diff --git a/src/ui/dialog/cssdialog.h b/src/ui/dialog/cssdialog.h
index 3bbab5031..1f8b7ca98 100644
--- a/src/ui/dialog/cssdialog.h
+++ b/src/ui/dialog/cssdialog.h
@@ -3,8 +3,10 @@
*/
/* Authors:
* Kamalpreet Kaur Grewal
+ * Tavmjong Bah
*
* Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr>
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -38,32 +40,39 @@ public:
~CssDialog();
static CssDialog &getInstance() { return *new CssDialog(); }
- void setDesktop(SPDesktop* desktop);
- class CssColumns : public Gtk::TreeModel::ColumnRecord
- {
+ // Data structure
+ class CssColumns : public Gtk::TreeModel::ColumnRecord {
public:
- CssColumns()
- { add(_colUnsetProp); add(_propertyLabel); }
+ 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;
+
+ // TreeView
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;
+ // Widgets
+ Gtk::VBox _mainBox;
+ Gtk::ScrolledWindow _scrolledWindow;
+ Gtk::HBox _buttonBox;
+
+ // Variables - Inkscape
+ SPDesktop* _desktop;
+
+ // Helper functions
+ void setDesktop(SPDesktop* desktop);
void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip);
+
+ // Signal handlers
void _addProperty();
};
diff --git a/src/ui/dialog/styledialog.cpp b/src/ui/dialog/styledialog.cpp
index 5246290b4..8d8ebf1b2 100644
--- a/src/ui/dialog/styledialog.cpp
+++ b/src/ui/dialog/styledialog.cpp
@@ -3,8 +3,10 @@
*/
/* Authors:
* Kamalpreet Kaur Grewal
+ * Tavmjong Bah
*
* Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr>
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -16,8 +18,16 @@
#include "sp-object.h"
#include "selection.h"
#include "xml/attribute-record.h"
+#include "xml/node-observer.h"
+#include "attribute-rel-svg.h"
+#include "document-undo.h"
+
+#include <glibmm/i18n.h>
#include <glibmm/regex.h>
-
+
+//#define DEBUG_STYLEDIALOG
+
+using Inkscape::DocumentUndo;
using Inkscape::Util::List;
using Inkscape::XML::AttributeRecord;
@@ -32,23 +42,104 @@ namespace Inkscape {
namespace UI {
namespace Dialog {
+class StyleDialog::NodeObserver : public Inkscape::XML::NodeObserver {
+public:
+ NodeObserver(StyleDialog* styleDialog) :
+ _styleDialog(styleDialog)
+ {
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::NodeObserver: Constructor" << std::endl;
+#endif
+ };
+
+ virtual void notifyContentChanged(Inkscape::XML::Node &node,
+ Inkscape::Util::ptr_shared<char> old_content,
+ Inkscape::Util::ptr_shared<char> new_content);
+
+ StyleDialog * _styleDialog;
+};
+
+
+void
+StyleDialog::NodeObserver::notifyContentChanged(
+ Inkscape::XML::Node &/*node*/,
+ Inkscape::Util::ptr_shared<char> /*old_content*/,
+ Inkscape::Util::ptr_shared<char> /*new_content*/ ) {
+
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::NodeObserver::notifyContentChanged" << std::endl;
+#endif
+
+ _styleDialog->_readStyleElement();
+ _styleDialog->_selectRow(NULL);
+}
+
+
+StyleDialog::TreeStore::TreeStore()
+{
+}
+
+
/**
- * @brief StyleDialog::_styleButton
- * @param btn
- * @param iconName
- * @param tooltip
- * This function sets the style of '+' and '-' buttons at the bottom of dialog.
+ * Allow dragging only selectors.
*/
-void StyleDialog::_styleButton(Gtk::Button& btn, char const* iconName,
- char const* tooltip)
+bool
+StyleDialog::TreeStore::row_draggable_vfunc(const Gtk::TreeModel::Path& path) const
{
- 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);
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::TreeStore::row_draggable_vfunc" << std::endl;
+#endif
+ auto unconstThis = const_cast<StyleDialog::TreeStore*>(this);
+ const_iterator iter = unconstThis->get_iter(path);
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ bool is_draggable = row[_styledialog->_mColumns._colIsSelector];
+ return is_draggable;
+ }
+ return Gtk::TreeStore::row_draggable_vfunc(path);
}
+
+/**
+ * Allow dropping only inbetween other selectors.
+ */
+bool
+StyleDialog::TreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest,
+ const Gtk::SelectionData& selection_data) const
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::TreeStore::row_drop_possible_vfunc" << std::endl;
+#endif
+
+ Gtk::TreeModel::Path dest_parent = dest;
+ dest_parent.up();
+ return dest_parent.empty();
+}
+
+
+// This is only here to handle updating style element after a drag and drop.
+void
+StyleDialog::TreeStore::on_row_deleted(const TreeModel::Path& path)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "on_row_deleted" << std::endl;
+#endif
+
+ if (_styledialog->_updating) return; // Don't write if we deleted row (other than from DND)
+
+ _styledialog->_writeStyleElement();
+}
+
+
+Glib::RefPtr<StyleDialog::TreeStore> StyleDialog::TreeStore::create(StyleDialog *styledialog)
+{
+ StyleDialog::TreeStore * store = new StyleDialog::TreeStore();
+ store->_styledialog = styledialog;
+ store->set_column_types( store->_styledialog->_mColumns );
+ return Glib::RefPtr<StyleDialog::TreeStore>( store );
+}
+
+
/**
* Constructor
* A treeview and a set of two buttons are added to the dialog. _addSelector
@@ -57,42 +148,45 @@ void StyleDialog::_styleButton(Gtk::Button& btn, char const* iconName,
*/
StyleDialog::StyleDialog() :
UI::Widget::Panel("", "/dialogs/style", SP_VERB_DIALOG_STYLE),
- _desktop(0)
+ _desktop(0),
+ _updating(false),
+ _textNode(NULL)
{
- 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);
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::StyleDialog" << std::endl;
+#endif
+ // Tree
Inkscape::UI::Widget::AddToIcon * addRenderer = manage(
new Inkscape::UI::Widget::AddToIcon() );
- addRenderer->property_active() = true;
- int addCol = _treeView.append_column("type", *addRenderer) - 1;
+ _store = TreeStore::create(this);
+ _treeView.set_model(_store);
+ _treeView.set_headers_visible(true);
+ _treeView.enable_model_drag_source();
+ _treeView.enable_model_drag_dest( Gdk::ACTION_MOVE );
+ int addCol = _treeView.append_column("", *addRenderer) - 1;
Gtk::TreeViewColumn *col = _treeView.get_column(addCol);
if ( col ) {
- col->add_attribute( addRenderer->property_active(), _mColumns._colAddRemove );
+ col->add_attribute( addRenderer->property_active(), _mColumns._colIsSelector );
}
-
- _treeView.append_column("Selector Name", _mColumns._selectorLabel);
+ _treeView.append_column("CSS Selector", _mColumns._colSelector);
_treeView.set_expander_column(*(_treeView.get_column(1)));
+ // Pack widgets
+ _paned.pack1(_mainBox, Gtk::SHRINK);
+ _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET);
+ _scrolledWindow.add(_treeView);
+ _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
create = manage( new Gtk::Button() );
_styleButton(*create, "list-add", "Add a new CSS Selector");
- create->signal_clicked().connect(sigc::mem_fun(*this,
- &StyleDialog::_addSelector));
+ 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->signal_clicked().connect(sigc::mem_fun(*this, &StyleDialog::_delSelector));
del->set_sensitive(false);
_mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK);
@@ -102,8 +196,43 @@ StyleDialog::StyleDialog() :
_getContents()->pack_start(_paned, Gtk::PACK_EXPAND_WIDGET);
- _targetDesktop = getDesktop();
- setDesktop(_targetDesktop);
+ // Dialog size request
+ Gtk::Requisition sreq1, sreq2;
+ get_preferred_size(sreq1, sreq2);
+ int minWidth = 200;
+ int minHeight = 300;
+ minWidth = (sreq2.width > minWidth ? sreq2.width : minWidth );
+ minHeight = (sreq2.height > minHeight ? sreq2.height : minHeight);
+ set_size_request(minWidth, minHeight);
+
+ // Signal handlers
+ _treeView.signal_button_release_event().connect( // Needs to be release, not press.
+ sigc::mem_fun(*this, &StyleDialog::_handleButtonEvent),
+ false);
+
+ _treeView.signal_button_release_event().connect_notify(
+ sigc::mem_fun(*this, &StyleDialog::_buttonEventsSelectObjs),
+ false);
+
+ _treeView.get_selection()->signal_changed().connect(
+ sigc::mem_fun(*this, &StyleDialog::_selChanged));
+
+ _objObserver.signal_changed().connect(sigc::mem_fun(*this, &StyleDialog::_objChanged));
+
+
+ // Add CSS dialog
+ _cssPane = new CssDialog;
+ _paned.pack2(*_cssPane, Gtk::SHRINK);
+ _cssPane->show_all();
+
+ _cssPane->_textRenderer->signal_edited().connect(
+ sigc::mem_fun(*this, &StyleDialog::_handleEdited));
+ _cssPane->_treeView.signal_button_release_event().connect(
+ sigc::mem_fun(*this, &StyleDialog::_delProperty),
+ false);
+
+ // Document & Desktop TO DO: Fix this brokeness
+ setDesktop(getDesktop()); // Adds signal handler
/**
* @brief document
@@ -111,444 +240,643 @@ StyleDialog::StyleDialog() :
* 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);
+ _document = _desktop->doc();
- _treeView.signal_button_press_event().connect_notify(sigc::mem_fun
- (*this, &StyleDialog::
- _buttonEventsSelectObjs),
- false);
+ _readStyleElement();
+ _selectRow(NULL);
- _cssPane = new CssDialog;
+ if (!_store->children().empty()) {
+ del->set_sensitive(true);
+ }
- _treeView.get_selection()->signal_changed().connect(sigc::mem_fun(*this,
- &StyleDialog::
- _selChanged));
}
+
/**
* @brief StyleDialog::~StyleDialog
* Class destructor
*/
StyleDialog::~StyleDialog()
{
+#ifdef DEBUG_STYLEDIALOOG
+ std::cout << "StyleDialog::~StyleDialog" << std::endl;
+#endif
setDesktop(NULL);
}
+
/**
* @brief StyleDialog::setDesktop
* @param desktop
- * This function sets the 'desktop' for the Style Dialog.
+ * Set the 'desktop' for the Style Dialog.
*/
void StyleDialog::setDesktop( SPDesktop* desktop )
{
+#ifdef DEBUG_STYLEDIALOOG
+ std::cout << "StyleDialog::setDesktop" << std::endl;
+#endif
Panel::setDesktop(desktop);
_desktop = Panel::getDesktop();
- _desktop->getSelection()->connectChanged(sigc::mem_fun(*this, &StyleDialog::
- _selectRow));
+ _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.
+ * @brief StyleDialog::_styleTextNode
+ * @return Inkscape::XML::Node* pointing to a style element's text node.
+ * Returns the style element's text node. If there is no style element, one is created.
+ * Ditto for text node.
*/
-void StyleDialog::_addSelector()
+Inkscape::XML::Node* StyleDialog::_getStyleTextNode()
{
- 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);
+ Inkscape::XML::Node *styleNode = NULL;
+ Inkscape::XML::Node *textNode = NULL;
- /**
- * 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));
+ Inkscape::XML::Node *root = _document->getReprRoot();
+ for (unsigned i = 0; i < root->childCount(); ++i) {
+ if (Glib::ustring(root->nthChild(i)->name()) == "svg:style") {
+
+ styleNode = root->nthChild(i);
+
+ for (unsigned j = 0; j < styleNode->childCount(); ++j) {
+ if (styleNode->nthChild(j)->type() == Inkscape::XML::TEXT_NODE) {
+ textNode = styleNode->nthChild(j);
+ }
+ }
+
+ if (textNode == NULL) {
+ // Style element found but does not contain text node!
+ std::cerr << "StyleDialog::_getStyleTextNode(): No text node!" << std::endl;
+ textNode = _document->getReprDoc()->createTextNode("");
+ styleNode->appendChild(textNode);
+ Inkscape::GC::release(textNode);
+ }
+ }
}
- textDialogPtr->set_size_request(200, 100);
- textDialogPtr->show_all();
- int result = textDialogPtr->run();
+ if (styleNode == NULL) {
+ // Style element not found, create one
+ styleNode = _document->getReprDoc()->createElement("svg:style");
+ textNode = _document->getReprDoc()->createTextNode("");
- /**
- * @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();
+ styleNode->appendChild(textNode);
+ Inkscape::GC::release(textNode);
+
+ root->addChild(styleNode, NULL);
+ Inkscape::GC::release(styleNode);
}
- else {
- _selectorName = ".Class1";
+
+ if (_textNode != textNode) {
+ _textNode = textNode;
+ NodeObserver *no = new NodeObserver(this);
+ textNode->addObserver(*no);
}
- del->set_sensitive(true);
+ return textNode;
+}
- /**
- * 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";
- }
+/**
+ * @brief StyleDialog::_readStyleElement
+ * Fill the Gtk::TreeStore from the svg:style element.
+ */
+void StyleDialog::_readStyleElement()
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_readStyleElement: updating " << (_updating?"true":"false")<< std::endl;
+#endif
- 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));
- }
- }
- }
+ if (_updating) return; // Don't read if we wrote style element.
+ _updating = true;
+
+ _store->clear();
+
+ Inkscape::XML::Node * textNode = _getStyleTextNode();
+ if (textNode == NULL) {
+ std::cerr << "StyleDialog::_readStyleElement: No text node!" << std::endl;
}
- else {
- _selectorValue = _selectorName + "{" + "}" + "\n";
- objExists = false;
+
+ // Get content from style text node.
+ std::string content = (textNode->content() ? textNode->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<Glib::ustring> tokens = Glib::Regex::split_simple("[}{]", content);
+
+ // If text node is empty, return (avoids problem with negative below).
+ if (tokens.size() == 0) {
+ return;
}
- 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];
+ for (unsigned i = 0; i < tokens.size()-1; i += 2) {
+
+ Glib::ustring selector = tokens[i];
+ REMOVE_SPACES(selector); // Remove leading/trailing spaces
+
+ // Get list of objects selector matches
+ std::vector<SPObject *> objVec = _getObjVec( selector );
+
+ Glib::ustring properties;
+ // Check to make sure we do have a value to match selector.
+ if ((i+1) < tokens.size()) {
+ properties = tokens[i+1];
+ } else {
+ std::cerr << "StyleDialog::_readStyleElement: Missing values "
+ "for last selector!" << std::endl;
}
- break;
- default:
- break;
- }
+ REMOVE_SPACES(properties);
- /**
- * 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;
+ Gtk::TreeModel::Row row = *(_store->append());
+ row[_mColumns._colSelector] = selector;
+ row[_mColumns._colIsSelector] = true;
+ row[_mColumns._colObj] = objVec;
+ row[_mColumns._colProperties] = properties;
+
+ // Add as children, objects that match selector.
+ for (auto& obj: objVec) {
+ Gtk::TreeModel::Row childrow = *(_store->append(row->children()));
+ childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId());
+ childrow[_mColumns._colIsSelector] = false;
+ childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj);
+ childrow[_mColumns._colProperties] = ""; // Unused
+ }
}
- _selAdd(row);
+ _updating = false;
}
+
/**
- * @brief StyleDialog::_updateStyleContent
- * This function updates the content in style element as new selectors (or
- * objects) are added/removed.
+ * @brief StyleDialog::_writeStyleElement
+ * Update the content of the style element as selectors (or objects) are added/removed.
*/
-void StyleDialog::_updateStyleContent()
+void StyleDialog::_writeStyleElement()
{
- std::string styleContent = "";
- for (unsigned i = 0; i < _selectorVec.size(); ++i) {
- styleContent = styleContent + _selectorVec[i]._xmlContent;
+ _updating = true;
+
+ Glib::ustring styleContent;
+ for (auto& row: _store->children()) {
+ styleContent = styleContent + row[_mColumns._colSelector] +
+ " { " + row[_mColumns._colProperties] + " }\n";
}
- _styleChild->firstChild()->setContent(styleContent.c_str());
+ // We could test if styleContent is empty and then delete the style node here but there is no
+ // harm in keeping it around ...
+
+ Inkscape::XML::Node *textNode = _getStyleTextNode();
+ textNode->setContent(styleContent.c_str());
+
+ DocumentUndo::done(_document, SP_VERB_DIALOG_STYLE, _("Edited style element."));
+
+ _updating = false;
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_writeStyleElement(): |" << styleContent << "|" << std::endl;
+#endif
}
+
/**
- * @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.
+ * @brief StyleDialog::_addToSelector
+ * @param row
+ * Add selected objects on the desktop to the selector corresponding to 'row'.
*/
-void StyleDialog::_delSelector()
+void StyleDialog::_addToSelector(Gtk::TreeModel::Row row)
{
- 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, "}");
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_addToSelector: Entrance" << std::endl;
+#endif
+ if (*row) {
+
+ Glib::ustring selector = row[_mColumns._colSelector];
+
+ if (selector[0] == '#') {
+ // 'id' selector... add selected object's id's to list.
+ Inkscape::Selection* selection = _desktop->getSelection();
+ for (auto& obj: selection->objects()) {
+
+ Glib::ustring id = (obj->getId()?obj->getId():"");
+
+ std::vector<SPObject *> objVec = row[_mColumns._colObj];
+ bool found = false;
+ for (auto& obj: objVec) {
+ if (id == obj->getId()) {
+ found = true;
+ break;
+ }
}
- }
- 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;
+ if (!found) {
+ // Update row
+ objVec.push_back(obj); // Adding to copy so need to update tree
+ row[_mColumns._colObj] = objVec;
+ row[_mColumns._colSelector] = _getIdList( objVec );
+
+ // Add child row
+ Gtk::TreeModel::Row childrow = *(_store->append(row->children()));
+ childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId());
+ childrow[_mColumns._colIsSelector] = false;
+ childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj);
+ childrow[_mColumns._colProperties] = ""; // Unused
+ }
}
+ }
- /**
- * 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();
+ else if (selector[0] == '.') {
+ // 'class' selector... add value to class attribute of selected objects.
+
+ // Get first class (split on white space or comma)
+ std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("[,\\s]+", selector);
+ Glib::ustring className = tokens[0];
+ className.erase(0,1);
+
+ // Get list of objects to modify
+ Inkscape::Selection* selection = _desktop->getSelection();
+ std::vector<SPObject *> objVec( selection->objects().begin(),
+ selection->objects().end() );
+
+ _insertClass( objVec, className );
+
+ row[_mColumns._colObj] = _getObjVec( selector );
+
+ for (auto& obj: objVec) {
+ // Add child row
+ Gtk::TreeModel::Row childrow = *(_store->append(row->children()));
+ childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId());
+ childrow[_mColumns._colIsSelector] = false;
+ childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj);
+ childrow[_mColumns._colProperties] = ""; // Unused
}
}
+
+ else {
+ // Do nothing for element selectors.
+ // std::cout << " Element selector... doing nothing!" << std::endl;
+ }
}
+
+ _writeStyleElement();
}
+
/**
- * @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.
+ * @brief StyleDialog::_removeFromSelector
+ * @param row
+ * Remove the object corresponding to 'row' from the parent selector.
*/
-Inkscape::XML::Node* StyleDialog::_styleElementNode()
+void StyleDialog::_removeFromSelector(Gtk::TreeModel::Row row)
{
- 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);
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_removeFromSelector: Entrance" << std::endl;
+#endif
+ if (*row) {
+
+ Glib::ustring objectLabel = row[_mColumns._colSelector];
+ Gtk::TreeModel::iterator iter = row->parent();
+ if (iter) {
+ Gtk::TreeModel::Row parent = *iter;
+ Glib::ustring selector = parent[_mColumns._colSelector];
+
+ if (selector[0] == '#') {
+ // 'id' selector... remove selected object's id's to list.
+
+ // Erase from selector label.
+ auto i = selector.find(objectLabel);
+ if (i != Glib::ustring::npos) {
+ selector.erase(i, objectLabel.length());
+ }
+ // Erase any comma/space
+ if (i != Glib::ustring::npos && selector[i] == ',') {
+ selector.erase(i, 1);
+ }
+ if (i != Glib::ustring::npos && selector[i] == ' ') {
+ selector.erase(i, 1);
+ }
+
+ // Update store
+ if (selector.empty()) {
+ _store->erase(parent);
+ } else {
+ // Save new selector and update object vector.
+ parent[_mColumns._colSelector] = selector;
+ parent[_mColumns._colObj] = _getObjVec( selector );
+ _store->erase(row);
+ }
+ }
+
+ else if (selector[0] == '.') {
+ // 'class' selector... remove value to class attribute of selected objects.
+
+ std::vector<SPObject *> objVec = row[_mColumns._colObj]; // Just one
+
+ // Get first class (split on white space or comma)
+ std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("[,\\s]+", selector);
+ Glib::ustring className = tokens[0];
+ className.erase(0,1); // Erase '.'
+
+ // Erase class name from 'class' attribute.
+ Glib::ustring classAttr = objVec[0]->getRepr()->attribute("class");
+ auto i = classAttr.find( className );
+ if (i != Glib::ustring::npos) {
+ classAttr.erase(i, className.length());
+ }
+ if (i != Glib::ustring::npos && classAttr[i] == ' ') {
+ classAttr.erase(i, 1);
+ }
+ objVec[0]->getRepr()->setAttribute("class", classAttr);
+
+ parent[_mColumns._colObj] = _getObjVec( selector );
+ _store->erase(row);
+ }
+
+ else {
+ // Do nothing for element selectors.
+ // std::cout << " Element selector... doing nothing!" << std::endl;
+ }
}
}
- return NULL;
+
+ _writeStyleElement();
+
}
+
/**
- * @brief StyleDialog::_setClassAttribute
+ * @brief StyleDialog::_getIdList
* @param sel
- * @return This function returns the ids of objects selected which are passed
- * to entrybox.
+ * @return This function returns a comma seperated list of ids for objects in input vector.
+ * It is used in creating an 'id' selector. It relies on objects having 'id's.
*/
-std::string StyleDialog::_setClassAttribute(std::vector<SPObject*> sel)
+Glib::ustring StyleDialog::_getIdList(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()) + " ";
+ Glib::ustring str;
+ for (auto& obj: sel) {
+ str += "#" + Glib::ustring(obj->getId()) + ", ";
+ }
+ if (!str.empty()) {
+ str.erase(str.size()-1); // Remove space at end. c++11 has pop_back() but not ustring.
+ str.erase(str.size()-1); // Remove comma at end.
}
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.
+ * @brief StyleDialog::_getObjVec
+ * @param selector: a valid CSS selector string.
+ * @return objVec: a vector of pointers to SPObject's the selector matches.
+ * Return a vector of all objects that selector matches.
*/
-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());
- }
+std::vector<SPObject *> StyleDialog::_getObjVec(Glib::ustring selector) {
- // 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);
- }
- }
+ std::vector<SPObject *> objVec = _document->getObjectsBySelector( selector );
- // Find objects that match element selector
- else {
- std::vector<SPObject *> objects = _document->
- getObjectsByElement(token2);
- objVec.insert(objVec.end(), objects.begin(), objects.end());
- }
- }
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_getObjVec: |" << selector << "|" << std::endl;
+ for (auto& obj: objVec) {
+ std::cout << " " << (obj->getId()?obj->getId():"null") << std::endl;
+ }
+#endif
- 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;
+ return objVec;
+}
+
+
+/**
+ * @brief StyleDialog::_insertClass
+ * @param objs: list of objects to insert class
+ * @param class: class to insert
+ * Insert a class name into objects' 'class' attribute.
+ */
+void StyleDialog::_insertClass(const std::vector<SPObject *>& objVec, const Glib::ustring& className) {
+
+ for (auto& obj: objVec) {
+
+ if (!obj->getRepr()->attribute("class")) {
+ // 'class' attribute does not exist, create it.
+ obj->getRepr()->setAttribute("class", className);
+ } else {
+ // 'class' attribute exists, append.
+ Glib::ustring classAttr = obj->getRepr()->attribute("class");
+
+ // Split on white space.
+ std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("\\s+", classAttr);
+ bool add = true;
+ for (auto& token: tokens) {
+ if (token == className) {
+ add = false; // Might be useful to still add...
+ break;
}
+ }
+ if (add) {
+ obj->getRepr()->setAttribute("class", classAttr + " " + className );
+ }
+ }
+ }
+ }
+
+
+/**
+ * @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)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_selectObjects: " << eventX << ", " << eventY << std::endl;
+#endif
- _selectorValue = selectors + "{" + values + "}\n";
- inkSelector._selector = selectors;
- inkSelector._matchingObjs = objVec;
- inkSelector._xmlContent = _selectorValue;
- _selectorVec.push_back(inkSelector);
+ _desktop->selection->clear();
+ Gtk::TreeViewColumn *col = _treeView.get_column(1);
+ Gtk::TreeModel::Path path;
+ int x2 = 0;
+ int y2 = 0;
+ // To do: We should be able to do this via passing in row.
+ if (_treeView.get_path_at_pos(eventX, eventY, 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);
+ }
}
}
}
- return _selectorVec;
}
+
/**
- * @brief StyleDialog::_populateTree
- * @param _selVec
- * This function populates the treeview with selectors available in the
- * stylesheet.
+ * @brief StyleDialog::_addSelector
+ *
+ * This function opens a dialog to add a selector. The dialog is prefilled
+ * with an 'id' selector containing a list of the id's of selected objects
+ * or with a 'class' selector if no objects are selected.
*/
-std::string StyleDialog::_populateTree(std::vector<InkSelector> _selVec)
+void StyleDialog::_addSelector()
{
- _selectorVec = _selVec;
- std::string selectorValue;
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_addSelector: Entrance" << std::endl;
+#endif
- 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());
+ // Store list of selected elements on desktop (not to be confused with selector).
+ Inkscape::Selection* selection = _desktop->getSelection();
+ std::vector<SPObject *> objVec( selection->objects().begin(),
+ selection->objects().end() );
+
+ // ==== Create popup dialog ====
+ Gtk::Dialog *textDialogPtr = new Gtk::Dialog();
+ textDialogPtr->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL);
+ textDialogPtr->add_button(_("Add"), Gtk::RESPONSE_OK);
+
+ Gtk::Entry *textEditPtr = manage ( new Gtk::Entry() );
+ textDialogPtr->get_vbox()->pack_start(*textEditPtr, Gtk::PACK_SHRINK);
+
+ Gtk::Label *textLabelPtr = manage ( new Gtk::Label(
+ _("Invalid entry: Not an id (#), class (.), or element CSS selector.")
+ ) );
+ textDialogPtr->get_vbox()->pack_start(*textLabelPtr, 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 {
+ textEditPtr->set_text(_getIdList(objVec));
}
- if (_selectorVec.size() > 0) {
+ Gtk::Requisition sreq1, sreq2;
+ textDialogPtr->get_preferred_size(sreq1, sreq2);
+ int minWidth = 200;
+ int minHeight = 100;
+ minWidth = (sreq2.width > minWidth ? sreq2.width : minWidth );
+ minHeight = (sreq2.height > minHeight ? sreq2.height : minHeight);
+ textDialogPtr->set_size_request(minWidth, minHeight);
+ textEditPtr->show();
+ textLabelPtr->hide();
+ textDialogPtr->show();
+
+
+ // ==== Get response ====
+ int result = -1;
+ bool invalid = true;
+ Glib::ustring selectorValue;
+
+ while (invalid) {
+ result = textDialogPtr->run();
+ if (result != Gtk::RESPONSE_OK) { // Cancel, close dialog, etc.
+ textDialogPtr->hide();
+ return;
+ }
+ /**
+ * @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"
+ */
+ selectorValue = textEditPtr->get_text();
+ Glib::ustring firstWord = selectorValue.substr(0, selectorValue.find(" "));
+
del->set_sensitive(true);
+
+ if (selectorValue[0] == '.' ||
+ selectorValue[0] == '#' ||
+ selectorValue[0] == '*' ||
+ SPAttributeRelSVG::isSVGElement( firstWord ) ) {
+ invalid = false;
+ } else {
+ textLabelPtr->show();
+ }
}
+ delete textDialogPtr;
+
+ // ==== Handle response ====
- return selectorValue;
+ // If class selector, add selector name to class attribute for each object
+ if (selectorValue[0] == '.') {
+
+ Glib::ustring className = selectorValue;
+ className.erase(0,1);
+ _insertClass(objVec, className);
+ }
+
+ // Generate a new object vector (we could have an element selector,
+ // the user could have edited the id selector list, etc.).
+ objVec = _getObjVec( selectorValue );
+
+ // Add entry to GUI tree
+ Gtk::TreeModel::Row row = *(_store->append());
+ row[_mColumns._colSelector] = selectorValue;
+ row[_mColumns._colIsSelector] = true;
+ row[_mColumns._colObj] = objVec;
+
+ // Add as children objects that match selector.
+ for (auto& obj: objVec) {
+ Gtk::TreeModel::Row childrow = *(_store->append(row->children()));
+ childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId());
+ childrow[_mColumns._colIsSelector] = false;
+ childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj);
+ }
+
+ // Add entry to style element
+ _writeStyleElement();
+}
+
+/**
+ * @brief StyleDialog::_delSelector
+ * This function deletes selector when '-' at the bottom is clicked.
+ * Note: If deleting a class selector, class attributes are NOT changed.
+ */
+void StyleDialog::_delSelector()
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_delSelector" << std::endl;
+#endif
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ _updating = true;
+ _store->erase(iter);
+ _updating = false;
+ _writeStyleElement();
+ }
}
/**
* @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.
+ * Handles the event when '+' button in front of a selector name is clicked or when a '-' button in
+ * front of a child object is clicked. In the first case, the selected objects on the desktop (if
+ * any) are added as children of the selector in the treeview. In the latter case, the object
+ * corresponding to the row is removed from the selector.
*/
bool StyleDialog::_handleButtonEvent(GdkEventButton *event)
{
- if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_handleButtonEvent: Entrance" << std::endl;
+#endif
+ if (event->type == GDK_BUTTON_RELEASE && event->button == 1) {
Gtk::TreeViewColumn *col = 0;
Gtk::TreeModel::Path path;
int x = static_cast<int>(event->x);
@@ -557,119 +885,16 @@ bool StyleDialog::_handleButtonEvent(GdkEventButton *event)
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::iterator iter = _store->get_iter(path);
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.
- */
+ // Add or remove objects from a
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;
- }
+ // Add selected objects to selector.
+ _addToSelector(row);
+ } else {
+ // Remove object from selector
+ _removeFromSelector(row);
}
}
}
@@ -678,274 +903,169 @@ bool StyleDialog::_handleButtonEvent(GdkEventButton *event)
}
/**
- * @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.
+ * @brief StyleDialog::_updateCSSPanel
+ * Updates CSS panel according to row in Style panel.
*/
-void StyleDialog::_selAdd(Gtk::TreeModel::Row row)
+void StyleDialog::_updateCSSPanel()
{
- 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];
- }
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_updateCSSPanel" << std::endl;
+#endif
+ _updating = true;
- /**
- * 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";
- }
+ // This should probably be in a member function of CSSDialog.
+ _cssPane->_store->clear();
- it = _selectorVec.erase(it);
- it = _selectorVec.insert(it, inkSelector);
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ Glib::ustring properties;
+ if (row[_mColumns._colIsSelector]) {
+ properties = row[_mColumns._colProperties];
+ _objObserver.set( NULL );
+ } else {
+ std::vector<SPObject *> objects = row[_mColumns._colObj];
+ _objObserver.set( objects[0] );
+ if (objects[0] && objects[0]->getAttribute("style") != NULL) {
+ properties = objects[0]->getAttribute("style");
+ }
+ }
+ REMOVE_SPACES(properties); // Remove leading/trailing spaces
+
+ std::vector<Glib::ustring> tokens =
+ Glib::Regex::split_simple("\\s*;\\s*", properties);
+ for (auto& token: tokens) {
+ if (!token.empty()) {
+ _cssPane->_propRow = *(_cssPane->_store->append());
+ _cssPane->_propRow[_cssPane->_cssColumns._colUnsetProp] = false;
+ _cssPane->_propRow[_cssPane->_cssColumns._propertyLabel] = token;
}
}
}
- if (_styleElementNode()) {
- _styleChild = _styleElementNode();
- _updateStyleContent();
- }
- else if (_styleExists && !_newDrawing) {
- _updateStyleContent();
- }
+
+ _updating = false;
}
+
/**
- * @brief StyleDialog::_selectObjects
+ * @brief StyleDialog::_buttonEventsSelectObjs
* @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.
+ * This function detects single or double click on a selector in any row. Clicking
+ * on a selector selects the matching objects on the desktop. A double click will
+ * in addition open the CSS dialog.
*/
void StyleDialog::_buttonEventsSelectObjs(GdkEventButton* event )
{
- if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_buttonEventsSelectObjs" << std::endl;
+#endif
+ _updating = true;
+
+ if (event->type == GDK_BUTTON_RELEASE && 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);
+
+ _updateCSSPanel();
}
- 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);
+ _updating = false;
+}
- //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, "}");
- }
- }
+/**
+ * @brief StyleDialog::_selectRow
+ * This function selects the row in treeview corresponding to an object selected
+ * in the drawing. If more than one row matches, the first is chosen.
+ */
+void StyleDialog::_selectRow(Selection */*sel*/)
+{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_selectRow: updating: " << (_updating?"true":"false") << std::endl;
+#endif
+ if (_updating) return; // Avoid updating if we have set row via dialog.
- 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);
- }
- }
- }
+ Inkscape::Selection* selection = _desktop->getSelection();
+ if (!selection->isEmpty()) {
+ SPObject *obj = selection->objects().back();
+
+ 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);
+ _updateCSSPanel();
+ return;
}
}
}
- 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);
}
+
+ // Selection empty or no row matches.
+ _treeView.get_selection()->unselect_all();
+ _updateCSSPanel();
}
+
/**
* @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();
- }
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_selChanged" << std::endl;
+#endif
+}
+
+
+void StyleDialog::_objChanged() {
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_objChanged" << std::endl;
+#endif
+ if (_updating) return;
+ _updateCSSPanel();
}
+
/**
* @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.
+ * Update trees when new text is entered into a CSS dialog row.
*/
void StyleDialog::_handleEdited(const Glib::ustring& path, const Glib::ustring& new_text)
{
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_handleEdited: path: " << path
+ << " new_text: " << new_text << std::endl;
+#endif
+
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::ustring properties;
+ for (auto& crow: _cssPane->_store->children()) {
+ properties = properties + crow[_cssPane->_cssColumns._propertyLabel] + "; ";
+ }
+
+ // Update selector data.
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();
- }
- }
+ row[_mColumns._colProperties] = properties;
+ _writeStyleElement();
}
}
@@ -953,14 +1073,15 @@ void StyleDialog::_handleEdited(const Glib::ustring& path, const Glib::ustring&
* @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'.
+ * Delete a property from the CSS dialog and then update trees.
*/
bool StyleDialog::_delProperty(GdkEventButton *event)
{
- if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+#ifdef DEBUG_STYLEDIALOG
+ std::cout << "StyleDialog::_delProperty" << std::endl;
+#endif
+
+ if (event->type == GDK_BUTTON_RELEASE && event->button == 1) {
Gtk::TreeViewColumn *col = 0;
Gtk::TreeModel::Path path;
int x = static_cast<int>(event->x);
@@ -971,42 +1092,33 @@ bool StyleDialog::_delProperty(GdkEventButton *event)
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();
+ Gtk::TreeModel::iterator cssIter =
+ _cssPane->_treeView.get_selection()->get_selected();
if (cssIter) {
- cssRow = *cssIter;
- toDelProperty = cssRow[_cssPane->_cssColumns._propertyLabel];
- }
+ _cssPane->_store->erase(cssIter);
- 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 properties;
+ for (auto& crow: _cssPane->_store->children()) {
+ properties = properties + crow[_cssPane->_cssColumns._propertyLabel] + "; ";
+ }
- 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);
+ // Update selector data.
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ row[_mColumns._colProperties] = properties;
+ _writeStyleElement();
+
+ // Update style attribute
+ if (!row[_mColumns._colIsSelector]) {
+ std::vector<SPObject *> objects = row[_mColumns._colObj];
+ if (objects[0]) {
+ Glib::ustring properties = row[_mColumns._colProperties];
+ if (properties.empty()) {
+ objects[0]->setAttribute("style", NULL);
+ } else {
+ objects[0]->setAttribute("style", properties);
}
}
}
@@ -1018,99 +1130,36 @@ bool StyleDialog::_delProperty(GdkEventButton *event)
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.
+ * @brief StyleDialog::_styleButton
+ * @param btn
+ * @param iconName
+ * @param tooltip
+ * Set the style of '+' and '-' buttons at the bottom of dialog.
*/
-void StyleDialog::_checkAllChildren(Gtk::TreeModel::Children& children)
+void StyleDialog::_styleButton(Gtk::Button& btn, char const* iconName,
+ char const* tooltip)
{
- 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);
- }
- }
+ 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);
}
-/**
- * @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
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/ui/dialog/styledialog.h b/src/ui/dialog/styledialog.h
index b03a1d2e1..f21e81700 100644
--- a/src/ui/dialog/styledialog.h
+++ b/src/ui/dialog/styledialog.h
@@ -3,8 +3,10 @@
*/
/* Authors:
* Kamalpreet Kaur Grewal
+ * Tavmjong Bah
*
* Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr>
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -24,94 +26,139 @@
#include "document.h"
#include "ui/dialog/cssdialog.h"
+#include "xml/helper-observer.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.
+ * A list of CSS selectors will show up in this dialog. This dialog allows one to
+ * add and delete selectors. Elements can be added to and removed from the selectors
+ * in the dialog. Selection of any selector row selects the matching objects in
+ * the drawing and vice-versa. (Only simple selectors supported for now.)
+ *
+ * This class must keep two things in sync:
+ * 1. The text node of the style element.
+ * 2. The Gtk::TreeModel.
*/
-typedef std::pair<std::pair<std::string, std::vector<SPObject *> >, std::string>
-_selectorVecType;
+class StyleDialog : public Widget::Panel {
-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 {
+ // Monitor <style> element for changes.
+ class NodeObserver;
+
+ // Data structure
+ class ModelColumns : public Gtk::TreeModel::ColumnRecord {
public:
- std::string _selector;
- std::vector<SPObject *> _matchingObjs;
- std::string _xmlContent;
+ ModelColumns() {
+ add(_colSelector);
+ add(_colIsSelector);
+ add(_colObj);
+ add(_colProperties);
+ }
+ Gtk::TreeModelColumn<Glib::ustring> _colSelector; // Selector or matching object id.
+ Gtk::TreeModelColumn<bool> _colIsSelector; // Selector row or child object row.
+ Gtk::TreeModelColumn<std::vector<SPObject *> > _colObj; // List of matching objects.
+ Gtk::TreeModelColumn<Glib::ustring> _colProperties; // List of properties.
};
+ ModelColumns _mColumns;
- 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 *);
+ // Override Gtk::TreeStore to control drag-n-drop (only allow dragging and dropping of selectors).
+ // See: https://developer.gnome.org/gtkmm-tutorial/stable/sec-treeview-examples.html.en
+ //
+ // TreeStore implements simple drag and drop (DND) but there appears no way to know when a DND
+ // has been completed (other than doing the whole DND ourselves). As a hack, we use
+ // on_row_deleted to trigger write of style element.
+ class TreeStore : public Gtk::TreeStore {
+ protected:
+ TreeStore();
+ bool row_draggable_vfunc(const Gtk::TreeModel::Path& path) const override;
+ bool row_drop_possible_vfunc(const Gtk::TreeModel::Path& path,
+ const Gtk::SelectionData& selection_data) const override;
+ void on_row_deleted(const TreeModel::Path& path) override;
- 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;
+ static Glib::RefPtr<StyleDialog::TreeStore> create(StyleDialog *styledialog);
+
+ private:
+ StyleDialog *_styledialog;
};
- SPDesktop* _desktop;
- SPDesktop* _targetDesktop;
- ModelColumns _mColumns;
+ // TreeView
+ Gtk::TreeView _treeView;
+ Glib::RefPtr<TreeStore> _store;
+
+ // Widgets
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);
+
+ // Variables - Inkscape
+ SPDesktop* _desktop; // To do: use panel _desktop
+ SPDocument* _document;
+ bool _updating; // Prevent cyclic actions: read <-> write, select via dialog <-> via desktop
+ Inkscape::XML::Node *_textNode;
+ Inkscape::XML::SignalObserver _objObserver; // Track object in selected row (to update CSS panel).
+
+ // Reading and writing the style element.
+ Inkscape::XML::Node *_getStyleTextNode();
+ void _readStyleElement();
+ void _writeStyleElement();
+
+ // Manipulate Tree
+ void _addToSelector(Gtk::TreeModel::Row row);
+ void _removeFromSelector(Gtk::TreeModel::Row row);
+ Glib::ustring _getIdList(std::vector<SPObject *>);
+ std::vector<SPObject *> _getObjVec(Glib::ustring selector);
+ void _insertClass(const std::vector<SPObject *>& objVec, const Glib::ustring& className);
+ void _selectObjects(int, int);
+ void _updateCSSPanel();
// Signal handlers
void _addSelector();
void _delSelector();
+ bool _handleButtonEvent(GdkEventButton *event);
+ void _buttonEventsSelectObjs(GdkEventButton *event);
+ void _selectRow(Selection *); // Select row in tree when selection changed.
void _selChanged();
+ void _objChanged();
- // Signal handler for CssDialog
+ // Signal handlers for CssDialog
void _handleEdited(const Glib::ustring& path, const Glib::ustring& new_text);
bool _delProperty(GdkEventButton *event);
+
+ // GUI
+ void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip);
};
-} // namespace Dialog
+} // namespace Dialogc
} // namespace UI
} // namespace Inkscape
#endif // STYLEDIALOG_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/ui/dialog/symbols.cpp b/src/ui/dialog/symbols.cpp
index 4840b897b..740ff4f39 100644
--- a/src/ui/dialog/symbols.cpp
+++ b/src/ui/dialog/symbols.cpp
@@ -25,6 +25,7 @@
#include <gtkmm/comboboxtext.h>
#include <gtkmm/iconview.h>
#include <gtkmm/liststore.h>
+#include <glibmm/regex.h>
#include <glibmm/stringutils.h>
#include <glibmm/markup.h>
#include <glibmm/i18n.h>
@@ -127,7 +128,7 @@ SymbolsDialog::SymbolsDialog( gchar const* prefsPath ) :
sigc::connection connSet = symbolSet->signal_changed().connect(
sigc::mem_fun(*this, &SymbolsDialog::rebuild));
instanceConns.push_back(connSet);
-
+
++row;
/********************* Icon View **************************/
@@ -342,7 +343,7 @@ void SymbolsDialog::rebuild() {
addSymbol->set_sensitive( true );
removeSymbol->set_sensitive( true );
} else {
- addSymbol->set_sensitive( false );
+ addSymbol->set_sensitive( false );
removeSymbol->set_sensitive( false );
}
add_symbols( symbolDocument );
@@ -451,10 +452,21 @@ void SymbolsDialog::iconChanged() {
#ifdef WITH_LIBVISIO
// Read Visio stencil files
-SPDocument* read_vss( gchar* fullname, gchar* filename ) {
+SPDocument* read_vss( gchar* fullname, Glib::ustring name ) {
+
+ #ifdef WIN32
+ // RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows
+ // therefore attempt to convert uri to the system codepage
+ // even if this is not possible the alternate short (8.3) file name will be used if available
+ fullname = g_win32_locale_filename_from_utf8(fullname);
+ #endif
RVNGFileStream input(fullname);
+ #ifdef WIN32
+ g_free(fullname);
+ #endif
+
if (!libvisio::VisioDocument::isSupported(&input)) {
return NULL;
}
@@ -474,6 +486,12 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) {
return NULL;
}
+ // prepare a valid title for the symbol file
+ Glib::ustring title = Glib::Markup::escape_text(name);
+ // prepare a valid id prefix for the symbols (unfortunately libvisio doesn't give us a name)
+ Glib::RefPtr<Glib::Regex> regex1 = Glib::Regex::create("[^a-zA-Z0-9_-]");
+ Glib::ustring id = regex1->replace(name, 0, "_", Glib::REGEX_MATCH_PARTIAL);
+
Glib::ustring tmpSVGOutput;
tmpSVGOutput += "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
tmpSVGOutput += "<svg\n";
@@ -483,17 +501,10 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) {
tmpSVGOutput += " version=\"1.1\"\n";
tmpSVGOutput += " style=\"fill:none;stroke:#000000;stroke-width:2\">\n";
tmpSVGOutput += " <title>";
- tmpSVGOutput += filename;
+ tmpSVGOutput += title;
tmpSVGOutput += "</title>\n";
tmpSVGOutput += " <defs>\n";
- // Create a string we can use for the symbol id (libvisio doesn't give us a name)
- std::string sanitized( filename );
- sanitized.erase( sanitized.find_last_of(".vss")-3 );
- sanitized.erase( std::remove_if( sanitized.begin(), sanitized.end(), ispunct ), sanitized.end() );
- std::replace( sanitized.begin(), sanitized.end(), ' ', '_' );
- // std::cout << filename << " |" << sanitized << "|" << std::endl;
-
// Each "symbol" is in it's own SVG file, we wrap with <symbol> and merge into one file.
for (unsigned i=0; i<output.size(); ++i) {
@@ -501,7 +512,7 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) {
ss << i;
tmpSVGOutput += " <symbol id=\"";
- tmpSVGOutput += sanitized;
+ tmpSVGOutput += id;
tmpSVGOutput += "_";
tmpSVGOutput += ss.str();
tmpSVGOutput += "\">\n";
@@ -521,12 +532,12 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) {
tmpSVGOutput += " </defs>\n";
tmpSVGOutput += "</svg>\n";
-
+
return SPDocument::createNewDocFromMem( tmpSVGOutput.c_str(), strlen( tmpSVGOutput.c_str()), 0 );
}
#endif
-
+
/* Hunts preference directories for symbol files */
void SymbolsDialog::get_symbols() {
@@ -565,11 +576,14 @@ void SymbolsDialog::get_symbols() {
#ifdef WITH_LIBVISIO
if( tag.compare( "vss" ) == 0 ) {
+ // strip extension from filename and use it as name for the symbol set
+ Glib::ustring name = Glib::ustring(filename);
+ name = name.erase(name.rfind('.'));
- symbol_doc = read_vss( fullname, filename );
+ symbol_doc = read_vss( fullname, name );
if( symbol_doc ) {
- symbolSets[Glib::ustring(filename)]= symbol_doc;
- symbolSet->append(filename);
+ symbolSets[name]= symbol_doc;
+ symbolSet->append(name);
}
}
#endif
@@ -630,7 +644,7 @@ GSList* SymbolsDialog::symbols_in_doc( SPDocument* symbolDocument ) {
}
GSList* SymbolsDialog::use_in_doc_recursive (SPObject *r, GSList *l)
-{
+{
if ( dynamic_cast<SPUse *>(r) ) {
l = g_slist_prepend (l, r);
@@ -816,7 +830,7 @@ SPDocument* SymbolsDialog::symbols_preview_doc()
" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\""
" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\""
" xmlns:xlink=\"http://www.w3.org/1999/xlink\">"
-" <defs id=\"defs\">"
+" <defs id=\"defs\">"
" <symbol id=\"the_symbol\"/>"
" </defs>"
" <use id=\"the_use\" xlink:href=\"#the_symbol\"/>"
diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp
index 24665be37..37497ac01 100644
--- a/src/widgets/text-toolbar.cpp
+++ b/src/widgets/text-toolbar.cpp
@@ -2023,7 +2023,7 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje
g_object_set( act, "short_label", "NotUsed", NULL );
gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
- g_object_set_data( holder, "TextDirectAction", act );
+ g_object_set_data( holder, "TextDirectionAction", act );
ege_select_one_action_set_appearance( act, "full" );
ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );