diff options
| author | Martin Owens <doctormo@gmail.com> | 2014-03-27 01:33:44 +0000 |
|---|---|---|
| committer | Martin Owens <doctormo@gmail.com> | 2014-03-27 01:33:44 +0000 |
| commit | 5a4fb2325f60d292b47330f540b26a3279341c90 (patch) | |
| tree | d2aa7967be25450b83e625025366c618101ae49f /src/widgets/stroke-style.cpp | |
| parent | The Polar Arrange Tab of the Arrange Dialog now hides the parametric (diff) | |
| parent | Remove Snap menu item and improve grid menu item text (diff) | |
| download | inkscape-5a4fb2325f60d292b47330f540b26a3279341c90.tar.gz inkscape-5a4fb2325f60d292b47330f540b26a3279341c90.zip | |
Commit a merge to trunk, with probabal errors
(bzr r11073.1.36)
Diffstat (limited to 'src/widgets/stroke-style.cpp')
| -rw-r--r-- | src/widgets/stroke-style.cpp | 1282 |
1 files changed, 699 insertions, 583 deletions
diff --git a/src/widgets/stroke-style.cpp b/src/widgets/stroke-style.cpp index 90bd62847..9567f81ba 100644 --- a/src/widgets/stroke-style.cpp +++ b/src/widgets/stroke-style.cpp @@ -17,308 +17,163 @@ #define noSP_SS_VERBOSE -#include "widgets/dash-selector.h" -#include <gtkmm/radiobutton.h> -#include <gtkmm/table.h> -#include <glibmm/i18n.h> - -#include "desktop-handles.h" -#include "desktop-style.h" -#include "dialogs/dialog-events.h" -#include "display/canvas-bpath.h" // for SP_STROKE_LINEJOIN_* -#include "display/drawing.h" -#include "document-private.h" -#include "document-undo.h" -#include "gradient-chemistry.h" -#include "helper/stock-items.h" -#include "helper/unit-menu.h" -#include "helper/units.h" -#include "inkscape.h" -#include "io/sys.h" -#include "marker.h" -#include "path-prefix.h" -#include "selection.h" -#include "sp-linear-gradient.h" -#include "sp-namedview.h" -#include "sp-pattern.h" -#include "sp-radial-gradient.h" -#include "sp-rect.h" -#include "sp-text.h" -#include "style.h" -#include "svg/css-ostringstream.h" -#include "ui/cache/svg_preview_cache.h" -#include "ui/icon-names.h" -#include "widgets/icon.h" -#include "widgets/paint-selector.h" -#include "widgets/sp-widget.h" -#include "widgets/spw-utilities.h" -#include "ui/widget/spinbutton.h" -#include "xml/repr.h" - #include "stroke-style.h" -#include "stroke-marker-selector.h" -#include "fill-style.h" // to get sp_fill_style_widget_set_desktop -#include "fill-n-stroke-factory.h" - -#include "verbs.h" +#include "../gradient-chemistry.h" +#include "sp-gradient.h" +#include "sp-stop.h" +#include "svg/svg-color.h" +#include "util/units.h" +#include "ui/widget/unit-menu.h" +#include "desktop-widget.h" using Inkscape::DocumentUndo; +using Inkscape::Util::unit_table; - -static MarkerComboBox *start_marker_combobox = NULL; -static MarkerComboBox *mid_marker_combobox = NULL; -static MarkerComboBox *end_marker_combobox = NULL; - -sigc::connection start_marker_connection; -sigc::connection mid_marker_connection; -sigc::connection end_marker_connection; - -static SPObject *ink_extract_marker_name(gchar const *n, SPDocument *doc); -static void ink_markers_combo_update(Gtk::Container* spw, SPMarkerLoc const which); - +/** + * Creates a new widget for the line stroke paint. + */ Gtk::Widget *sp_stroke_style_paint_widget_new(void) { return Inkscape::Widgets::createStyleWidget( STROKE ); } -void sp_stroke_style_widget_set_desktop(Gtk::Widget *widget, SPDesktop *desktop) -{ - sp_fill_style_widget_set_desktop(widget, desktop); -} - -/* Line */ - -static void sp_stroke_style_line_selection_modified(SPWidget *spw, Inkscape::Selection *selection, guint flags, gpointer data); -static void sp_stroke_style_line_selection_changed(SPWidget *spw, Inkscape::Selection *selection, gpointer data); - -static void sp_stroke_style_line_update(Gtk::Container *spw, Inkscape::Selection *sel); - -static void sp_stroke_style_set_join_buttons(Gtk::Container *spw, Gtk::ToggleButton *active); - -static void sp_stroke_style_set_cap_buttons(Gtk::Container *spw, Gtk::ToggleButton *active); - -static void sp_stroke_style_width_changed(Gtk::Container *spw); -static void sp_stroke_style_miterlimit_changed(Gtk::Container *spw); -static void sp_stroke_style_any_toggled(Gtk::ToggleButton *tb, Gtk::Container *spw); -static void sp_stroke_style_line_dash_changed(Gtk::Container *spw); - -static void sp_stroke_style_update_marker_combo(Gtk::Container *spw, GSList const *objects); - - /** - * Helper function for creating radio buttons. This should probably be re-thought out - * when reimplementing this with Gtkmm. + * Creates a new widget for the line stroke style. */ -static Gtk::RadioButton * -sp_stroke_radio_button(Gtk::RadioButton *tb, char const *icon, - Gtk::HBox *hb, Gtk::Container *spw, - gchar const *key, gchar const *data) +Gtk::Widget *sp_stroke_style_line_widget_new(void) { - g_assert(icon != NULL); - g_assert(hb != NULL); - g_assert(spw != NULL); + return Inkscape::Widgets::createStrokeStyleWidget(); +} - if (tb == NULL) { - tb = new Gtk::RadioButton(); - } else { - Gtk::RadioButtonGroup grp = tb->get_group(); - tb = new Gtk::RadioButton(grp); +void sp_stroke_style_widget_set_desktop(Gtk::Widget *widget, SPDesktop *desktop) +{ + Inkscape::StrokeStyle *ss = dynamic_cast<Inkscape::StrokeStyle*>(widget); + if (ss) { + ss->setDesktop(desktop); } - - tb->show(); - tb->set_mode(false); - hb->pack_start(*tb, false, false, 0); - spw->set_data(icon, tb); - tb->set_data(key, (gpointer*)data); - tb->signal_toggled().connect(sigc::bind<Gtk::RadioButton *, Gtk::Container *>( - sigc::ptr_fun(&sp_stroke_style_any_toggled), tb, spw)); - Gtk::Widget *px = manage(Glib::wrap(sp_icon_new(Inkscape::ICON_SIZE_LARGE_TOOLBAR, icon))); - g_assert(px != NULL); - px->show(); - tb->add(*px); - - return tb; - } + /** - * Handles when user selects one of the markers from the marker combobox. - * Gets the marker uri string and applies it to all selected - * items in the current desktop. + * Extract the actual name of the link + * e.g. get mTriangle from url(#mTriangle). + * \return Buffer containing the actual name, allocated from GLib; + * the caller should free the buffer when they no longer need it. */ -static void -sp_marker_select(MarkerComboBox *marker_combo, Gtk::Container *spw, SPMarkerLoc const which) +SPObject* getMarkerObj(gchar const *n, SPDocument *doc) { - if (spw->get_data("update")) { - return; + gchar const *p = n; + while (*p != '\0' && *p != '#') { + p++; } - SPDesktop *desktop = inkscape_active_desktop(); - SPDocument *document = sp_desktop_document(desktop); - if (!document) { - return; + if (*p == '\0' || p[1] == '\0') { + return NULL; } - /* Get Marker */ - gchar const *marker = marker_combo->get_active_marker_uri(); - - - SPCSSAttr *css = sp_repr_css_attr_new(); - gchar const *combo_id = marker_combo->get_id(); - sp_repr_css_set_property(css, combo_id, marker); - - // Also update the marker combobox, so the document's markers - // show up at the top of the combobox -// sp_stroke_style_line_update( SP_WIDGET(spw), desktop ? sp_desktop_selection(desktop) : NULL); - ink_markers_combo_update(spw, which); - - Inkscape::Selection *selection = sp_desktop_selection(desktop); - GSList const *items = selection->itemList(); - for (; items != NULL; items = items->next) { - SPItem *item = reinterpret_cast<SPItem *>(items->data); - if (!SP_IS_SHAPE(item) || SP_IS_RECT(item)) { // can't set marker to rect, until it's converted to using <path> - continue; - } - Inkscape::XML::Node *selrepr = item->getRepr(); - if (selrepr) { - sp_repr_css_change_recursive(selrepr, css, "style"); - } - item->requestModified(SP_OBJECT_MODIFIED_FLAG); - item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + p++; + int c = 0; + while (p[c] != '\0' && p[c] != ')') { + c++; } - sp_repr_css_attr_unref(css); - css = 0; + if (p[c] == '\0') { + return NULL; + } - DocumentUndo::done(document, SP_VERB_DIALOG_FILL_STROKE, - _("Set markers")); + gchar* b = g_strdup(p); + b[c] = '\0'; -}; + // FIXME: get the document from the object and let the caller pass it in + SPObject *marker = doc->getObjectById(b); -static void -ink_markers_combo_update(Gtk::Container* /*spw*/, SPMarkerLoc const which) { + g_free(b); + return marker; +} - switch (which) { - case SP_MARKER_LOC_START: - start_marker_connection.block(); - start_marker_combobox->set_active_history(); - start_marker_connection.unblock(); - break; +namespace Inkscape { - case SP_MARKER_LOC_MID: - mid_marker_connection.block(); - mid_marker_combobox->set_active_history(); - mid_marker_connection.unblock(); - break; - case SP_MARKER_LOC_END: - end_marker_connection.block(); - end_marker_combobox->set_active_history(); - end_marker_connection.unblock(); - break; - default: - g_assert_not_reached(); - } +/** + * Construct a stroke-style radio button with a given icon + * + * \param[in] grp The Gtk::RadioButtonGroup to which to add the new button + * \param[in] icon The icon to use for the button + * \param[in] button_type The type of stroke-style radio button (join/cap) + * \param[in] stroke_style The style attribute to associate with the button + */ +StrokeStyle::StrokeStyleButton::StrokeStyleButton(Gtk::RadioButtonGroup &grp, + char const *icon, + StrokeStyleButtonType button_type, + gchar const *stroke_style) + : + Gtk::RadioButton(grp), + button_type(button_type), + stroke_style(stroke_style) +{ + show(); + set_mode(false); + + Gtk::Widget *px = manage(Glib::wrap(sp_icon_new(Inkscape::ICON_SIZE_LARGE_TOOLBAR, icon))); + g_assert(px != NULL); + px->show(); + add(*px); } /** - * Sets the stroke width units for all selected items. - * Also handles absolute and dimensionless units. + * Create the fill or stroke style widget, and hook up all the signals. */ -static gboolean stroke_width_set_unit(SPUnitSelector *, - SPUnit const *old, - SPUnit const *new_units, - Gtk::Container *spw) +Gtk::Widget *Inkscape::Widgets::createStrokeStyleWidget( ) { - if (spw->get_data("update")) { - return FALSE; - } - - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - - if (!desktop) { - return FALSE; - } - - Inkscape::Selection *selection = sp_desktop_selection (desktop); - - if (selection->isEmpty()) - return FALSE; - - GSList const *objects = selection->itemList(); - - if ((old->base == SP_UNIT_ABSOLUTE || old->base == SP_UNIT_DEVICE) && - (new_units->base == SP_UNIT_DIMENSIONLESS)) { - - /* Absolute to percentage */ - spw->set_data ("update", GUINT_TO_POINTER (TRUE)); - - Gtk::Adjustment *a = static_cast<Gtk::Adjustment *>(spw->get_data("width")); - float w = sp_units_get_pixels (a->get_value(), *old); - - gdouble average = stroke_average_width (objects); + StrokeStyle *strokeStyle = new StrokeStyle(); - if (average == Geom::infinity() || average == 0) - return FALSE; - - a->set_value (100.0 * w / average); - - spw->set_data ("update", GUINT_TO_POINTER (FALSE)); - return TRUE; - - } else if ((old->base == SP_UNIT_DIMENSIONLESS) && - (new_units->base == SP_UNIT_ABSOLUTE || new_units->base == SP_UNIT_DEVICE)) { - - /* Percentage to absolute */ - spw->set_data ("update", GUINT_TO_POINTER (TRUE)); - - Gtk::Adjustment *a = static_cast<Gtk::Adjustment *>(spw->get_data ("width")); - - gdouble average = stroke_average_width (objects); - - a->set_value (sp_pixels_get_units (0.01 * a->get_value() * average, *new_units)); - - spw->set_data ("update", GUINT_TO_POINTER (FALSE)); - return TRUE; - } - - return FALSE; + return strokeStyle; } - -/** - * Creates a new widget for the line stroke style. - */ -Gtk::Container *sp_stroke_style_line_widget_new(void) +StrokeStyle::StrokeStyle() : + Gtk::VBox(), + miterLimitSpin(), + widthSpin(), + unitSelector(), + joinMiter(), + joinRound(), + joinBevel(), + capButt(), + capRound(), + capSquare(), + dashSelector(), + update(false), + desktop(0), + selectChangedConn(), + selectModifiedConn(), + startMarkerConn(), + midMarkerConn(), + endMarkerConn(), + _old_unit(NULL) { - Gtk::Widget *us; - SPDashSelector *ds; - GtkWidget *us_old, *spw_old; - Gtk::Container *spw; - Gtk::Table *t; - Inkscape::UI::Widget::SpinButton *sb; - Gtk::RadioButton *tb; - Gtk::HBox *f, *hb; - - spw_old = sp_widget_new_global(INKSCAPE); - spw = dynamic_cast<Gtk::Container *>(manage(Glib::wrap(spw_old))); - - f = new Gtk::HBox(false, 0); + Gtk::HBox *hb; + Gtk::HBox *f = new Gtk::HBox(false, 0); f->show(); - spw->add(*f); + add(*f); - t = new Gtk::Table(3, 6, false); - t->show(); - t->set_border_width(4); - t->set_row_spacings(4); - f->add(*t); - spw->set_data("stroke", t); +#if WITH_GTKMM_3_0 + table = new Gtk::Grid(); + table->set_border_width(4); + table->set_row_spacing(4); +#else + table = new Gtk::Table(3, 6, false); + table->set_border_width(4); + table->set_row_spacings(4); +#endif + + table->show(); + f->add(*table); gint i = 0; //spw_label(t, C_("Stroke width", "_Width:"), 0, i); - hb = spw_hbox(t, 3, 1, i); + hb = spw_hbox(table, 3, 1, i); // TODO: when this is gtkmmified, use an Inkscape::UI::Widget::ScalarUnit instead of the separate // spinbutton and unit selector for stroke width. In sp_stroke_style_line_update, use @@ -328,73 +183,76 @@ Gtk::Container *sp_stroke_style_line_widget_new(void) // function in desktop-style. #if WITH_GTKMM_3_0 - Glib::RefPtr<Gtk::Adjustment> a = Gtk::Adjustment::create(1.0, 0.0, 1000.0, 0.1, 10.0, 0.0); - spw->set_data("width", &a); - sb = new Inkscape::UI::Widget::SpinButton(a, 0.1, 3); + widthAdj = new Glib::RefPtr<Gtk::Adjustment>(Gtk::Adjustment::create(1.0, 0.0, 1000.0, 0.1, 10.0, 0.0)); #else - Gtk::Adjustment *a = new Gtk::Adjustment(1.0, 0.0, 1000.0, 0.1, 10.0, 0.0); - spw->set_data("width", a); - sb = new Inkscape::UI::Widget::SpinButton(*a, 0.1, 3); + widthAdj = new Gtk::Adjustment(1.0, 0.0, 1000.0, 0.1, 10.0, 0.0); #endif - sb->set_tooltip_text(_("Stroke width")); - sb->show(); - spw_label(t, C_("Stroke width", "_Width:"), 0, i, sb); - sp_dialog_defocus_on_enter_cpp(sb); + widthSpin = new Inkscape::UI::Widget::SpinButton(*widthAdj, 0.1, 3); + widthSpin->set_tooltip_text(_("Stroke width")); + widthSpin->show(); + spw_label(table, C_("Stroke width", "_Width:"), 0, i, widthSpin); + + sp_dialog_defocus_on_enter_cpp(widthSpin); - hb->pack_start(*sb, false, false, 0); - us_old = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE); - us = manage(Glib::wrap(us_old)); + hb->pack_start(*widthSpin, false, false, 0); + unitSelector = new Inkscape::UI::Widget::UnitMenu(); + unitSelector->setUnitType(Inkscape::Util::UNIT_TYPE_LINEAR); + Gtk::Widget *us = manage(unitSelector); SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if (desktop) - sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us_old), sp_desktop_namedview(desktop)->doc_units); - sp_unit_selector_add_unit(SP_UNIT_SELECTOR(us_old), &sp_unit_get_by_id(SP_UNIT_PERCENT), 0); - g_signal_connect ( G_OBJECT (us_old), "set_unit", G_CALLBACK (stroke_width_set_unit), spw ); + + unitSelector->addUnit(*unit_table.getUnit("%")); + _old_unit = unitSelector->getUnit(); + if (desktop) { + unitSelector->setUnit(sp_desktop_namedview(desktop)->doc_units->abbr); + _old_unit = sp_desktop_namedview(desktop)->doc_units; + } + widthSpin->setUnitMenu(unitSelector); + unitChangedConn = unitSelector->signal_changed().connect(sigc::mem_fun(*this, &StrokeStyle::unitChangedCB)); + us->show(); - sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us_old), GTK_ADJUSTMENT(a->gobj()) ); + hb->pack_start(*us, FALSE, FALSE, 0); - spw->set_data("units", us_old); - a->signal_value_changed().connect(sigc::bind(sigc::ptr_fun(&sp_stroke_style_width_changed), spw)); +#if WITH_GTKMM_3_0 + (*widthAdj)->signal_value_changed().connect(sigc::mem_fun(*this, &StrokeStyle::widthChangedCB)); +#else + widthAdj->signal_value_changed().connect(sigc::mem_fun(*this, &StrokeStyle::widthChangedCB)); +#endif i++; /* Join type */ // TRANSLATORS: The line join style specifies the shape to be used at the // corners of paths. It can be "miter", "round" or "bevel". - spw_label(t, _("Join:"), 0, i, NULL); + spw_label(table, _("Join:"), 0, i, NULL); - hb = spw_hbox(t, 3, 1, i); + hb = spw_hbox(table, 3, 1, i); - tb = NULL; + Gtk::RadioButtonGroup joinGrp; - tb = sp_stroke_radio_button(tb, INKSCAPE_ICON("stroke-join-miter"), - hb, spw, "join", "miter"); + joinMiter = makeRadioButton(joinGrp, INKSCAPE_ICON("stroke-join-miter"), + hb, STROKE_STYLE_BUTTON_JOIN, "miter"); // TRANSLATORS: Miter join: joining lines with a sharp (pointed) corner. // For an example, draw a triangle with a large stroke width and modify the // "Join" option (in the Fill and Stroke dialog). - tb->set_tooltip_text(_("Miter join")); - spw->set_data("miter join", tb); - - tb = sp_stroke_radio_button(tb, INKSCAPE_ICON("stroke-join-round"), - hb, spw, "join", "round"); + joinMiter->set_tooltip_text(_("Miter join")); + joinRound = makeRadioButton(joinGrp, INKSCAPE_ICON("stroke-join-round"), + hb, STROKE_STYLE_BUTTON_JOIN, "round"); // TRANSLATORS: Round join: joining lines with a rounded corner. // For an example, draw a triangle with a large stroke width and modify the // "Join" option (in the Fill and Stroke dialog). - tb->set_tooltip_text(_("Round join")); - spw->set_data("round join", tb); - - tb = sp_stroke_radio_button(tb, INKSCAPE_ICON("stroke-join-bevel"), - hb, spw, "join", "bevel"); + joinRound->set_tooltip_text(_("Round join")); + joinBevel = makeRadioButton(joinGrp, INKSCAPE_ICON("stroke-join-bevel"), + hb, STROKE_STYLE_BUTTON_JOIN, "bevel"); // TRANSLATORS: Bevel join: joining lines with a blunted (flattened) corner. // For an example, draw a triangle with a large stroke width and modify the // "Join" option (in the Fill and Stroke dialog). - tb->set_tooltip_text(_("Bevel join")); - spw->set_data("bevel join", tb); + joinBevel->set_tooltip_text(_("Bevel join")); i++; @@ -407,178 +265,480 @@ Gtk::Container *sp_stroke_style_line_widget_new(void) // when they become too long. //spw_label(t, _("Miter _limit:"), 0, i); - hb = spw_hbox(t, 3, 1, i); + hb = spw_hbox(table, 3, 1, i); #if WITH_GTKMM_3_0 - a = Gtk::Adjustment::create(4.0, 0.0, 100.0, 0.1, 10.0, 0.0); - spw->set_data("miterlimit", &a); - sb = new Inkscape::UI::Widget::SpinButton(a, 0.1, 2); + miterLimitAdj = new Glib::RefPtr<Gtk::Adjustment>(Gtk::Adjustment::create(4.0, 0.0, 100.0, 0.1, 10.0, 0.0)); + miterLimitSpin = new Inkscape::UI::Widget::SpinButton(*miterLimitAdj, 0.1, 2); #else - a = new Gtk::Adjustment(4.0, 0.0, 100.0, 0.1, 10.0, 0.0); - spw->set_data("miterlimit", a); - sb = new Inkscape::UI::Widget::SpinButton(*a, 0.1, 2); + miterLimitAdj = new Gtk::Adjustment(4.0, 0.0, 100.0, 0.1, 10.0, 0.0); + miterLimitSpin = new Inkscape::UI::Widget::SpinButton(*miterLimitAdj, 0.1, 2); #endif - sb->set_tooltip_text(_("Maximum length of the miter (in units of stroke width)")); - sb->show(); - spw_label(t, _("Miter _limit:"), 0, i, sb); - spw->set_data("miterlimit_sb", sb); - sp_dialog_defocus_on_enter_cpp(sb); + miterLimitSpin->set_tooltip_text(_("Maximum length of the miter (in units of stroke width)")); + miterLimitSpin->show(); + spw_label(table, _("Miter _limit:"), 0, i, miterLimitSpin); + sp_dialog_defocus_on_enter_cpp(miterLimitSpin); + + hb->pack_start(*miterLimitSpin, false, false, 0); - hb->pack_start(*sb, false, false, 0); +#if WITH_GTKMM_3_0 + (*miterLimitAdj)->signal_value_changed().connect(sigc::mem_fun(*this, &StrokeStyle::miterLimitChangedCB)); - a->signal_value_changed().connect(sigc::bind(sigc::ptr_fun(&sp_stroke_style_miterlimit_changed), spw)); +#else + miterLimitAdj->signal_value_changed().connect(sigc::mem_fun(*this, &StrokeStyle::miterLimitChangedCB)); +#endif i++; /* Cap type */ // TRANSLATORS: cap type specifies the shape for the ends of lines //spw_label(t, _("_Cap:"), 0, i); - spw_label(t, _("Cap:"), 0, i, NULL); + spw_label(table, _("Cap:"), 0, i, NULL); - hb = spw_hbox(t, 3, 1, i); + hb = spw_hbox(table, 3, 1, i); - tb = NULL; + Gtk::RadioButtonGroup capGrp; - tb = sp_stroke_radio_button(tb, INKSCAPE_ICON("stroke-cap-butt"), - hb, spw, "cap", "butt"); - spw->set_data("cap butt", tb); + capButt = makeRadioButton(capGrp, INKSCAPE_ICON("stroke-cap-butt"), + hb, STROKE_STYLE_BUTTON_CAP, "butt"); // TRANSLATORS: Butt cap: the line shape does not extend beyond the end point // of the line; the ends of the line are square - tb->set_tooltip_text(_("Butt cap")); + capButt->set_tooltip_text(_("Butt cap")); - tb = sp_stroke_radio_button(tb, INKSCAPE_ICON("stroke-cap-round"), - hb, spw, "cap", "round"); - spw->set_data("cap round", tb); + capRound = makeRadioButton(capGrp, INKSCAPE_ICON("stroke-cap-round"), + hb, STROKE_STYLE_BUTTON_CAP, "round"); // TRANSLATORS: Round cap: the line shape extends beyond the end point of the // line; the ends of the line are rounded - tb->set_tooltip_text(_("Round cap")); + capRound->set_tooltip_text(_("Round cap")); - tb = sp_stroke_radio_button(tb, INKSCAPE_ICON("stroke-cap-square"), - hb, spw, "cap", "square"); - spw->set_data("cap square", tb); + capSquare = makeRadioButton(capGrp, INKSCAPE_ICON("stroke-cap-square"), + hb, STROKE_STYLE_BUTTON_CAP, "square"); // TRANSLATORS: Square cap: the line shape extends beyond the end point of the // line; the ends of the line are square - tb->set_tooltip_text(_("Square cap")); + capSquare->set_tooltip_text(_("Square cap")); i++; - /* Dash */ - spw_label(t, _("Dashes:"), 0, i, NULL); //no mnemonic for now + spw_label(table, _("Dashes:"), 0, i, NULL); //no mnemonic for now //decide what to do: // implement a set_mnemonic_source function in the // SPDashSelector class, so that we do not have to // expose any of the underlying widgets? - ds = manage(new SPDashSelector); + dashSelector = manage(new SPDashSelector); + + dashSelector->show(); + +#if WITH_GTKMM_3_0 + dashSelector->set_hexpand(); + dashSelector->set_halign(Gtk::ALIGN_FILL); + dashSelector->set_valign(Gtk::ALIGN_CENTER); + table->attach(*dashSelector, 1, i, 3, 1); +#else + table->attach(*dashSelector, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0); +#endif + + dashSelector->changed_signal.connect(sigc::mem_fun(*this, &StrokeStyle::lineDashChangedCB)); - ds->show(); - t->attach(*ds, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0); - spw->set_data("dash", ds); - ds->changed_signal.connect(sigc::bind(sigc::ptr_fun(&sp_stroke_style_line_dash_changed), spw)); i++; /* Drop down marker selectors*/ // TRANSLATORS: Path markers are an SVG feature that allows you to attach arbitrary shapes // (arrowheads, bullets, faces, whatever) to the start, end, or middle nodes of a path. - start_marker_combobox = manage(new MarkerComboBox("marker-start")); - spw_label(t, _("_Start Markers:"), 0, i, start_marker_combobox); - start_marker_combobox->set_tooltip_text(_("Start Markers are drawn on the first node of a path or shape")); - start_marker_connection = start_marker_combobox->signal_changed().connect( - sigc::bind<MarkerComboBox *, Gtk::Container *, SPMarkerLoc>( - sigc::ptr_fun(&sp_marker_select), start_marker_combobox, spw, SP_MARKER_LOC_START)); - start_marker_combobox->show(); - t->attach(*start_marker_combobox, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0); - spw->set_data("start_marker_combobox", start_marker_combobox); - i++; + spw_label(table, _("Markers:"), 0, i, NULL); - mid_marker_combobox = manage(new MarkerComboBox("marker-mid")); - spw_label(t, _("_Mid Markers:"), 0, i, mid_marker_combobox); - mid_marker_combobox->set_tooltip_text(_("Mid Markers are drawn on every node of a path or shape except the first and last nodes")); - mid_marker_connection = mid_marker_combobox->signal_changed().connect( - sigc::bind<MarkerComboBox *, Gtk::Container *, SPMarkerLoc>( - sigc::ptr_fun(&sp_marker_select), mid_marker_combobox, spw, SP_MARKER_LOC_MID)); - mid_marker_combobox->show(); - t->attach(*mid_marker_combobox, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0); - spw->set_data("mid_marker_combobox", mid_marker_combobox); + hb = spw_hbox(table, 1, 1, i); i++; - end_marker_combobox = manage(new MarkerComboBox("marker-end")); - spw_label(t, _("_End Markers:"), 0, i, end_marker_combobox); - end_marker_combobox->set_tooltip_text(_("End Markers are drawn on the last node of a path or shape")); - end_marker_connection = end_marker_combobox->signal_changed().connect( - sigc::bind<MarkerComboBox *, Gtk::Container *, SPMarkerLoc>( - sigc::ptr_fun(&sp_marker_select), end_marker_combobox, spw, SP_MARKER_LOC_END)); - end_marker_combobox->show(); - t->attach(*end_marker_combobox, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0); - spw->set_data("end_marker_combobox", end_marker_combobox); - i++; + startMarkerCombo = manage(new MarkerComboBox("marker-start", SP_MARKER_LOC_START)); + startMarkerCombo->set_tooltip_text(_("Start Markers are drawn on the first node of a path or shape")); + startMarkerConn = startMarkerCombo->signal_changed().connect( + sigc::bind<MarkerComboBox *, StrokeStyle *, SPMarkerLoc>( + sigc::ptr_fun(&StrokeStyle::markerSelectCB), startMarkerCombo, this, SP_MARKER_LOC_START)); + startMarkerCombo->show(); + + hb->pack_start(*startMarkerCombo, true, true, 0); + + midMarkerCombo = manage(new MarkerComboBox("marker-mid", SP_MARKER_LOC_MID)); + midMarkerCombo->set_tooltip_text(_("Mid Markers are drawn on every node of a path or shape except the first and last nodes")); + midMarkerConn = midMarkerCombo->signal_changed().connect( + sigc::bind<MarkerComboBox *, StrokeStyle *, SPMarkerLoc>( + sigc::ptr_fun(&StrokeStyle::markerSelectCB), midMarkerCombo, this, SP_MARKER_LOC_MID)); + midMarkerCombo->show(); + + hb->pack_start(*midMarkerCombo, true, true, 0); + + endMarkerCombo = manage(new MarkerComboBox("marker-end", SP_MARKER_LOC_END)); + endMarkerCombo->set_tooltip_text(_("End Markers are drawn on the last node of a path or shape")); + endMarkerConn = endMarkerCombo->signal_changed().connect( + sigc::bind<MarkerComboBox *, StrokeStyle *, SPMarkerLoc>( + sigc::ptr_fun(&StrokeStyle::markerSelectCB), endMarkerCombo, this, SP_MARKER_LOC_END)); + endMarkerCombo->show(); + + hb->pack_start(*endMarkerCombo, true, true, 0); + + setDesktop(desktop); + updateLine(); + +} + +StrokeStyle::~StrokeStyle() +{ + selectModifiedConn.disconnect(); + selectChangedConn.disconnect(); +} + +void StrokeStyle::setDesktop(SPDesktop *desktop) +{ + if (this->desktop != desktop) { + + if (this->desktop) { + selectModifiedConn.disconnect(); + selectChangedConn.disconnect(); + } + this->desktop = desktop; + if (desktop && desktop->selection) { + selectChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(*this, &StrokeStyle::selectionChangedCB))); + selectModifiedConn = desktop->selection->connectModified(sigc::hide<0>(sigc::mem_fun(*this, &StrokeStyle::selectionModifiedCB))); + } + updateLine(); + } +} + + +/** + * Helper function for creating stroke-style radio buttons. + * + * \param[in] grp The Gtk::RadioButtonGroup in which to add the button + * \param[in] icon The icon for the button + * \param[in] hb The Gtk::Box container in which to add the button + * \param[in] button_type The type (join/cap) for the button + * \param[in] stroke_style The style attribute to associate with the button + * + * \details After instantiating the button, it is added to a container box and + * a handler for the toggle event is connected. + */ +StrokeStyle::StrokeStyleButton * +StrokeStyle::makeRadioButton(Gtk::RadioButtonGroup &grp, + char const *icon, + Gtk::HBox *hb, + StrokeStyleButtonType button_type, + gchar const *stroke_style) +{ + g_assert(icon != NULL); + g_assert(hb != NULL); + + StrokeStyleButton *tb = new StrokeStyleButton(grp, icon, button_type, stroke_style); + + hb->pack_start(*tb, false, false, 0); + set_data(icon, tb); + + tb->signal_toggled().connect(sigc::bind<StrokeStyleButton *, StrokeStyle *>( + sigc::ptr_fun(&StrokeStyle::buttonToggledCB), tb, this)); + + return tb; +} - // FIXME: we cheat and still use gtk+ signals +/** + * Handles when user selects one of the markers from the marker combobox. + * Gets the marker uri string and applies it to all selected + * items in the current desktop. + */ +void StrokeStyle::markerSelectCB(MarkerComboBox *marker_combo, StrokeStyle *spw, SPMarkerLoc const /*which*/) +{ + if (spw->update) { + return; + } - g_signal_connect(G_OBJECT(spw_old), "modify_selection", - G_CALLBACK(sp_stroke_style_line_selection_modified), - spw); - g_signal_connect(G_OBJECT(spw_old), "change_selection", - G_CALLBACK(sp_stroke_style_line_selection_changed), - spw); + spw->update = true; - sp_stroke_style_line_update(spw, desktop ? sp_desktop_selection(desktop) : NULL); + SPDocument *document = sp_desktop_document(spw->desktop); + if (!document) { + return; + } - return spw; + /* Get Marker */ + gchar const *marker = marker_combo->get_active_marker_uri(); + + + SPCSSAttr *css = sp_repr_css_attr_new(); + gchar const *combo_id = marker_combo->get_id(); + sp_repr_css_set_property(css, combo_id, marker); + + // Also update the marker combobox, so the document's markers + // show up at the top of the combobox +// sp_stroke_style_line_update( SP_WIDGET(spw), desktop ? sp_desktop_selection(desktop) : NULL); + //spw->updateMarkerHist(which); + + Inkscape::Selection *selection = sp_desktop_selection(spw->desktop); + GSList const *items = selection->itemList(); + for (; items != NULL; items = items->next) { + SPItem *item = reinterpret_cast<SPItem *>(items->data); + if (!SP_IS_SHAPE(item) || SP_IS_RECT(item)) { // can't set marker to rect, until it's converted to using <path> + continue; + } + Inkscape::XML::Node *selrepr = item->getRepr(); + if (selrepr) { + sp_repr_css_change_recursive(selrepr, css, "style"); + SPObject *markerObj = getMarkerObj(marker, document); + spw->setMarkerColor(markerObj, marker_combo->get_loc(), item); + } + + item->requestModified(SP_OBJECT_MODIFIED_FLAG); + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + + DocumentUndo::done(document, SP_VERB_DIALOG_FILL_STROKE, _("Set markers")); + } + + sp_repr_css_attr_unref(css); + css = 0; + + spw->update = false; +}; + +void StrokeStyle::updateMarkerHist(SPMarkerLoc const which) +{ + switch (which) { + case SP_MARKER_LOC_START: + startMarkerConn.block(); + startMarkerCombo->set_active_history(); + startMarkerConn.unblock(); + break; + + case SP_MARKER_LOC_MID: + midMarkerConn.block(); + midMarkerCombo->set_active_history(); + midMarkerConn.unblock(); + break; + + case SP_MARKER_LOC_END: + endMarkerConn.block(); + endMarkerCombo->set_active_history(); + endMarkerConn.unblock(); + break; + default: + g_assert_not_reached(); + } +} + +/** + * Callback for when UnitMenu widget is modified. + * Triggers update action. + */ +void StrokeStyle::unitChangedCB() +{ + Inkscape::Util::Unit const *new_unit = unitSelector->getUnit(); + if (new_unit->type == Inkscape::Util::UNIT_TYPE_DIMENSIONLESS) { + widthSpin->set_value(100); + } + widthSpin->set_value(Inkscape::Util::Quantity::convert(widthSpin->get_value(), _old_unit, new_unit)); + _old_unit = new_unit; } /** * Callback for when stroke style widget is modified. * Triggers update action. */ -static void -sp_stroke_style_line_selection_modified(SPWidget *, - Inkscape::Selection *selection, - guint flags, - gpointer data) +void +StrokeStyle::selectionModifiedCB(guint flags) { - Gtk::Container *spw = static_cast<Gtk::Container *>(data); if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) { - sp_stroke_style_line_update(spw, selection); + updateLine(); } - } /** * Callback for when stroke style widget is changed. * Triggers update action. */ -static void -sp_stroke_style_line_selection_changed(SPWidget *, - Inkscape::Selection *selection, - gpointer data) +void +StrokeStyle::selectionChangedCB() +{ + updateLine(); +} + +/* + * Fork marker if necessary and set the referencing items url to the new marker + * Return the new marker + */ +SPObject * +StrokeStyle::forkMarker(SPObject *marker, int loc, SPItem *item) +{ + if (!item || !marker) { + return NULL; + } + + gchar const *marker_id = SPMarkerNames[loc].key; + + /* + * Optimization when all the references to this marker are from this item + * then we can reuse it and dont need to fork + */ + Glib::ustring urlId = Glib::ustring::format("url(#", marker->getRepr()->attribute("id"), ")"); + unsigned int refs = 0; + for (int i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) { + if (item->style->marker[i].set && !strcmp(urlId.c_str(), item->style->marker[i].value)) { + refs++; + } + } + if (marker->hrefcount <= refs) { + return marker; + } + + marker = sp_marker_fork_if_necessary(marker); + + // Update the items url to new marker + Inkscape::XML::Node *mark_repr = marker->getRepr(); + SPCSSAttr *css_item = sp_repr_css_attr_new(); + sp_repr_css_set_property(css_item, marker_id, g_strconcat("url(#", mark_repr->attribute("id"), ")", NULL)); + sp_repr_css_change_recursive(item->getRepr(), css_item, "style"); + + sp_repr_css_attr_unref(css_item); + css_item = 0; + + return marker; +} + +/** + * Change the color of the marker to match the color of the item. + * Marker stroke color is set to item stroke color. + * Fill color : + * 1. If the item has fill, use that for the marker fill, + * 2. If the marker has same fill and stroke assume its solid, use item stroke for both fill and stroke the line stroke + * 3. If the marker has fill color, use the marker fill color + * + */ +void +StrokeStyle::setMarkerColor(SPObject *marker, int loc, SPItem *item) { - Gtk::Container *spw = static_cast<Gtk::Container *>(data); - sp_stroke_style_line_update(spw, selection); + + if (!item || !marker) { + return; + } + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gboolean colorStock = prefs->getBool("/options/markers/colorStockMarkers", true); + gboolean colorCustom = prefs->getBool("/options/markers/colorCustomMarkers", false); + const gchar *stock = marker->getRepr()->attribute("inkscape:isstock"); + gboolean isStock = (stock && !strcmp(stock,"true")); + + if (isStock ? !colorStock : !colorCustom) { + return; + } + + // Check if we need to fork this marker + marker = forkMarker(marker, loc, item); + + Inkscape::XML::Node *repr = marker->getRepr()->firstChild(); + if (!repr) { + return; + }; + + // Current line style + SPCSSAttr *css_item = sp_css_attr_from_object(item, SP_STYLE_FLAG_ALWAYS); + const char *lstroke = getItemColorForMarker(item, FOR_STROKE, loc); + const char *lstroke_opacity = sp_repr_css_property(css_item, "stroke-opacity", "1"); + const char *lfill = getItemColorForMarker(item, FOR_FILL, loc); + const char *lfill_opacity = sp_repr_css_property(css_item, "fill-opacity", "1"); + + // Current marker style + SPCSSAttr *css_marker = sp_css_attr_from_object(marker->firstChild(), SP_STYLE_FLAG_ALWAYS); + const char *mfill = sp_repr_css_property(css_marker, "fill", "none"); + const char *mstroke = sp_repr_css_property(css_marker, "fill", "none"); + + // Create new marker style with the lines stroke + SPCSSAttr *css = sp_repr_css_attr_new(); + + sp_repr_css_set_property(css, "stroke", lstroke); + sp_repr_css_set_property(css, "stroke-opacity", lstroke_opacity); + + if (strcmp(lfill, "none") ) { + // 1. If the line has fill, use that for the marker fill + sp_repr_css_set_property(css, "fill", lfill); + sp_repr_css_set_property(css, "fill-opacity", lfill_opacity); + } + else if (mfill && mstroke && !strcmp(mfill, mstroke) && mfill[0] == '#' && strcmp(mfill, "#ffffff")) { + // 2. If the marker has same fill and stroke assume its solid. use line stroke for both fill and stroke the line stroke + sp_repr_css_set_property(css, "fill", lstroke); + sp_repr_css_set_property(css, "fill-opacity", lstroke_opacity); + } + else if (mfill && mfill[0] == '#' && strcmp(mfill, "#000000")) { + // 3. If the marker has fill color, use the marker fill color + sp_repr_css_set_property(css, "fill", mfill); + //sp_repr_css_set_property(css, "fill-opacity", mfill_opacity); + } + + sp_repr_css_change_recursive(marker->firstChild()->getRepr(), css, "style"); + + // Tell the combos to update its image cache of this marker + gchar const *mid = marker->getRepr()->attribute("id"); + startMarkerCombo->update_marker_image(mid); + midMarkerCombo->update_marker_image(mid); + endMarkerCombo->update_marker_image(mid); + + sp_repr_css_attr_unref(css); + css = 0; + + } +/* + * Get the fill or stroke color of the item + * If its a gradient, then return first or last stop color + */ +const char * +StrokeStyle::getItemColorForMarker(SPItem *item, Inkscape::PaintTarget fill_or_stroke, int loc) +{ + SPCSSAttr *css_item = sp_css_attr_from_object(item, SP_STYLE_FLAG_ALWAYS); + const char *color; + if (fill_or_stroke == FOR_FILL) + color = sp_repr_css_property(css_item, "fill", "none"); + else + color = sp_repr_css_property(css_item, "stroke", "none"); + + if (!strncmp (color, "url(", 4)) { + // If the item has a gradient use the first stop color for the marker + + SPGradient *grad = getGradient(item, fill_or_stroke); + if (grad) { + SPGradient *vector = grad->getVector(FALSE); + SPStop *stop = vector->getFirstStop(); + if (loc == SP_MARKER_LOC_END) { + stop = sp_last_stop(vector); + } + if (stop) { + guint32 const c1 = stop->get_rgba32(); + gchar c[64]; + sp_svg_write_color(c, sizeof(c), c1); + color = g_strdup(c); + //lstroke_opacity = Glib::ustring::format(stop->opacity).c_str(); + } + } + } + return color; +} /** * Sets selector widgets' dash style from an SPStyle object. */ -static void -sp_dash_selector_set_from_style(SPDashSelector *dsel, SPStyle *style) +void +StrokeStyle::setDashSelectorFromStyle(SPDashSelector *dsel, SPStyle *style) { - if (style->stroke_dash.n_dash > 0) { + if (!style->stroke_dasharray.values.empty()) { double d[64]; - int len = MIN(style->stroke_dash.n_dash, 64); - for (int i = 0; i < len; i++) { + size_t len = MIN(style->stroke_dasharray.values.size(), 64); + for (unsigned i = 0; i < len; i++) { if (style->stroke_width.computed != 0) - d[i] = style->stroke_dash.dash[i] / style->stroke_width.computed; + d[i] = style->stroke_dasharray.values[i] / style->stroke_width.computed; else - d[i] = style->stroke_dash.dash[i]; // is there a better thing to do for stroke_width==0? + d[i] = style->stroke_dasharray.values[i]; // is there a better thing to do for stroke_width==0? } dsel->set_dash(len, d, style->stroke_width.computed != 0 ? - style->stroke_dash.offset / style->stroke_width.computed : - style->stroke_dash.offset); + style->stroke_dashoffset.value / style->stroke_width.computed : + style->stroke_dashoffset.value); } else { dsel->set_dash(0, NULL, 0.0); } @@ -587,69 +747,65 @@ sp_dash_selector_set_from_style(SPDashSelector *dsel, SPStyle *style) /** * Sets the join type for a line, and updates the stroke style widget's buttons */ -static void -sp_jointype_set (Gtk::Container *spw, unsigned const jointype) +void +StrokeStyle::setJoinType (unsigned const jointype) { Gtk::RadioButton *tb = NULL; switch (jointype) { case SP_STROKE_LINEJOIN_MITER: - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-join-miter"))); + tb = joinMiter; break; case SP_STROKE_LINEJOIN_ROUND: - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-join-round"))); + tb = joinRound; break; case SP_STROKE_LINEJOIN_BEVEL: - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-join-bevel"))); + tb = joinBevel; break; default: break; } - sp_stroke_style_set_join_buttons(spw, tb); + setJoinButtons(tb); } /** * Sets the cap type for a line, and updates the stroke style widget's buttons */ -static void -sp_captype_set (Gtk::Container *spw, unsigned const captype) +void +StrokeStyle::setCapType (unsigned const captype) { Gtk::RadioButton *tb = NULL; switch (captype) { case SP_STROKE_LINECAP_BUTT: - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-cap-butt"))); + tb = capButt; break; case SP_STROKE_LINECAP_ROUND: - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-cap-round"))); + tb = capRound; break; case SP_STROKE_LINECAP_SQUARE: - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-cap-square"))); + tb = capSquare; break; default: break; } - sp_stroke_style_set_cap_buttons(spw, tb); + setCapButtons(tb); } /** * Callback for when stroke style widget is updated, including markers, cap type, * join type, etc. */ -static void -sp_stroke_style_line_update(Gtk::Container *spw, Inkscape::Selection *sel) +void +StrokeStyle::updateLine() { - if (spw->get_data("update")) { + if (update) { return; } - spw->set_data("update", GINT_TO_POINTER(TRUE)); + update = true; - FillOrStroke kind = GPOINTER_TO_INT(spw->get_data("kind")) ? FILL : STROKE; + Inkscape::Selection *sel = desktop ? sp_desktop_selection(desktop) : NULL; - Gtk::Table *sset = static_cast<Gtk::Table *>(spw->get_data("stroke")); - Gtk::Adjustment *width = static_cast<Gtk::Adjustment *>(spw->get_data("width")); - Gtk::Adjustment *ml = static_cast<Gtk::Adjustment *>(spw->get_data("miterlimit")); - SPUnitSelector *us = SP_UNIT_SELECTOR(spw->get_data("units")); - SPDashSelector *dsel = static_cast<SPDashSelector *>(spw->get_data("dash")); + FillOrStroke kind = GPOINTER_TO_INT(get_data("kind")) ? FILL : STROKE; // create temporary style SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT); @@ -662,73 +818,75 @@ sp_stroke_style_line_update(Gtk::Container *spw, Inkscape::Selection *sel) if (!sel || sel->isEmpty()) { // Nothing selected, grey-out all controls in the stroke-style dialog - sset->set_sensitive(false); + table->set_sensitive(false); - spw->set_data("update", GINT_TO_POINTER(FALSE)); + update = false; return; } else { - sset->set_sensitive(true); - - SPUnit const *unit = sp_unit_selector_get_unit(us); + table->set_sensitive(true); if (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED) { - sp_unit_selector_set_unit(us, &sp_unit_get_by_id(SP_UNIT_PERCENT)); + unitSelector->setUnit("%"); } else { // same width, or only one object; no sense to keep percent, switch to absolute - if (unit->base != SP_UNIT_ABSOLUTE && unit->base != SP_UNIT_DEVICE) { - sp_unit_selector_set_unit(us, sp_desktop_namedview(SP_ACTIVE_DESKTOP)->doc_units); + Inkscape::Util::Unit const *tempunit = unitSelector->getUnit(); + if (tempunit->type != Inkscape::Util::UNIT_TYPE_LINEAR) { + unitSelector->setUnit(sp_desktop_namedview(SP_ACTIVE_DESKTOP)->doc_units->abbr); } } - unit = sp_unit_selector_get_unit(us); + Inkscape::Util::Unit const *unit = unitSelector->getUnit(); - if (unit->base == SP_UNIT_ABSOLUTE || unit->base == SP_UNIT_DEVICE) { - double avgwidth = sp_pixels_get_units (query->stroke_width.computed, *unit); - width->set_value(avgwidth); + if (unit->type == Inkscape::Util::UNIT_TYPE_LINEAR) { + double avgwidth = Inkscape::Util::Quantity::convert(query->stroke_width.computed, "px", unit); +#if WITH_GTKMM_3_0 + (*widthAdj)->set_value(avgwidth); +#else + widthAdj->set_value(avgwidth); +#endif } else { - width->set_value(100); +#if WITH_GTKMM_3_0 + (*widthAdj)->set_value(100); +#else + widthAdj->set_value(100); +#endif } // if none of the selected objects has a stroke, than quite some controls should be disabled // The markers might still be shown though, so these will not be disabled bool enabled = (result_sw != QUERY_STYLE_NOTHING) && !targPaint.isNoneSet(); /* No objects stroked, set insensitive */ - Gtk::RadioButton *tb = NULL; - tb = static_cast<Gtk::RadioButton *>(spw->get_data("miter join")); - tb->set_sensitive(enabled); - tb = static_cast<Gtk::RadioButton *>(spw->get_data("round join")); - tb->set_sensitive(enabled); - tb = static_cast<Gtk::RadioButton *>(spw->get_data("bevel join")); - tb->set_sensitive(enabled); - - Inkscape::UI::Widget::SpinButton* sb = NULL; - sb = static_cast<Inkscape::UI::Widget::SpinButton *>(spw->get_data("miterlimit_sb")); - sb->set_sensitive(enabled); - - tb = static_cast<Gtk::RadioButton *>(spw->get_data("cap butt")); - tb->set_sensitive(enabled); - tb = static_cast<Gtk::RadioButton *>(spw->get_data("cap round")); - tb->set_sensitive(enabled); - tb = static_cast<Gtk::RadioButton *>(spw->get_data("cap square")); - tb->set_sensitive(enabled); - - dsel->set_sensitive(enabled); + joinMiter->set_sensitive(enabled); + joinRound->set_sensitive(enabled); + joinBevel->set_sensitive(enabled); + + miterLimitSpin->set_sensitive(enabled); + + capButt->set_sensitive(enabled); + capRound->set_sensitive(enabled); + capSquare->set_sensitive(enabled); + + dashSelector->set_sensitive(enabled); } if (result_ml != QUERY_STYLE_NOTHING) - ml->set_value(query->stroke_miterlimit.value); // TODO: reflect averagedness? +#if WITH_GTKMM_3_0 + (*miterLimitAdj)->set_value(query->stroke_miterlimit.value); // TODO: reflect averagedness? +#else + miterLimitAdj->set_value(query->stroke_miterlimit.value); // TODO: reflect averagedness? +#endif if (result_join != QUERY_STYLE_MULTIPLE_DIFFERENT) { - sp_jointype_set(spw, query->stroke_linejoin.value); + setJoinType(query->stroke_linejoin.value); } else { - sp_stroke_style_set_join_buttons(spw, NULL); + setJoinButtons(NULL); } if (result_cap != QUERY_STYLE_MULTIPLE_DIFFERENT) { - sp_captype_set (spw, query->stroke_linecap.value); + setCapType (query->stroke_linecap.value); } else { - sp_stroke_style_set_cap_buttons(spw, NULL); + setCapButtons(NULL); } sp_style_unref(query); @@ -741,21 +899,21 @@ sp_stroke_style_line_update(Gtk::Container *spw, Inkscape::Selection *sel) SPStyle * const style = object->style; /* Markers */ - sp_stroke_style_update_marker_combo(spw, objects); // FIXME: make this desktop query too + updateAllMarkers(objects); // FIXME: make this desktop query too /* Dash */ - sp_dash_selector_set_from_style(dsel, style); // FIXME: make this desktop query too + setDashSelectorFromStyle(dashSelector, style); // FIXME: make this desktop query too - sset->set_sensitive(true); + table->set_sensitive(true); - spw->set_data("update", GINT_TO_POINTER(FALSE)); + update = false; } /** * Sets a line's dash properties in a CSS style object. */ -static void -sp_stroke_style_set_scaled_dash(SPCSSAttr *css, +void +StrokeStyle::setScaledDash(SPCSSAttr *css, int ndash, double *dash, double offset, double scale) { @@ -781,21 +939,15 @@ sp_stroke_style_set_scaled_dash(SPCSSAttr *css, /** * Sets line properties like width, dashes, markers, etc. on all currently selected items. */ -static void -sp_stroke_style_scale_line(Gtk::Container *spw) +void +StrokeStyle::scaleLine() { - if (spw->get_data("update")) { + if (update) { return; } - spw->set_data("update", GINT_TO_POINTER(TRUE)); - - Gtk::Adjustment *wadj = static_cast<Gtk::Adjustment *>(spw->get_data("width")); - SPUnitSelector *us = SP_UNIT_SELECTOR(spw->get_data("units")); - SPDashSelector *dsel = static_cast<SPDashSelector *>(spw->get_data("dash")); - Gtk::Adjustment *ml = static_cast<Gtk::Adjustment *>(spw->get_data("miterlimit")); - - SPDesktop *desktop = SP_ACTIVE_DESKTOP; + update = true; + SPDocument *document = sp_desktop_document (desktop); Inkscape::Selection *selection = sp_desktop_selection (desktop); @@ -805,21 +957,25 @@ sp_stroke_style_scale_line(Gtk::Container *spw) SPCSSAttr *css = sp_repr_css_attr_new(); if (items) { +#if WITH_GTKMM_3_0 + double width_typed = (*widthAdj)->get_value(); + double const miterlimit = (*miterLimitAdj)->get_value(); +#else + double width_typed = widthAdj->get_value(); + double const miterlimit = miterLimitAdj->get_value(); +#endif - double width_typed = wadj->get_value(); - double const miterlimit = ml->get_value(); - - SPUnit const *const unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us)); + Inkscape::Util::Unit const *const unit = unitSelector->getUnit(); double *dash, offset; int ndash; - dsel->get_dash(&ndash, &dash, &offset); + dashSelector->get_dash(&ndash, &dash, &offset); for (GSList const *i = items; i != NULL; i = i->next) { /* Set stroke width */ double width; - if (unit->base == SP_UNIT_ABSOLUTE || unit->base == SP_UNIT_DEVICE) { - width = sp_units_get_pixels (width_typed, *unit); + if (unit->type == Inkscape::Util::UNIT_TYPE_LINEAR) { + width = Inkscape::Util::Quantity::convert(width_typed, unit, "px"); } else { // percentage gdouble old_w = SP_OBJECT(i->data)->style->stroke_width.computed; width = old_w * width_typed / 100; @@ -838,16 +994,20 @@ sp_stroke_style_scale_line(Gtk::Container *spw) } /* Set dash */ - sp_stroke_style_set_scaled_dash(css, ndash, dash, offset, width); + setScaledDash(css, ndash, dash, offset, width); sp_desktop_apply_css_recursive (SP_OBJECT(i->data), css, true); } g_free(dash); - if (unit->base != SP_UNIT_ABSOLUTE && unit->base != SP_UNIT_DEVICE) { + if (unit->type != Inkscape::Util::UNIT_TYPE_LINEAR) { // reset to 100 percent - wadj->set_value(100.0); +#if WITH_GTKMM_3_0 + (*widthAdj)->set_value(100.0); +#else + widthAdj->set_value(100.0); +#endif } } @@ -862,35 +1022,35 @@ sp_stroke_style_scale_line(Gtk::Container *spw) DocumentUndo::done(document, SP_VERB_DIALOG_FILL_STROKE, _("Set stroke style")); - spw->set_data("update", GINT_TO_POINTER(FALSE)); + update = false; } /** * Callback for when the stroke style's width changes. * Causes all line styles to be applied to all selected items. */ -static void -sp_stroke_style_width_changed(Gtk::Container *spw) +void +StrokeStyle::widthChangedCB() { - if (spw->get_data("update")) { + if (update) { return; } - sp_stroke_style_scale_line(spw); + scaleLine(); } /** * Callback for when the stroke style's miterlimit changes. * Causes all line styles to be applied to all selected items. */ -static void -sp_stroke_style_miterlimit_changed(Gtk::Container *spw) +void +StrokeStyle::miterLimitChangedCB() { - if (spw->get_data("update")) { + if (update) { return; } - sp_stroke_style_scale_line(spw); + scaleLine(); } /** @@ -898,14 +1058,14 @@ sp_stroke_style_miterlimit_changed(Gtk::Container *spw) * Causes all line styles to be applied to all selected items. */ -static void -sp_stroke_style_line_dash_changed(Gtk::Container *spw) +void +StrokeStyle::lineDashChangedCB() { - if (spw->get_data("update")) { + if (update) { return; } - sp_stroke_style_scale_line(spw); + scaleLine(); } /** @@ -915,48 +1075,36 @@ sp_stroke_style_line_dash_changed(Gtk::Container *spw) * calls the respective routines to update css properties, etc. * */ -static void sp_stroke_style_any_toggled(Gtk::ToggleButton *tb, Gtk::Container *spw) +void StrokeStyle::buttonToggledCB(StrokeStyleButton *tb, StrokeStyle *spw) { - if (spw->get_data("update")) { + if (spw->update) { return; } - if (tb->get_active()) { - - gchar const *join - = static_cast<gchar const *>(tb->get_data("join")); - gchar const *cap - = static_cast<gchar const *>(tb->get_data("cap")); - - if (join) { - Gtk::SpinButton *ml = static_cast<Gtk::SpinButton *>(spw->get_data("miterlimit_sb")); - ml->set_sensitive(!strcmp(join, "miter")); + if (tb->get_button_type() == STROKE_STYLE_BUTTON_JOIN) { + spw->miterLimitSpin->set_sensitive(!strcmp(tb->get_stroke_style(), "miter")); } - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - /* TODO: Create some standardized method */ SPCSSAttr *css = sp_repr_css_attr_new(); - if (join) { - sp_repr_css_set_property(css, "stroke-linejoin", join); - - sp_desktop_set_style (desktop, css); - - sp_stroke_style_set_join_buttons(spw, tb); - } else if (cap) { - sp_repr_css_set_property(css, "stroke-linecap", cap); - - sp_desktop_set_style (desktop, css); - - sp_stroke_style_set_cap_buttons(spw, tb); + switch (tb->get_button_type()) { + case STROKE_STYLE_BUTTON_JOIN: + sp_repr_css_set_property(css, "stroke-linejoin", tb->get_stroke_style()); + sp_desktop_set_style (spw->desktop, css); + spw->setJoinButtons(tb); + break; + case STROKE_STYLE_BUTTON_CAP: + sp_repr_css_set_property(css, "stroke-linecap", tb->get_stroke_style()); + sp_desktop_set_style (spw->desktop, css); + spw->setCapButtons(tb); } sp_repr_css_attr_unref(css); css = 0; - DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_DIALOG_FILL_STROKE, + DocumentUndo::done(sp_desktop_document(spw->desktop), SP_VERB_DIALOG_FILL_STROKE, _("Set stroke style")); } } @@ -964,38 +1112,24 @@ static void sp_stroke_style_any_toggled(Gtk::ToggleButton *tb, Gtk::Container *s /** * Updates the join style toggle buttons */ -static void -sp_stroke_style_set_join_buttons(Gtk::Container *spw, Gtk::ToggleButton *active) +void +StrokeStyle::setJoinButtons(Gtk::ToggleButton *active) { - Gtk::RadioButton *tb; - - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-join-miter"))); - tb->set_active(active == tb); - - Gtk::SpinButton *ml = static_cast<Gtk::SpinButton *>(spw->get_data("miterlimit_sb")); - ml->set_sensitive(active == tb); - - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-join-round"))); - tb->set_active(active == tb); - - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-join-bevel"))); - tb->set_active(active == tb); + joinMiter->set_active(active == joinMiter); + miterLimitSpin->set_sensitive(active == joinMiter); + joinRound->set_active(active == joinRound); + joinBevel->set_active(active == joinBevel); } /** * Updates the cap style toggle buttons */ -static void -sp_stroke_style_set_cap_buttons(Gtk::Container *spw, Gtk::ToggleButton *active) +void +StrokeStyle::setCapButtons(Gtk::ToggleButton *active) { - Gtk::RadioButton *tb; - - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-cap-butt"))); - tb->set_active(active == tb); - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-cap-round"))); - tb->set_active(active == tb); - tb = static_cast<Gtk::RadioButton *>(spw->get_data(INKSCAPE_ICON("stroke-cap-square"))); - tb->set_active(active == tb); + capButt->set_active(active == capButt); + capRound->set_active(active == capRound); + capSquare->set_active(active == capSquare); } @@ -1003,13 +1137,13 @@ sp_stroke_style_set_cap_buttons(Gtk::Container *spw, Gtk::ToggleButton *active) * Updates the marker combobox to highlight the appropriate marker and scroll to * that marker. */ -static void -sp_stroke_style_update_marker_combo(Gtk::Container *spw, GSList const *objects) +void +StrokeStyle::updateAllMarkers(GSList const *objects) { - struct { char const *key; int loc; } const keyloc[] = { - { "start_marker_combobox", SP_MARKER_LOC_START }, - { "mid_marker_combobox", SP_MARKER_LOC_MID }, - { "end_marker_combobox", SP_MARKER_LOC_END } + struct { MarkerComboBox *key; int loc; } const keyloc[] = { + { startMarkerCombo, SP_MARKER_LOC_START }, + { midMarkerCombo, SP_MARKER_LOC_MID }, + { endMarkerCombo, SP_MARKER_LOC_END } }; bool all_texts = true; @@ -1020,7 +1154,7 @@ sp_stroke_style_update_marker_combo(Gtk::Container *spw, GSList const *objects) } for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) { - MarkerComboBox *combo = static_cast<MarkerComboBox *>(spw->get_data(keyloc[i].key)); + MarkerComboBox *combo = static_cast<MarkerComboBox *>(keyloc[i].key); // Per SVG spec, text objects cannot have markers; disable combobox if only texts are selected combo->set_sensitive(!all_texts); } @@ -1033,21 +1167,35 @@ sp_stroke_style_update_marker_combo(Gtk::Container *spw, GSList const *objects) // For all three marker types, // find the corresponding combobox item - MarkerComboBox *combo = static_cast<MarkerComboBox *>(spw->get_data(keyloc[i].key)); + MarkerComboBox *combo = static_cast<MarkerComboBox *>(keyloc[i].key); // Quit if we're in update state if (combo->update()) { return; } + combo->setDesktop(desktop); + if (object->style->marker[keyloc[i].loc].value != NULL && !all_texts) { // If the object has this type of markers, // Extract the name of the marker that the object uses - SPObject *marker = ink_extract_marker_name(object->style->marker[keyloc[i].loc].value, object->document); + SPObject *marker = getMarkerObj(object->style->marker[keyloc[i].loc].value, object->document); // Scroll the combobox to that marker combo->set_current(marker); + // Set the marker color + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gboolean update = prefs->getBool("/options/markers/colorUpdateMarkers", true); + + if (update) { + setMarkerColor(marker, combo->get_loc(), SP_ITEM(object)); + + SPDocument *document = sp_desktop_document(desktop); + DocumentUndo::done(document, SP_VERB_DIALOG_FILL_STROKE, + _("Set marker color")); + } + } else { combo->set_current(NULL); } @@ -1056,41 +1204,9 @@ sp_stroke_style_update_marker_combo(Gtk::Container *spw, GSList const *objects) } -/** - * Extract the actual name of the link - * e.g. get mTriangle from url(#mTriangle). - * \return Buffer containing the actual name, allocated from GLib; - * the caller should free the buffer when they no longer need it. - */ -static SPObject* -ink_extract_marker_name(gchar const *n, SPDocument *doc) -{ - gchar const *p = n; - while (*p != '\0' && *p != '#') { - p++; - } - - if (*p == '\0' || p[1] == '\0') { - return NULL; - } - - p++; - int c = 0; - while (p[c] != '\0' && p[c] != ')') { - c++; - } - - if (p[c] == '\0') { - return NULL; - } - gchar* b = g_strdup(p); - b[c] = '\0'; +} // namespace Inkscape - // FIXME: get the document from the object and let the caller pass it in - SPObject *marker = doc->getObjectById(b); - return marker; -} /* Local Variables: |
