diff options
| author | MenTaLguY <mental@rydia.net> | 2006-01-16 02:36:01 +0000 |
|---|---|---|
| committer | mental <mental@users.sourceforge.net> | 2006-01-16 02:36:01 +0000 |
| commit | 179fa413b047bede6e32109e2ce82437c5fb8d34 (patch) | |
| tree | a5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/dialogs/stroke-style.cpp | |
| download | inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip | |
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/dialogs/stroke-style.cpp')
| -rw-r--r-- | src/dialogs/stroke-style.cpp | 1726 |
1 files changed, 1726 insertions, 0 deletions
diff --git a/src/dialogs/stroke-style.cpp b/src/dialogs/stroke-style.cpp new file mode 100644 index 000000000..b252cd125 --- /dev/null +++ b/src/dialogs/stroke-style.cpp @@ -0,0 +1,1726 @@ +#define __SP_STROKE_STYLE_C__ + +/** + * \brief Stroke style dialog + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Bryce Harrington <brycehar@bryceharrington.com> + * bulia byak <buliabyak@users.sf.net> + * + * Copyright (C) 2001-2005 authors + * Copyright (C) 2001 Ximian, Inc. + * Copyright (C) 2004 John Cliff + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#define noSP_SS_VERBOSE + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + + +#include <gtk/gtk.h> + +#include <glibmm/i18n.h> +#include "helper/unit-menu.h" +#include "helper/units.h" +#include "svg/css-ostringstream.h" +#include "widgets/sp-widget.h" +#include "widgets/spw-utilities.h" +#include "sp-linear-gradient.h" +#include "sp-radial-gradient.h" +#include "sp-marker.h" +#include <sp-pattern.h> +#include <widgets/paint-selector.h> +#include <widgets/dash-selector.h> +#include "style.h" +#include "gradient-chemistry.h" +#include "sp-namedview.h" +#include "desktop-handles.h" +#include "desktop-style.h" +#include "selection.h" +#include "inkscape.h" +#include "inkscape-stock.h" +#include "dialogs/dialog-events.h" +#include "sp-text.h" +#include "sp-rect.h" +#include "document-private.h" +#include "display/nr-arena.h" +#include "display/nr-arena-item.h" +#include "path-prefix.h" +#include "widgets/icon.h" +#include "helper/stock-items.h" +#include "io/sys.h" + +#include "dialogs/stroke-style.h" + +/* Paint */ + +static void sp_stroke_style_paint_construct(SPWidget *spw, SPPaintSelector *psel); +static void sp_stroke_style_paint_selection_modified (SPWidget *spw, Inkscape::Selection *selection, guint flags, SPPaintSelector *psel); +static void sp_stroke_style_paint_selection_changed (SPWidget *spw, Inkscape::Selection *selection, SPPaintSelector *psel); +static void sp_stroke_style_paint_update(SPWidget *spw); + +static void sp_stroke_style_paint_mode_changed(SPPaintSelector *psel, SPPaintSelectorMode mode, SPWidget *spw); +static void sp_stroke_style_paint_dragged(SPPaintSelector *psel, SPWidget *spw); +static void sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw); + +static void sp_stroke_style_widget_change_subselection ( Inkscape::Application *inkscape, SPDesktop *desktop, SPWidget *spw ); + +GtkWidget * +sp_stroke_style_paint_widget_new(void) +{ + GtkWidget *spw, *psel; + + spw = sp_widget_new_global(INKSCAPE); + + psel = sp_paint_selector_new(false); // without fillrule selector + gtk_widget_show(psel); + gtk_container_add(GTK_CONTAINER(spw), psel); + gtk_object_set_data(GTK_OBJECT(spw), "paint-selector", psel); + + gtk_signal_connect(GTK_OBJECT(spw), "construct", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_construct), + psel); + gtk_signal_connect(GTK_OBJECT(spw), "modify_selection", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_selection_modified), + psel); + gtk_signal_connect(GTK_OBJECT(spw), "change_selection", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_selection_changed), + psel); + + g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_stroke_style_widget_change_subselection), spw); + + gtk_signal_connect(GTK_OBJECT(psel), "mode_changed", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_mode_changed), + spw); + gtk_signal_connect(GTK_OBJECT(psel), "dragged", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_dragged), + spw); + gtk_signal_connect(GTK_OBJECT(psel), "changed", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_changed), + spw); + + sp_stroke_style_paint_update (SP_WIDGET(spw)); + return spw; +} + +static void +sp_stroke_style_paint_construct(SPWidget *spw, SPPaintSelector *psel) +{ +#ifdef SP_SS_VERBOSE + g_print( "Stroke style widget constructed: inkscape %p repr %p\n", + spw->inkscape, spw->repr ); +#endif + if (spw->inkscape) { + sp_stroke_style_paint_update (spw); + } +} + +static void +sp_stroke_style_paint_selection_modified ( SPWidget *spw, + Inkscape::Selection *selection, + guint flags, + SPPaintSelector *psel) +{ + if (flags & ( SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG | + SP_OBJECT_STYLE_MODIFIED_FLAG) ) { + sp_stroke_style_paint_update(spw); + } +} + + +static void +sp_stroke_style_paint_selection_changed ( SPWidget *spw, + Inkscape::Selection *selection, + SPPaintSelector *psel ) +{ + sp_stroke_style_paint_update (spw); +} + +static void +sp_stroke_style_widget_change_subselection ( Inkscape::Application *inkscape, + SPDesktop *desktop, + SPWidget *spw ) +{ + sp_stroke_style_paint_update (spw); +} + +static void +sp_stroke_style_paint_update (SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE)); + + SPPaintSelector *psel = SP_PAINT_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "paint-selector")); + + // create temporary style + SPStyle *query = sp_style_new (); + // query into it + int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKE); + + switch (result) { + case QUERY_STYLE_NOTHING: + { + /* No paint at all */ + sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY); + break; + } + + case QUERY_STYLE_SINGLE: + case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector + case QUERY_STYLE_MULTIPLE_SAME: + { + SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, false); + sp_paint_selector_set_mode (psel, pselmode); + + if (query->stroke.set && query->stroke.type == SP_PAINT_TYPE_COLOR) { + gfloat d[3]; + sp_color_get_rgb_floatv (&query->stroke.value.color, d); + SPColor color; + sp_color_set_rgb_float (&color, d[0], d[1], d[2]); + sp_paint_selector_set_color_alpha (psel, &color, SP_SCALE24_TO_FLOAT (query->stroke_opacity.value)); + + } else if (query->stroke.set && query->stroke.type == SP_PAINT_TYPE_PAINTSERVER) { + + SPPaintServer *server = SP_STYLE_STROKE_SERVER (query); + + if (SP_IS_LINEARGRADIENT (server)) { + SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE); + sp_paint_selector_set_gradient_linear (psel, vector); + + SPLinearGradient *lg = SP_LINEARGRADIENT (server); + sp_paint_selector_set_gradient_properties (psel, + SP_GRADIENT_UNITS (lg), + SP_GRADIENT_SPREAD (lg)); + } else if (SP_IS_RADIALGRADIENT (server)) { + SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE); + sp_paint_selector_set_gradient_radial (psel, vector); + + SPRadialGradient *rg = SP_RADIALGRADIENT (server); + sp_paint_selector_set_gradient_properties (psel, + SP_GRADIENT_UNITS (rg), + SP_GRADIENT_SPREAD (rg)); + } else if (SP_IS_PATTERN (server)) { + SPPattern *pat = pattern_getroot (SP_PATTERN (server)); + sp_update_pattern_list (psel, pat); + } + } + break; + } + + case QUERY_STYLE_MULTIPLE_DIFFERENT: + { + sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE); + break; + } + } + + g_free (query); + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE)); +} + +static void +sp_stroke_style_paint_mode_changed( SPPaintSelector *psel, + SPPaintSelectorMode mode, + SPWidget *spw ) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + /* TODO: Does this work? + * Not really, here we have to get old color back from object + * Instead of relying on paint widget having meaningful colors set + */ + sp_stroke_style_paint_changed(psel, spw); +} + +static gchar *undo_label_1 = "stroke:flatcolor:1"; +static gchar *undo_label_2 = "stroke:flatcolor:2"; +static gchar *undo_label = undo_label_1; + +static void +sp_stroke_style_paint_dragged(SPPaintSelector *psel, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + switch (psel->mode) { + case SP_PAINT_SELECTOR_MODE_COLOR_RGB: + case SP_PAINT_SELECTOR_MODE_COLOR_CMYK: + { + sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "stroke", "stroke-opacity"); + sp_document_maybe_done (SP_DT_DOCUMENT(SP_ACTIVE_DESKTOP), undo_label); + break; + } + + default: + g_warning( "file %s: line %d: Paint %d should not emit 'dragged'", + __FILE__, __LINE__, psel->mode); + break; + } +} + +static void +sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE)); + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + SPDocument *document = SP_DT_DOCUMENT (desktop); + Inkscape::Selection *selection = SP_DT_SELECTION (desktop); + + GSList const *items = selection->itemList(); + + switch (psel->mode) { + case SP_PAINT_SELECTOR_MODE_EMPTY: + // This should not happen. + g_warning ( "file %s: line %d: Paint %d should not emit 'changed'", + __FILE__, __LINE__, psel->mode); + break; + case SP_PAINT_SELECTOR_MODE_MULTIPLE: + // This happens when you switch multiple objects with different gradients to flat color; + // nothing to do here. + break; + + case SP_PAINT_SELECTOR_MODE_NONE: + { + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property(css, "stroke", "none"); + + sp_desktop_set_style (desktop, css); + + sp_repr_css_attr_unref(css); + + sp_document_done(document); + break; + } + + case SP_PAINT_SELECTOR_MODE_COLOR_RGB: + case SP_PAINT_SELECTOR_MODE_COLOR_CMYK: + { + sp_paint_selector_set_flat_color (psel, desktop, "stroke", "stroke-opacity"); + sp_document_maybe_done (SP_DT_DOCUMENT(desktop), undo_label); + + // on release, toggle undo_label so that the next drag will not be lumped with this one + if (undo_label == undo_label_1) + undo_label = undo_label_2; + else + undo_label = undo_label_1; + + break; + } + + case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR: + case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL: + if (items) { + SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR + ? SP_GRADIENT_TYPE_LINEAR + : SP_GRADIENT_TYPE_RADIAL ); + SPGradient *vector = sp_paint_selector_get_gradient_vector(psel); + if (!vector) { + /* No vector in paint selector should mean that we just changed mode */ + + SPStyle *query = sp_style_new (); + int result = objects_query_fillstroke ((GSList *) items, query, false); + guint32 common_rgb = 0; + if (result == QUERY_STYLE_MULTIPLE_SAME) { + if (query->fill.type != SP_PAINT_TYPE_COLOR) { + common_rgb = sp_desktop_get_color(desktop, false); + } else { + common_rgb = sp_color_get_rgba32_ualpha(&query->stroke.value.color, 0xff); + } + vector = sp_document_default_gradient_vector(document, common_rgb); + } + g_free (query); + + for (GSList const *i = items; i != NULL; i = i->next) { + if (!vector) { + sp_item_set_gradient(SP_ITEM(i->data), + sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), false), + gradient_type, false); + } else { + sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, false); + } + } + } else { + vector = sp_gradient_ensure_vector_normalized(vector); + for (GSList const *i = items; i != NULL; i = i->next) { + SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, false); + sp_gradient_selector_attrs_to_gradient(gr, psel); + } + } + + sp_document_done(document); + } + break; + + case SP_PAINT_SELECTOR_MODE_PATTERN: + + if (items) { + + SPPattern *pattern = sp_paint_selector_get_pattern (psel); + if (!pattern) { + + /* No Pattern in paint selector should mean that we just + * changed mode - dont do jack. + */ + + } else { + Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern); + SPCSSAttr *css = sp_repr_css_attr_new (); + gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id")); + sp_repr_css_set_property (css, "stroke", urltext); + + for (GSList const *i = items; i != NULL; i = i->next) { + Inkscape::XML::Node *selrepr = SP_OBJECT_REPR (i->data); + SPObject *selobj = SP_OBJECT (i->data); + if (!selrepr) + continue; + + SPStyle *style = SP_OBJECT_STYLE (selobj); + if (style && style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) { + SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (selobj); + if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern) + // only if this object's pattern is not rooted in our selected pattern, apply + continue; + } + + sp_repr_css_change_recursive (selrepr, css, "style"); + } + + sp_repr_css_attr_unref (css); + g_free (urltext); + + } // end if + + sp_document_done (document); + } // end if + + break; + + case SP_PAINT_SELECTOR_MODE_UNSET: + if (items) { + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_unset_property (css, "stroke"); + + sp_desktop_set_style (desktop, css); + sp_repr_css_attr_unref (css); + + sp_document_done (document); + } + break; + + default: + g_warning( "file %s: line %d: Paint selector should not be in " + "mode %d", + __FILE__, __LINE__, + psel->mode ); + break; + } + + g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE)); +} + + + + + +/* Line */ + +static void sp_stroke_style_line_construct(SPWidget *spw, gpointer data); +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(SPWidget *spw, Inkscape::Selection *sel); + +static void sp_stroke_style_set_join_buttons(SPWidget *spw, + GtkWidget *active); + +static void sp_stroke_style_set_cap_buttons(SPWidget *spw, + GtkWidget *active); + +static void sp_stroke_style_width_changed(GtkAdjustment *adj, SPWidget *spw); +static void sp_stroke_style_miterlimit_changed(GtkAdjustment *adj, SPWidget *spw); +static void sp_stroke_style_any_toggled(GtkToggleButton *tb, SPWidget *spw); +static void sp_stroke_style_line_dash_changed(SPDashSelector *dsel, + SPWidget *spw); + +static void sp_stroke_style_update_marker_menus(SPWidget *spw, GSList const *objects); + +static SPObject *ink_extract_marker_name(gchar const *n); + + +/** + * Helper function for creating radio buttons. This should probably be re-thought out + * when reimplementing this with Gtkmm. + */ +static GtkWidget * +sp_stroke_radio_button(GtkWidget *tb, char const *icon, + GtkWidget *hb, GtkWidget *spw, + gchar const *key, gchar const *data) +{ + g_assert(icon != NULL); + g_assert(hb != NULL); + g_assert(spw != NULL); + + if (tb == NULL) { + tb = gtk_radio_button_new(NULL); + } else { + tb = gtk_radio_button_new(gtk_radio_button_group(GTK_RADIO_BUTTON(tb)) ); + } + + gtk_widget_show(tb); + gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(tb), FALSE); + gtk_box_pack_start(GTK_BOX(hb), tb, FALSE, FALSE, 0); + gtk_object_set_data(GTK_OBJECT(spw), icon, tb); + gtk_object_set_data(GTK_OBJECT(tb), key, (gpointer*)data); + gtk_signal_connect(GTK_OBJECT(tb), "toggled", + GTK_SIGNAL_FUNC(sp_stroke_style_any_toggled), + spw); + GtkWidget *px = sp_icon_new(GTK_ICON_SIZE_LARGE_TOOLBAR, icon); + g_assert(px != NULL); + gtk_widget_show(px); + gtk_container_add(GTK_CONTAINER(tb), px); + + return tb; + +} + +static GtkWidget * +sp_marker_prev_new(unsigned size, gchar const *mname, + SPDocument *source, SPDocument *sandbox, + gchar *menu_id, NRArena const *arena, unsigned visionkey, NRArenaItem *root) +{ + // the object of the marker + SPObject const *marker = source->getObjectById(mname); + if (marker == NULL) + return NULL; + + // the repr of the marker; make a copy with id="sample" + Inkscape::XML::Node *mrepr = SP_OBJECT_REPR (marker)->duplicate(); + mrepr->setAttribute("id", "sample"); + + // replace the old sample in the sandbox by the new one + Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (sandbox->getObjectById("defs")); + SPObject *oldmarker = sandbox->getObjectById("sample"); + if (oldmarker) + oldmarker->deleteObject(false); + defsrepr->appendChild(mrepr); + Inkscape::GC::release(mrepr); + +// Uncomment this to get the sandbox documents saved (useful for debugging) + //FILE *fp = fopen (g_strconcat(mname, ".svg", NULL), "w"); + //sp_repr_save_stream (sp_document_repr_doc (sandbox), fp); + //fclose (fp); + + // object to render; note that the id is the same as that of the menu we're building + SPObject *object = sandbox->getObjectById(menu_id); + sp_document_root (sandbox)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + sp_document_ensure_up_to_date(sandbox); + + if (object == NULL || !SP_IS_ITEM(object)) + return NULL; // sandbox broken? + + // Find object's bbox in document + NR::Matrix const i2doc(sp_item_i2doc_affine(SP_ITEM(object))); + + NR::Rect const dbox = SP_ITEM(object)->invokeBbox(i2doc); + + if (dbox.isEmpty()) { + return NULL; + } + + /* Update to renderable state */ + NRMatrix t; + double sf = 0.8; + nr_matrix_set_scale(&t, sf, sf); + nr_arena_item_set_transform(root, &t); + NRGC gc(NULL); + nr_matrix_set_identity(&gc.transform); + nr_arena_item_invoke_update( root, NULL, &gc, + NR_ARENA_ITEM_STATE_ALL, + NR_ARENA_ITEM_STATE_NONE ); + + /* Item integer bbox in points */ + NRRectL ibox; + ibox.x0 = (int) floor(sf * dbox.min()[NR::X] + 0.5); + ibox.y0 = (int) floor(sf * dbox.min()[NR::Y] + 0.5); + ibox.x1 = (int) floor(sf * dbox.max()[NR::X] + 0.5); + ibox.y1 = (int) floor(sf * dbox.max()[NR::Y] + 0.5); + + /* Find visible area */ + int width = ibox.x1 - ibox.x0; + int height = ibox.y1 - ibox.y0; + int dx = size; + int dy = size; + dx=(dx - width)/2; // watch out for size, since 'unsigned'-'signed' can cause problems if the result is negative + dy=(dy - height)/2; + + NRRectL area; + area.x0 = ibox.x0 - dx; + area.y0 = ibox.y0 - dy; + area.x1 = area.x0 + size; + area.y1 = area.y0 + size; + + /* Actual renderable area */ + NRRectL ua; + ua.x0 = MAX(ibox.x0, area.x0); + ua.y0 = MAX(ibox.y0, area.y0); + ua.x1 = MIN(ibox.x1, area.x1); + ua.y1 = MIN(ibox.y1, area.y1); + + /* Set up pixblock */ + guchar *px = nr_new(guchar, 4 * size * size); + memset(px, 0x00, 4 * size * size); + + /* Render */ + NRPixBlock B; + nr_pixblock_setup_extern( &B, NR_PIXBLOCK_MODE_R8G8B8A8N, + ua.x0, ua.y0, ua.x1, ua.y1, + px + 4 * size * (ua.y0 - area.y0) + + 4 * (ua.x0 - area.x0), + 4 * size, FALSE, FALSE ); + nr_arena_item_invoke_render( root, &ua, &B, + NR_ARENA_ITEM_RENDER_NO_CACHE ); + nr_pixblock_release(&B); + + // Create widget + GtkWidget *pb = gtk_image_new_from_pixbuf(gdk_pixbuf_new_from_data(px, + GDK_COLORSPACE_RGB, + TRUE, + 8, size, size, size * 4, + (GdkPixbufDestroyNotify)nr_free, + NULL)); + return pb; +} + + +#define MARKER_ITEM_MARGIN 0 + + +/** + * sp_marker_list_from_doc() + * + * \brief Pick up all markers from source, except those that are in + * current_doc (if non-NULL), and add items to the m menu + * + */ +static void +sp_marker_list_from_doc (GtkWidget *m, SPDocument *current_doc, SPDocument *source, SPDocument *markers_doc, SPDocument *sandbox, gchar *menu_id) +{ + + // search through defs + GSList *ml = NULL; + SPDefs *defs= (SPDefs *) SP_DOCUMENT_DEFS (source); + for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(defs)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT (ochild) ) { + if (SP_IS_MARKER(ochild)) { + ml = g_slist_prepend (ml, ochild); + } + } + + // Do this here, outside of loop, to speed up preview generation: + /* Create new arena */ + NRArena const *arena = NRArena::create(); + /* Create ArenaItem and set transform */ + unsigned const visionkey = sp_item_display_key_new(1); + NRArenaItem *root = sp_item_invoke_show( SP_ITEM(SP_DOCUMENT_ROOT (sandbox)), (NRArena *) arena, visionkey, SP_ITEM_SHOW_DISPLAY ); + + for (; ml != NULL; ml = ml->next) { + + if (!SP_IS_MARKER(ml->data)) + continue; + + Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) ml->data); + + bool stock_dupe = false; + + if (markers_doc && repr->attribute("inkscape:stockid")) { + // find out if markers_doc has a marker with the same stockid, and if so, skip this + for (SPObject *child = sp_object_first_child(SP_OBJECT(SP_DOCUMENT_DEFS(markers_doc))) ; + child != NULL; + child = SP_OBJECT_NEXT(child) ) + { + if (SP_IS_MARKER(child) && + SP_OBJECT_REPR(child)->attribute("inkscape:stockid") && + !strcmp(repr->attribute("inkscape:stockid"), SP_OBJECT_REPR(child)->attribute("inkscape:stockid"))) { + stock_dupe = true; + } + } + } + + if (stock_dupe) // stock item, dont add to list from current doc + continue; + + GtkWidget *i = gtk_menu_item_new(); + gtk_widget_show(i); + + if (repr->attribute("inkscape:stockid")) + g_object_set_data (G_OBJECT(i), "stockid", (void *) "true"); + else + g_object_set_data (G_OBJECT(i), "stockid", (void *) "false"); + + gchar const *markid = repr->attribute("id"); + g_object_set_data (G_OBJECT(i), "marker", (void *) markid); + + GtkWidget *hb = gtk_hbox_new(FALSE, MARKER_ITEM_MARGIN); + gtk_widget_show(hb); + + // generate preview + GtkWidget *prv = sp_marker_prev_new (22, markid, source, sandbox, menu_id, arena, visionkey, root); + gtk_widget_show(prv); + gtk_box_pack_start(GTK_BOX(hb), prv, FALSE, FALSE, 6); + + // create label + GtkWidget *l = gtk_label_new(repr->attribute("id")); + gtk_widget_show(l); + gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.5); + + gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0); + + gtk_widget_show(hb); + gtk_container_add(GTK_CONTAINER(i), hb); + + gtk_menu_append(GTK_MENU(m), i); + } + + g_slist_free (ml); +} + + +SPDocument * +ink_markers_preview_doc () +{ +gchar const *buffer = "<svg xmlns=\"http://www.w3.org/2000/svg\" 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\" />" + +" <g id=\"marker-start\">" +" <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:url(#sample);marker-mid:none;marker-end:none\"" +" d=\"M 12.5,13 L 25,13\" id=\"path1\" />" +" <rect style=\"fill:none;stroke:none\" id=\"rect2\"" +" width=\"25\" height=\"25\" x=\"0\" y=\"0\" />" +" </g>" + +" <g id=\"marker-mid\">" +" <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:none;marker-mid:url(#sample);marker-end:none\"" +" d=\"M 0,113 L 12.5,113 L 25,113\" id=\"path11\" />" +" <rect style=\"fill:none;stroke:none\" id=\"rect22\"" +" width=\"25\" height=\"25\" x=\"0\" y=\"100\" />" +" </g>" + +" <g id=\"marker-end\">" +" <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:none;marker-mid:none;marker-end:url(#sample)\"" +" d=\"M 0,213 L 12.5,213\" id=\"path111\" />" +" <rect style=\"fill:none;stroke:none\" id=\"rect222\"" +" width=\"25\" height=\"25\" x=\"0\" y=\"200\" />" +" </g>" + +"</svg>"; + + return sp_document_new_from_mem (buffer, strlen(buffer), FALSE); +} + + + +static GtkWidget * +ink_marker_menu( GtkWidget *tbl, gchar *menu_id, SPDocument *sandbox) +{ + SPDesktop *desktop = inkscape_active_desktop(); + SPDocument *doc = SP_DT_DOCUMENT(desktop); + GtkWidget *mnu = gtk_option_menu_new(); + + /* Create new menu widget */ + GtkWidget *m = gtk_menu_new(); + gtk_widget_show(m); + + g_object_set_data(G_OBJECT(mnu), "updating", (gpointer) FALSE); + + if (!doc) { + GtkWidget *i = gtk_menu_item_new_with_label(_("No document selected")); + gtk_widget_show(i); + gtk_menu_append(GTK_MENU(m), i); + gtk_widget_set_sensitive(mnu, FALSE); + + } else { + + // add "None" + { + GtkWidget *i = gtk_menu_item_new(); + gtk_widget_show(i); + + g_object_set_data(G_OBJECT(i), "marker", (void *) "none"); + + GtkWidget *hb = gtk_hbox_new(FALSE, MARKER_ITEM_MARGIN); + gtk_widget_show(hb); + + GtkWidget *l = gtk_label_new( _("None") ); + gtk_widget_show(l); + gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.5); + + gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0); + + gtk_widget_show(hb); + gtk_container_add(GTK_CONTAINER(i), hb); + gtk_menu_append(GTK_MENU(m), i); + } + + // find and load markers.svg + static SPDocument *markers_doc = NULL; + char *markers_source = g_build_filename(INKSCAPE_MARKERSDIR, "markers.svg", NULL); + if (Inkscape::IO::file_test(markers_source, G_FILE_TEST_IS_REGULAR)) { + markers_doc = sp_document_new(markers_source, FALSE); + } + g_free(markers_source); + + // suck in from current doc + sp_marker_list_from_doc ( m, NULL, doc, markers_doc, sandbox, menu_id ); + + // add separator + { + GtkWidget *i = gtk_separator_menu_item_new(); + gtk_widget_show(i); + gtk_menu_append(GTK_MENU(m), i); + } + + // suck in from markers.svg + if (markers_doc) { + sp_document_ensure_up_to_date(doc); + sp_marker_list_from_doc ( m, doc, markers_doc, NULL, sandbox, menu_id ); + } + + gtk_widget_set_sensitive(mnu, TRUE); + } + + gtk_object_set_data(GTK_OBJECT(mnu), "menu_id", menu_id); + gtk_option_menu_set_menu(GTK_OPTION_MENU(mnu), m); + + /* Set history */ + gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0); + + return mnu; +} + + +/*user selected existing marker from list*/ +static void +sp_marker_select(GtkOptionMenu *mnu, GtkWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + SPDesktop *desktop = inkscape_active_desktop(); + SPDocument *document = SP_DT_DOCUMENT(desktop); + if (!document) { + return; + } + + /* Get Marker */ + if (!g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), + "marker")) + { + return; + } + gchar *markid = (gchar *) g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), + "marker"); + gchar *marker = ""; + if (strcmp(markid, "none")){ + gchar *stockid = (gchar *) g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), + "stockid"); + + gchar *markurn = markid; + if (!strcmp(stockid,"true")) markurn = g_strconcat("urn:inkscape:marker:",markid,NULL); + SPObject *mark = get_stock_item(markurn); + if (mark) { + Inkscape::XML::Node *repr = SP_OBJECT_REPR(mark); + marker = g_strconcat("url(#", repr->attribute("id"), ")", NULL); + } + } else { + marker = markid; + } + + SPCSSAttr *css = sp_repr_css_attr_new(); + gchar *menu_id = (gchar *) g_object_get_data(G_OBJECT(mnu), "menu_id"); + sp_repr_css_set_property(css, menu_id, marker); + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + GSList const *items = selection->itemList(); + for (; items != NULL; items = items->next) { + SPItem *item = (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 = SP_OBJECT_REPR((SPItem *) items->data); + if (selrepr) { + sp_repr_css_change_recursive(selrepr, css, "style"); + } + SP_OBJECT(items->data)->requestModified(SP_OBJECT_MODIFIED_FLAG); + SP_OBJECT(items->data)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } + + sp_repr_css_attr_unref(css); + + sp_document_done(document); +} + +static gboolean stroke_width_set_unit(SPUnitSelector *, + SPUnit const *old, + SPUnit const *new_units, + GObject *spw) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + if (!desktop) { + return FALSE; + } + + Inkscape::Selection *selection = SP_DT_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 */ + g_object_set_data (spw, "update", GUINT_TO_POINTER (TRUE)); + + GtkAdjustment *a = GTK_ADJUSTMENT(g_object_get_data (spw, "width")); + float w = sp_units_get_pixels (a->value, *old); + + gdouble average = stroke_average_width (objects); + + if (average == NR_HUGE || average == 0) + return FALSE; + + gtk_adjustment_set_value (a, 100.0 * w / average); + + g_object_set_data (spw, "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 */ + g_object_set_data (spw, "update", GUINT_TO_POINTER (TRUE)); + + GtkAdjustment *a = GTK_ADJUSTMENT(g_object_get_data (spw, "width")); + + gdouble average = stroke_average_width (objects); + + gtk_adjustment_set_value (a, sp_pixels_get_units (0.01 * a->value * average, *new_units)); + + g_object_set_data (spw, "update", GUINT_TO_POINTER (FALSE)); + return TRUE; + } + + return FALSE; +} + + +/** + * \brief Creates a new widget for the line stroke style. + * + */ +GtkWidget * +sp_stroke_style_line_widget_new(void) +{ + GtkWidget *spw, *f, *t, *hb, *sb, *us, *tb, *ds; + GtkObject *a; + + GtkTooltips *tt = gtk_tooltips_new(); + + spw = sp_widget_new_global(INKSCAPE); + + f = gtk_hbox_new (FALSE, 0); + gtk_widget_show(f); + gtk_container_add(GTK_CONTAINER(spw), f); + + t = gtk_table_new(3, 6, FALSE); + gtk_widget_show(t); + gtk_container_set_border_width(GTK_CONTAINER(t), 4); + gtk_table_set_row_spacings(GTK_TABLE(t), 4); + gtk_container_add(GTK_CONTAINER(f), t); + gtk_object_set_data(GTK_OBJECT(spw), "stroke", t); + + gint i = 0; + + /* Stroke width */ + spw_label(t, _("Width:"), 0, i); + + hb = spw_hbox(t, 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 +// setHundredPercent to remember the aeraged width corresponding to 100%. Then the +// stroke_width_set_unit will be removed (because ScalarUnit takes care of conversions itself), and +// with it, the two remaining calls of stroke_average_width, allowing us to get rid of that +// function in desktop-style. + + a = gtk_adjustment_new(1.0, 0.0, 1000.0, 0.1, 10.0, 10.0); + gtk_object_set_data(GTK_OBJECT(spw), "width", a); + sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), 0.1, 3); + gtk_tooltips_set_tip(tt, sb, _("Stroke width"), NULL); + gtk_widget_show(sb); + + sp_dialog_defocus_on_enter(sb); + + gtk_box_pack_start(GTK_BOX(hb), sb, FALSE, FALSE, 0); + us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE); + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop) + sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), SP_DT_NAMEDVIEW(desktop)->doc_units); + sp_unit_selector_add_unit(SP_UNIT_SELECTOR(us), &sp_unit_get_by_id(SP_UNIT_PERCENT), 0); + g_signal_connect ( G_OBJECT (us), "set_unit", G_CALLBACK (stroke_width_set_unit), spw ); + gtk_widget_show(us); + sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a) ); + gtk_box_pack_start(GTK_BOX(hb), us, FALSE, FALSE, 0); + gtk_object_set_data(GTK_OBJECT(spw), "units", us); + + gtk_signal_connect( GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(sp_stroke_style_width_changed), spw ); + 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); + + hb = spw_hbox(t, 3, 1, i); + + tb = NULL; + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_MITER, + hb, spw, "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). + gtk_tooltips_set_tip(tt, tb, _("Miter join"), NULL); + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_ROUND, + hb, spw, "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). + gtk_tooltips_set_tip(tt, tb, _("Round join"), NULL); + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_BEVEL, + hb, spw, "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). + gtk_tooltips_set_tip(tt, tb, _("Bevel join"), NULL); + + i++; + + /* Miterlimit */ + // TRANSLATORS: Miter limit: only for "miter join", this limits the length + // of the sharp "spike" when the lines connect at too sharp an angle. + // When two line segments meet at a sharp angle, a miter join results in a + // spike that extends well beyond the connection point. The purpose of the + // miter limit is to cut off such spikes (i.e. convert them into bevels) + // when they become too long. + spw_label(t, _("Miter limit:"), 0, i); + + hb = spw_hbox(t, 3, 1, i); + + a = gtk_adjustment_new(4.0, 0.0, 100.0, 0.1, 10.0, 10.0); + gtk_object_set_data(GTK_OBJECT(spw), "miterlimit", a); + + sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), 0.1, 2); + gtk_tooltips_set_tip(tt, sb, _("Maximum length of the miter (in units of stroke width)"), NULL); + gtk_widget_show(sb); + gtk_object_set_data(GTK_OBJECT(spw), "miterlimit_sb", sb); + sp_dialog_defocus_on_enter(sb); + + gtk_box_pack_start(GTK_BOX(hb), sb, FALSE, FALSE, 0); + + gtk_signal_connect( GTK_OBJECT(a), "value_changed", + GTK_SIGNAL_FUNC(sp_stroke_style_miterlimit_changed), spw ); + i++; + + /* Cap type */ + // TRANSLATORS: cap type specifies the shape for the ends of lines + spw_label(t, _("Cap:"), 0, i); + + hb = spw_hbox(t, 3, 1, i); + + tb = NULL; + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_BUTT, + hb, spw, "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 + gtk_tooltips_set_tip(tt, tb, _("Butt cap"), NULL); + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_ROUND, + hb, spw, "cap", "round"); + + // TRANSLATORS: Round cap: the line shape extends beyond the end point of the + // line; the ends of the line are rounded + gtk_tooltips_set_tip(tt, tb, _("Round cap"), NULL); + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_SQUARE, + hb, spw, "cap", "square"); + + // TRANSLATORS: Square cap: the line shape extends beyond the end point of the + // line; the ends of the line are square + gtk_tooltips_set_tip(tt, tb, _("Square cap"), NULL); + + i++; + + + /* Dash */ + spw_label(t, _("Dashes:"), 0, i); + ds = sp_dash_selector_new( inkscape_get_repr( INKSCAPE, + "palette.dashes") ); + + gtk_widget_show(ds); + gtk_table_attach( GTK_TABLE(t), ds, 1, 4, i, i+1, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data(GTK_OBJECT(spw), "dash", ds); + gtk_signal_connect( GTK_OBJECT(ds), "changed", + GTK_SIGNAL_FUNC(sp_stroke_style_line_dash_changed), + spw ); + i++; + + /* Drop down marker selectors*/ + + // doing this here once, instead of for each preview, to speed things up + SPDocument *sandbox = ink_markers_preview_doc (); + + // 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. + spw_label(t, _("Start Markers:"), 0, i); + GtkWidget *mnu = ink_marker_menu( spw ,"marker-start", sandbox); + gtk_signal_connect( GTK_OBJECT(mnu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw ); + gtk_widget_show(mnu); + gtk_table_attach( GTK_TABLE(t), mnu, 1, 4, i, i+1, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data(GTK_OBJECT(spw), "start_mark_menu", mnu); + + i++; + spw_label(t, _("Mid Markers:"), 0, i); + mnu = NULL; + mnu = ink_marker_menu( spw ,"marker-mid", sandbox); + gtk_signal_connect( GTK_OBJECT(mnu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw ); + gtk_widget_show(mnu); + gtk_table_attach( GTK_TABLE(t), mnu, 1, 4, i, i+1, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data(GTK_OBJECT(spw), "mid_mark_menu", mnu); + + i++; + spw_label(t, _("End Markers:"), 0, i); + mnu = NULL; + mnu = ink_marker_menu( spw ,"marker-end", sandbox); + gtk_signal_connect( GTK_OBJECT(mnu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw ); + gtk_widget_show(mnu); + gtk_table_attach( GTK_TABLE(t), mnu, 1, 4, i, i+1, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data(GTK_OBJECT(spw), "end_mark_menu", mnu); + + i++; + + gtk_signal_connect( GTK_OBJECT(spw), "construct", + GTK_SIGNAL_FUNC(sp_stroke_style_line_construct), + NULL ); + gtk_signal_connect( GTK_OBJECT(spw), "modify_selection", + GTK_SIGNAL_FUNC(sp_stroke_style_line_selection_modified), + NULL ); + gtk_signal_connect( GTK_OBJECT(spw), "change_selection", + GTK_SIGNAL_FUNC(sp_stroke_style_line_selection_changed), + NULL ); + + sp_stroke_style_line_update( SP_WIDGET(spw), desktop ? SP_DT_SELECTION(desktop) : NULL); + + return spw; +} + + + +static void +sp_stroke_style_line_construct(SPWidget *spw, gpointer data) +{ + +#ifdef SP_SS_VERBOSE + g_print( "Stroke style widget constructed: inkscape %p repr %p\n", + spw->inkscape, spw->repr ); +#endif + if (spw->inkscape) { + sp_stroke_style_line_update(spw, + ( SP_ACTIVE_DESKTOP + ? SP_DT_SELECTION(SP_ACTIVE_DESKTOP) + : NULL )); + } +} + +static void +sp_stroke_style_line_selection_modified ( SPWidget *spw, + Inkscape::Selection *selection, + guint flags, + gpointer data ) +{ + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) { + sp_stroke_style_line_update (spw, selection); + } + +} + +static void +sp_stroke_style_line_selection_changed ( SPWidget *spw, + Inkscape::Selection *selection, + gpointer data ) +{ + sp_stroke_style_line_update (spw, selection); +} + + +static void +sp_dash_selector_set_from_style (GtkWidget *dsel, SPStyle *style) +{ + if (style->stroke_dash.n_dash > 0) { + double d[64]; + int len = MIN(style->stroke_dash.n_dash, 64); + for (int i = 0; i < len; i++) { + if (style->stroke_width.computed != 0) + d[i] = style->stroke_dash.dash[i] / style->stroke_width.computed; + else + d[i] = style->stroke_dash.dash[i]; // is there a better thing to do for stroke_width==0? + } + sp_dash_selector_set_dash(SP_DASH_SELECTOR(dsel), len, d, + style->stroke_width.computed != 0? + style->stroke_dash.offset / style->stroke_width.computed : + style->stroke_dash.offset); + } else { + sp_dash_selector_set_dash(SP_DASH_SELECTOR(dsel), 0, NULL, 0.0); + } +} + +static void +sp_jointype_set (SPWidget *spw, unsigned const jointype) +{ + GtkWidget *tb = NULL; + switch (jointype) { + case SP_STROKE_LINEJOIN_MITER: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_MITER)); + break; + case SP_STROKE_LINEJOIN_ROUND: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_ROUND)); + break; + case SP_STROKE_LINEJOIN_BEVEL: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_BEVEL)); + break; + default: + break; + } + sp_stroke_style_set_join_buttons (spw, tb); +} + +static void +sp_captype_set (SPWidget *spw, unsigned const captype) +{ + GtkWidget *tb = NULL; + switch (captype) { + case SP_STROKE_LINECAP_BUTT: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_BUTT)); + break; + case SP_STROKE_LINECAP_ROUND: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_ROUND)); + break; + case SP_STROKE_LINECAP_SQUARE: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_SQUARE)); + break; + default: + break; + } + sp_stroke_style_set_cap_buttons (spw, tb); +} + +static void +sp_stroke_style_line_update(SPWidget *spw, Inkscape::Selection *sel) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE)); + + GtkWidget *sset = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "stroke")); + GtkObject *width = GTK_OBJECT(gtk_object_get_data(GTK_OBJECT(spw), "width")); + GtkObject *ml = GTK_OBJECT(gtk_object_get_data(GTK_OBJECT(spw), "miterlimit")); + GtkWidget *us = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "units")); + GtkWidget *dsel = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "dash")); + + // create temporary style + SPStyle *query = sp_style_new (); + // query into it + int result_sw = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEWIDTH); + int result_ml = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEMITERLIMIT); + int result_cap = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKECAP); + int result_join = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEJOIN); + + if (result_sw == QUERY_STYLE_NOTHING) { + /* No objects stroked, set insensitive */ + gtk_widget_set_sensitive(sset, FALSE); + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE)); + return; + } else { + gtk_widget_set_sensitive(sset, TRUE); + + SPUnit const *unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us)); + + if (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED) { + sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), &sp_unit_get_by_id(SP_UNIT_PERCENT)); + } 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(SP_UNIT_SELECTOR(us), SP_DT_NAMEDVIEW(SP_ACTIVE_DESKTOP)->doc_units); + } + } + + unit = sp_unit_selector_get_unit (SP_UNIT_SELECTOR (us)); + + if (unit->base == SP_UNIT_ABSOLUTE || unit->base == SP_UNIT_DEVICE) { + double avgwidth = sp_pixels_get_units (query->stroke_width.computed, *unit); + gtk_adjustment_set_value(GTK_ADJUSTMENT(width), avgwidth); + } else { + gtk_adjustment_set_value(GTK_ADJUSTMENT(width), 100); + } + } + + if (result_ml != QUERY_STYLE_NOTHING) + gtk_adjustment_set_value(GTK_ADJUSTMENT(ml), query->stroke_miterlimit.value); // TODO: reflect averagedness? + + if (result_join != QUERY_STYLE_MULTIPLE_DIFFERENT) { + sp_jointype_set (spw, query->stroke_linejoin.value); + } else { + sp_stroke_style_set_join_buttons(spw, NULL); + } + + if (result_cap != QUERY_STYLE_MULTIPLE_DIFFERENT) { + sp_captype_set (spw, query->stroke_linecap.value); + } else { + sp_stroke_style_set_cap_buttons(spw, NULL); + } + + g_free (query); + + GSList const *objects = sel->itemList(); + SPObject * const object = SP_OBJECT(objects->data); + SPStyle * const style = SP_OBJECT_STYLE(object); + + /* Markers */ + sp_stroke_style_update_marker_menus(spw, objects); // FIXME: make this desktop query too + + /* Dash */ + sp_dash_selector_set_from_style (dsel, style); // FIXME: make this desktop query too + + gtk_widget_set_sensitive(sset, TRUE); + + gtk_object_set_data(GTK_OBJECT(spw), "update", + GINT_TO_POINTER(FALSE)); +} + +static void +sp_stroke_style_set_scaled_dash(SPCSSAttr *css, + int ndash, double *dash, double offset, + double scale) +{ + if (ndash > 0) { + Inkscape::CSSOStringStream osarray; + for (int i = 0; i < ndash; i++) { + osarray << dash[i] * scale; + if (i < (ndash - 1)) { + osarray << ","; + } + } + sp_repr_css_set_property(css, "stroke-dasharray", osarray.str().c_str()); + + Inkscape::CSSOStringStream osoffset; + osoffset << offset * scale; + sp_repr_css_set_property(css, "stroke-dashoffset", osoffset.str().c_str()); + } else { + sp_repr_css_set_property(css, "stroke-dasharray", "none"); + sp_repr_css_set_property(css, "stroke-dashoffset", NULL); + } +} + +static void +sp_stroke_style_scale_line(SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE)); + + GtkAdjustment *wadj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(spw), "width")); + SPUnitSelector *us = SP_UNIT_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "units")); + SPDashSelector *dsel = SP_DASH_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "dash")); + GtkAdjustment *ml = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(spw), "miterlimit")); + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + SPDocument *document = SP_DT_DOCUMENT (desktop); + Inkscape::Selection *selection = SP_DT_SELECTION (desktop); + + GSList const *items = selection->itemList(); + + /* TODO: Create some standardized method */ + SPCSSAttr *css = sp_repr_css_attr_new(); + + if (items) { + + double width_typed = wadj->value; + double const miterlimit = ml->value; + + SPUnit const *const unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us)); + + double *dash, offset; + int ndash; + sp_dash_selector_get_dash(dsel, &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); + } else { // percentage + gdouble old_w = SP_OBJECT_STYLE (i->data)->stroke_width.computed; + width = old_w * width_typed / 100; + } + + { + Inkscape::CSSOStringStream os_width; + os_width << width; + sp_repr_css_set_property(css, "stroke-width", os_width.str().c_str()); + } + + { + Inkscape::CSSOStringStream os_ml; + os_ml << miterlimit; + sp_repr_css_set_property(css, "stroke-miterlimit", os_ml.str().c_str()); + } + + /* Set dash */ + sp_stroke_style_set_scaled_dash(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) { + // reset to 100 percent + gtk_adjustment_set_value (wadj, 100.0); + } + + } + + // we have already changed the items, so set style without changing selection + // FIXME: move the above stroke-setting stuff, including percentages, to desktop-style + sp_desktop_set_style (desktop, css, false); + + sp_repr_css_attr_unref(css); + + sp_document_done(document); + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE)); +} + + + +static void +sp_stroke_style_width_changed(GtkAdjustment *adj, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + sp_stroke_style_scale_line(spw); +} + +static void +sp_stroke_style_miterlimit_changed(GtkAdjustment *adj, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + sp_stroke_style_scale_line(spw); +} + +static void +sp_stroke_style_line_dash_changed(SPDashSelector *dsel, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + sp_stroke_style_scale_line(spw); +} + + + +/** + * \brief This routine handles toggle events for buttons in the stroke style + * dialog. + * When activated, this routine gets the data for the various widgets, and then + * calls the respective routines to update css properties, etc. + * + */ +static void +sp_stroke_style_any_toggled(GtkToggleButton *tb, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + if (gtk_toggle_button_get_active(tb)) { + + gchar const *join + = static_cast<gchar const *>(gtk_object_get_data(GTK_OBJECT(tb), "join")); + gchar const *cap + = static_cast<gchar const *>(gtk_object_get_data(GTK_OBJECT(tb), "cap")); + + if (join) { + GtkWidget *ml = GTK_WIDGET(g_object_get_data(G_OBJECT(spw), "miterlimit_sb")); + gtk_widget_set_sensitive (ml, !strcmp(join, "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, GTK_WIDGET(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, GTK_WIDGET(tb)); + } + + sp_repr_css_attr_unref(css); + + sp_document_done(SP_DT_DOCUMENT(desktop)); + } +} + + +static void +sp_stroke_style_set_join_buttons(SPWidget *spw, GtkWidget *active) +{ + GtkWidget *tb; + + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_JOIN_MITER) ); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); + + GtkWidget *ml = GTK_WIDGET(g_object_get_data(G_OBJECT(spw), "miterlimit_sb")); + gtk_widget_set_sensitive(ml, (active == tb)); + + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_JOIN_ROUND) ); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_JOIN_BEVEL) ); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); +} + + + +static void +sp_stroke_style_set_cap_buttons(SPWidget *spw, GtkWidget *active) +{ + GtkWidget *tb; + + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_CAP_BUTT)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_CAP_ROUND) ); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_CAP_SQUARE) ); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); +} + +static void +ink_marker_menu_set_current(SPObject *marker, GtkOptionMenu *mnu) +{ + gtk_object_set_data(GTK_OBJECT(mnu), "update", GINT_TO_POINTER(TRUE)); + + GtkMenu *m = GTK_MENU(gtk_option_menu_get_menu(mnu)); + if (marker != NULL) { + bool mark_is_stock = false; + if (SP_OBJECT_REPR(marker)->attribute("inkscape:stockid")) + mark_is_stock = true; + + gchar *markname; + if (mark_is_stock) + markname = g_strdup(SP_OBJECT_REPR(marker)->attribute("inkscape:stockid")); + else + markname = g_strdup(SP_OBJECT_REPR(marker)->attribute("id")); + + int markpos = 0; + GList *kids = GTK_MENU_SHELL(m)->children; + int i = 0; + for (; kids != NULL; kids = kids->next) { + gchar *mark = (gchar *) g_object_get_data(G_OBJECT(kids->data), "marker"); + if ( mark && strcmp(mark, markname) == 0 ) { + if ( mark_is_stock && !strcmp((gchar *) g_object_get_data(G_OBJECT(kids->data), "stockid"), "true")) + markpos = i; + if ( !mark_is_stock && !strcmp((gchar *) g_object_get_data(G_OBJECT(kids->data), "stockid"), "false")) + markpos = i; + } + i++; + } + gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), markpos); + + g_free (markname); + } + else { + gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0); + } + gtk_object_set_data(GTK_OBJECT(mnu), "update", GINT_TO_POINTER(FALSE)); +} + +static void +sp_stroke_style_update_marker_menus( SPWidget *spw, + GSList const *objects) +{ + struct { char const *key; int loc; } const keyloc[] = { + { "start_mark_menu", SP_MARKER_LOC_START }, + { "mid_mark_menu", SP_MARKER_LOC_MID }, + { "end_mark_menu", SP_MARKER_LOC_END } + }; + + bool all_texts = true; + for (GSList *i = (GSList *) objects; i != NULL; i = i->next) { + if (!SP_IS_TEXT (i->data)) { + all_texts = false; + } + } + + for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) { + GtkOptionMenu *mnu = (GtkOptionMenu *) g_object_get_data(G_OBJECT(spw), keyloc[i].key); + if (all_texts) { + // Per SVG spec, text objects cannot have markers; disable menus if only texts are selected + gtk_widget_set_sensitive (GTK_WIDGET(mnu), FALSE); + } else { + gtk_widget_set_sensitive (GTK_WIDGET(mnu), TRUE); + } + } + + // We show markers of the first object in the list only + // FIXME: use the first in the list that has the marker of each type, if any + SPObject *object = SP_OBJECT(objects->data); + + for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) { + // For all three marker types, + + // find the corresponding menu + GtkOptionMenu *mnu = (GtkOptionMenu *) g_object_get_data(G_OBJECT(spw), keyloc[i].key); + + // Quit if we're in update state + if (gtk_object_get_data(GTK_OBJECT(mnu), "update")) { + return; + } + + 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); + // Scroll the menu to that marker + ink_marker_menu_set_current (marker, mnu); + + } else { + gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0); + } + } +} + + +/** 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) +{ + 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'; + + + SPDesktop *desktop = inkscape_active_desktop(); + SPDocument *doc = SP_DT_DOCUMENT(desktop); + SPObject *marker = doc->getObjectById(b); + return marker; +} + + +/* + 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=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
