Code

Moving cache management code from .h to .cpp.
[inkscape.git] / src / dialogs / stroke-style.cpp
1 #define __SP_STROKE_STYLE_C__
3 /**
4  * \brief  Stroke style dialog
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Bryce Harrington <brycehar@bryceharrington.org>
9  *   bulia byak <buliabyak@users.sf.net>
10  *
11  * Copyright (C) 2001-2005 authors
12  * Copyright (C) 2001 Ximian, Inc.
13  * Copyright (C) 2004 John Cliff
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #define noSP_SS_VERBOSE
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
26 #include <glib/gmem.h>
27 #include <gtk/gtk.h>
29 #include <glibmm/i18n.h>
30 #include "helper/unit-menu.h"
31 #include "helper/units.h"
32 #include "svg/css-ostringstream.h"
33 #include "widgets/sp-widget.h"
34 #include "widgets/spw-utilities.h"
35 #include "sp-linear-gradient.h"
36 #include "sp-radial-gradient.h"
37 #include "marker.h"
38 #include "sp-pattern.h"
39 #include "widgets/paint-selector.h"
40 #include "widgets/dash-selector.h"
41 #include "style.h"
42 #include "gradient-chemistry.h"
43 #include "sp-namedview.h"
44 #include "desktop-handles.h"
45 #include "desktop-style.h"
46 #include "selection.h"
47 #include "inkscape.h"
48 #include "inkscape-stock.h"
49 #include "dialogs/dialog-events.h"
50 #include "sp-text.h"
51 #include "sp-rect.h"
52 #include "document-private.h"
53 #include "display/nr-arena.h"
54 #include "display/nr-arena-item.h"
55 #include "path-prefix.h"
56 #include "widgets/icon.h"
57 #include "helper/stock-items.h"
58 #include "io/sys.h"
59 #include "ui/cache/svg_preview_cache.h"
61 #include "dialogs/stroke-style.h"
64 /* Paint */
66 static void sp_stroke_style_paint_construct(SPWidget *spw, SPPaintSelector *psel);
67 static void sp_stroke_style_paint_selection_modified (SPWidget *spw, Inkscape::Selection *selection, guint flags, SPPaintSelector *psel);
68 static void sp_stroke_style_paint_selection_changed (SPWidget *spw, Inkscape::Selection *selection, SPPaintSelector *psel);
69 static void sp_stroke_style_paint_update(SPWidget *spw);
71 static void sp_stroke_style_paint_mode_changed(SPPaintSelector *psel, SPPaintSelectorMode mode, SPWidget *spw);
72 static void sp_stroke_style_paint_dragged(SPPaintSelector *psel, SPWidget *spw);
73 static void sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw);
75 static void sp_stroke_style_widget_change_subselection ( Inkscape::Application *inkscape, SPDesktop *desktop, SPWidget *spw );
77 /** Marker selection option menus */
78 static GtkWidget * marker_start_menu = NULL;
79 static GtkWidget * marker_mid_menu = NULL;
80 static GtkWidget * marker_end_menu = NULL;
83 /**
84  * Create the stroke style widget, and hook up all the signals.
85  */
86 GtkWidget *
87 sp_stroke_style_paint_widget_new(void)
88 {
89     GtkWidget *spw, *psel;
91     spw = sp_widget_new_global(INKSCAPE);
93     psel = sp_paint_selector_new(false); // without fillrule selector
94     gtk_widget_show(psel);
95     gtk_container_add(GTK_CONTAINER(spw), psel);
96     gtk_object_set_data(GTK_OBJECT(spw), "paint-selector", psel);
98     gtk_signal_connect(GTK_OBJECT(spw), "construct",
99                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_construct),
100                        psel);
101     gtk_signal_connect(GTK_OBJECT(spw), "modify_selection",
102                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_selection_modified),
103                        psel);
104     gtk_signal_connect(GTK_OBJECT(spw), "change_selection",
105                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_selection_changed),
106                        psel);
108     g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_stroke_style_widget_change_subselection), spw);
110     gtk_signal_connect(GTK_OBJECT(psel), "mode_changed",
111                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_mode_changed),
112                        spw);
113     gtk_signal_connect(GTK_OBJECT(psel), "dragged",
114                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_dragged),
115                        spw);
116     gtk_signal_connect(GTK_OBJECT(psel), "changed",
117                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_changed),
118                        spw);
120     sp_stroke_style_paint_update (SP_WIDGET(spw));
121     return spw;
124 /**
125  * On construction, simply does an update of the stroke style paint object.
126  */
127 static void
128 sp_stroke_style_paint_construct(SPWidget *spw, SPPaintSelector *psel)
130 #ifdef SP_SS_VERBOSE
131     g_print( "Stroke style widget constructed: inkscape %p repr %p\n",
132              spw->inkscape, spw->repr );
133 #endif
134     if (spw->inkscape) {
135         sp_stroke_style_paint_update (spw);
136     } 
139 /**
140  * On signal modified, invokes an update of the stroke style paint object.
141  */
142 static void
143 sp_stroke_style_paint_selection_modified ( SPWidget *spw,
144                                         Inkscape::Selection *selection,
145                                         guint flags,
146                                         SPPaintSelector *psel)
148     if (flags & ( SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG |
149                   SP_OBJECT_STYLE_MODIFIED_FLAG) ) {
150         sp_stroke_style_paint_update(spw);
151     }
155 /**
156  * On signal selection changed, invokes an update of the stroke style paint object.
157  */
158 static void
159 sp_stroke_style_paint_selection_changed ( SPWidget *spw,
160                                         Inkscape::Selection *selection,
161                                         SPPaintSelector *psel )
163     sp_stroke_style_paint_update (spw);
167 /**
168  * On signal change subselection, invoke an update of the stroke style widget.
169  */
170 static void
171 sp_stroke_style_widget_change_subselection ( Inkscape::Application *inkscape, 
172                                         SPDesktop *desktop,
173                                         SPWidget *spw )
175     sp_stroke_style_paint_update (spw);
178 /**
179  * Gets the active stroke style property, then sets the appropriate color, alpha, gradient,
180  * pattern, etc. for the paint-selector.
181  */
182 static void
183 sp_stroke_style_paint_update (SPWidget *spw)
185     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
186         return;
187     }
189     gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE));
191     SPPaintSelector *psel = SP_PAINT_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "paint-selector"));
193     // create temporary style
194     SPStyle *query = sp_style_new ();
195     // query into it
196     int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKE); 
198     switch (result) {
199         case QUERY_STYLE_NOTHING:
200         {
201             /* No paint at all */
202             sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY);
203             break;
204         }
206         case QUERY_STYLE_SINGLE:
207         case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector
208         case QUERY_STYLE_MULTIPLE_SAME:
209         {
210             SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, false);
211             sp_paint_selector_set_mode (psel, pselmode);
213             if (query->stroke.set && query->stroke.type == SP_PAINT_TYPE_COLOR) {
214                 gfloat d[3];
215                 sp_color_get_rgb_floatv (&query->stroke.value.color, d);
216                 SPColor color;
217                 sp_color_set_rgb_float (&color, d[0], d[1], d[2]);
218                 sp_paint_selector_set_color_alpha (psel, &color, SP_SCALE24_TO_FLOAT (query->stroke_opacity.value));
220             } else if (query->stroke.set && query->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
222                 SPPaintServer *server = SP_STYLE_STROKE_SERVER (query);
224                 if (SP_IS_LINEARGRADIENT (server)) {
225                     SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
226                     sp_paint_selector_set_gradient_linear (psel, vector);
228                     SPLinearGradient *lg = SP_LINEARGRADIENT (server);
229                     sp_paint_selector_set_gradient_properties (psel,
230                                                        SP_GRADIENT_UNITS (lg),
231                                                        SP_GRADIENT_SPREAD (lg));
232                 } else if (SP_IS_RADIALGRADIENT (server)) {
233                     SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
234                     sp_paint_selector_set_gradient_radial (psel, vector);
236                     SPRadialGradient *rg = SP_RADIALGRADIENT (server);
237                     sp_paint_selector_set_gradient_properties (psel,
238                                                        SP_GRADIENT_UNITS (rg),
239                                                        SP_GRADIENT_SPREAD (rg));
240                 } else if (SP_IS_PATTERN (server)) {
241                     SPPattern *pat = pattern_getroot (SP_PATTERN (server));
242                     sp_update_pattern_list (psel, pat);
243                 }
244             }
245             break;
246         }
248         case QUERY_STYLE_MULTIPLE_DIFFERENT:
249         {
250             sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE);
251             break;
252         }
253     }
255     g_free (query);
257     gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE));
260 /**
261  * When the mode is changed, invoke a regular changed handler.
262  */
263 static void
264 sp_stroke_style_paint_mode_changed( SPPaintSelector *psel,
265                                     SPPaintSelectorMode mode,
266                                     SPWidget *spw )
268     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
269         return;
270     }
272     /* TODO: Does this work?
273      * Not really, here we have to get old color back from object
274      * Instead of relying on paint widget having meaningful colors set
275      */
276     sp_stroke_style_paint_changed(psel, spw);
279 static gchar *undo_label_1 = "stroke:flatcolor:1";
280 static gchar *undo_label_2 = "stroke:flatcolor:2";
281 static gchar *undo_label = undo_label_1;
283 /**
284  * When a drag callback occurs on a paint selector object, if it is a RGB or CMYK 
285  * color mode, then set the stroke opacity to psel's flat color.
286  */
287 static void
288 sp_stroke_style_paint_dragged(SPPaintSelector *psel, SPWidget *spw)
290     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
291         return;
292     }
294     switch (psel->mode) {
295         case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
296         case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
297         {
298             sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "stroke", "stroke-opacity");
299             sp_document_maybe_done (sp_desktop_document(SP_ACTIVE_DESKTOP), undo_label, SP_VERB_DIALOG_FILL_STROKE, 
300                                     _("Set stroke color"));
301             break;
302         }
304         default:
305             g_warning( "file %s: line %d: Paint %d should not emit 'dragged'",
306                        __FILE__, __LINE__, psel->mode);
307             break;
308     }
311 /**
312  * When the stroke style's paint settings change, this handler updates the
313  * repr's stroke css style and applies the style to relevant drawing items.
314  */
315 static void
316 sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw)
318     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
319         return;
320     }
321     g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
323     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
324     SPDocument *document = sp_desktop_document (desktop);
325     Inkscape::Selection *selection = sp_desktop_selection (desktop);
327     GSList const *items = selection->itemList();
329     switch (psel->mode) {
330         case SP_PAINT_SELECTOR_MODE_EMPTY:
331             // This should not happen.
332             g_warning ( "file %s: line %d: Paint %d should not emit 'changed'",
333                         __FILE__, __LINE__, psel->mode);
334             break;
335         case SP_PAINT_SELECTOR_MODE_MULTIPLE:
336             // This happens when you switch multiple objects with different gradients to flat color;
337             // nothing to do here.
338             break;
340         case SP_PAINT_SELECTOR_MODE_NONE:
341         {
342             SPCSSAttr *css = sp_repr_css_attr_new();
343             sp_repr_css_set_property(css, "stroke", "none");
345             sp_desktop_set_style (desktop, css);
347             sp_repr_css_attr_unref(css);
349             sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE, 
350                              _("Remove stroke"));
351             break;
352         }
354         case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
355         case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
356         {
357             sp_paint_selector_set_flat_color (psel, desktop, "stroke", "stroke-opacity");
358             sp_document_maybe_done (sp_desktop_document(desktop), undo_label, SP_VERB_DIALOG_FILL_STROKE, 
359                                     _("Set stroke color"));
361             // on release, toggle undo_label so that the next drag will not be lumped with this one
362             if (undo_label == undo_label_1)
363                 undo_label = undo_label_2;
364             else 
365                 undo_label = undo_label_1;
367             break;
368         }
370         case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR:
371         case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL:
372             if (items) {
373                 SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR
374                                                        ? SP_GRADIENT_TYPE_LINEAR
375                                                        : SP_GRADIENT_TYPE_RADIAL );
376                 SPGradient *vector = sp_paint_selector_get_gradient_vector(psel);
377                 if (!vector) {
378                     /* No vector in paint selector should mean that we just changed mode */
380                     SPStyle *query = sp_style_new ();
381                     int result = objects_query_fillstroke ((GSList *) items, query, false); 
382                     guint32 common_rgb = 0;
383                     if (result == QUERY_STYLE_MULTIPLE_SAME) {
384                         if (query->fill.type != SP_PAINT_TYPE_COLOR) {
385                             common_rgb = sp_desktop_get_color(desktop, false);
386                         } else {
387                             common_rgb = sp_color_get_rgba32_ualpha(&query->stroke.value.color, 0xff);
388                         }
389                         vector = sp_document_default_gradient_vector(document, common_rgb);
390                     }
391                     g_free (query);
393                     for (GSList const *i = items; i != NULL; i = i->next) {
394                         if (!vector) {
395                             sp_item_set_gradient(SP_ITEM(i->data), 
396                                                  sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), false),
397                                                  gradient_type, false);
398                         } else {
399                             sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, false);
400                         }
401                     }
402                 } else {
403                     vector = sp_gradient_ensure_vector_normalized(vector);
404                     for (GSList const *i = items; i != NULL; i = i->next) {
405                         SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, false);
406                         sp_gradient_selector_attrs_to_gradient(gr, psel);
407                     }
408                 }
410                 sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE, 
411                                  _("Set gradient on stroke"));
412             }
413             break;
415         case SP_PAINT_SELECTOR_MODE_PATTERN:
417             if (items) {
419                 SPPattern *pattern = sp_paint_selector_get_pattern (psel);
420                 if (!pattern) {
422                     /* No Pattern in paint selector should mean that we just
423                      * changed mode - dont do jack.
424                      */
426                 } else {
427                     Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern);
428                     SPCSSAttr *css = sp_repr_css_attr_new ();
429                     gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id"));
430                     sp_repr_css_set_property (css, "stroke", urltext);
432                     for (GSList const *i = items; i != NULL; i = i->next) {
433                          Inkscape::XML::Node *selrepr = SP_OBJECT_REPR (i->data);
434                          SPObject *selobj = SP_OBJECT (i->data);
435                          if (!selrepr)
436                              continue;
438                          SPStyle *style = SP_OBJECT_STYLE (selobj);
439                          if (style && style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
440                              SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (selobj);
441                              if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern)
442                                  // only if this object's pattern is not rooted in our selected pattern, apply
443                                  continue;
444                          }
446                          sp_repr_css_change_recursive (selrepr, css, "style");
447                      }
449                     sp_repr_css_attr_unref (css);
450                     g_free (urltext);
452                 } // end if
454                 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, 
455                                   _("Set pattern on stroke"));
456             } // end if
458             break;
460         case SP_PAINT_SELECTOR_MODE_UNSET:
461             if (items) {
462                     SPCSSAttr *css = sp_repr_css_attr_new ();
463                     sp_repr_css_unset_property (css, "stroke");
465                     sp_desktop_set_style (desktop, css);
466                     sp_repr_css_attr_unref (css);
468                     sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, 
469                                       _("Unset stroke"));
470             }
471             break;
473         default:
474             g_warning( "file %s: line %d: Paint selector should not be in "
475                        "mode %d",
476                        __FILE__, __LINE__,
477                        psel->mode );
478             break;
479     }
481     g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
488 /* Line */
490 static void sp_stroke_style_line_construct(SPWidget *spw, gpointer data);
491 static void sp_stroke_style_line_selection_modified (SPWidget *spw,
492                                                   Inkscape::Selection *selection,
493                                                   guint flags,
494                                                   gpointer data);
496 static void sp_stroke_style_line_selection_changed (SPWidget *spw,
497                                                    Inkscape::Selection *selection,
498                                                    gpointer data );
500 static void sp_stroke_style_line_update(SPWidget *spw, Inkscape::Selection *sel);
502 static void sp_stroke_style_set_join_buttons(SPWidget *spw,
503                                              GtkWidget *active);
505 static void sp_stroke_style_set_cap_buttons(SPWidget *spw,
506                                             GtkWidget *active);
508 static void sp_stroke_style_width_changed(GtkAdjustment *adj, SPWidget *spw);
509 static void sp_stroke_style_miterlimit_changed(GtkAdjustment *adj, SPWidget *spw);
510 static void sp_stroke_style_any_toggled(GtkToggleButton *tb, SPWidget *spw);
511 static void sp_stroke_style_line_dash_changed(SPDashSelector *dsel,
512                                               SPWidget *spw);
514 static void sp_stroke_style_update_marker_menus(SPWidget *spw, GSList const *objects);
516 static SPObject *ink_extract_marker_name(gchar const *n);
519 /**
520  * Helper function for creating radio buttons.  This should probably be re-thought out
521  * when reimplementing this with Gtkmm.
522  */
523 static GtkWidget *
524 sp_stroke_radio_button(GtkWidget *tb, char const *icon,
525                        GtkWidget *hb, GtkWidget *spw,
526                        gchar const *key, gchar const *data)
528     g_assert(icon != NULL);
529     g_assert(hb  != NULL);
530     g_assert(spw != NULL);
532     if (tb == NULL) {
533         tb = gtk_radio_button_new(NULL);
534     } else {
535         tb = gtk_radio_button_new(gtk_radio_button_group(GTK_RADIO_BUTTON(tb)) );
536     }
538     gtk_widget_show(tb);
539     gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(tb), FALSE);
540     gtk_box_pack_start(GTK_BOX(hb), tb, FALSE, FALSE, 0);
541     gtk_object_set_data(GTK_OBJECT(spw), icon, tb);
542     gtk_object_set_data(GTK_OBJECT(tb), key, (gpointer*)data);
543     gtk_signal_connect(GTK_OBJECT(tb), "toggled",
544                        GTK_SIGNAL_FUNC(sp_stroke_style_any_toggled),
545                        spw);
546     GtkWidget *px = sp_icon_new(Inkscape::ICON_SIZE_LARGE_TOOLBAR, icon);
547     g_assert(px != NULL);
548     gtk_widget_show(px);
549     gtk_container_add(GTK_CONTAINER(tb), px);
551     return tb;
555 /**
556  * Creates a copy of the marker named mname, determines its visible and renderable
557  * area in menu_id's bounding box, and then renders it.  This allows us to fill in
558  * preview images of each marker in the marker menu.
559  */
560 static GtkWidget *
561 sp_marker_prev_new(unsigned psize, gchar const *mname,
562                    SPDocument *source, SPDocument *sandbox,
563                    gchar *menu_id, NRArena const *arena, unsigned visionkey, NRArenaItem *root)
565     // Retrieve the marker named 'mname' from the source SVG document
566     SPObject const *marker = source->getObjectById(mname);
567     if (marker == NULL)
568         return NULL;
570     // Create a copy repr of the marker with id="sample"
571     Inkscape::XML::Node *mrepr = SP_OBJECT_REPR (marker)->duplicate();
572     mrepr->setAttribute("id", "sample");
574     // Replace the old sample in the sandbox by the new one
575     Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (sandbox->getObjectById("defs"));
576     SPObject *oldmarker = sandbox->getObjectById("sample");
577     if (oldmarker)
578         oldmarker->deleteObject(false);
579     defsrepr->appendChild(mrepr);
580     Inkscape::GC::release(mrepr);
582 // Uncomment this to get the sandbox documents saved (useful for debugging)
583     //FILE *fp = fopen (g_strconcat(mname, ".svg", NULL), "w");
584     //sp_repr_save_stream (sp_document_repr_doc (sandbox), fp);
585     //fclose (fp);
587     // object to render; note that the id is the same as that of the menu we're building
588     SPObject *object = sandbox->getObjectById(menu_id);
589     sp_document_root (sandbox)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
590     sp_document_ensure_up_to_date(sandbox);
592     if (object == NULL || !SP_IS_ITEM(object))
593         return NULL; // sandbox broken?
595     // Find object's bbox in document
596     NR::Matrix const i2doc(sp_item_i2doc_affine(SP_ITEM(object)));
597     NR::Rect const dbox = SP_ITEM(object)->invokeBbox(i2doc);
599     if (dbox.isEmpty()) {
600         return NULL;
601     }
603     /* Update to renderable state */
604     double sf = 0.8;
605     GdkPixbuf* pixbuf = render_pixbuf(root, sf, dbox, psize);
607     // Create widget
608     GtkWidget *pb = gtk_image_new_from_pixbuf(pixbuf);
610     return pb;
614 /**
615  *  Returns a list of markers in the defs of the given source document as a GSList object
616  *  Returns NULL if there are no markers in the document.
617  */
618 GSList *
619 ink_marker_list_get (SPDocument *source)
621     if (source == NULL)
622         return NULL;
624     GSList *ml   = NULL;
625     SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS (source);
626     for ( SPObject *child = sp_object_first_child(SP_OBJECT(defs));
627           child != NULL;
628           child = SP_OBJECT_NEXT (child) )
629     {
630         if (SP_IS_MARKER(child)) {
631             ml = g_slist_prepend (ml, child);
632         }
633     }
634     return ml;
637 #define MARKER_ITEM_MARGIN 0
639 /**
640  * Adds previews of markers in marker_list to the given menu widget
641  */
642 static void
643 sp_marker_menu_build (GtkWidget *m, GSList *marker_list, SPDocument *source, SPDocument *sandbox, gchar *menu_id)
645     // Do this here, outside of loop, to speed up preview generation:
646     NRArena const *arena = NRArena::create();
647     unsigned const visionkey = sp_item_display_key_new(1);
648     NRArenaItem *root =  sp_item_invoke_show( SP_ITEM(SP_DOCUMENT_ROOT (sandbox)), (NRArena *) arena, visionkey, SP_ITEM_SHOW_DISPLAY );
650     for (; marker_list != NULL; marker_list = marker_list->next) {
651         Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) marker_list->data);
652         GtkWidget *i = gtk_menu_item_new();
653         gtk_widget_show(i);
655         if (repr->attribute("inkscape:stockid"))
656             g_object_set_data (G_OBJECT(i), "stockid", (void *) "true");
657         else
658             g_object_set_data (G_OBJECT(i), "stockid", (void *) "false");
660         gchar const *markid = repr->attribute("id");
661         g_object_set_data (G_OBJECT(i), "marker", (void *) markid);
663         GtkWidget *hb = gtk_hbox_new(FALSE, MARKER_ITEM_MARGIN);
664         gtk_widget_show(hb);
666         // generate preview
667         GtkWidget *prv = sp_marker_prev_new (22, markid, source, sandbox, menu_id, arena, visionkey, root);
668         gtk_widget_show(prv);
669         gtk_box_pack_start(GTK_BOX(hb), prv, FALSE, FALSE, 6);
671         // create label
672         GtkWidget *l = gtk_label_new(repr->attribute("id"));
673         gtk_widget_show(l);
674         gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.5);
676         gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0);
678         gtk_widget_show(hb);
679         gtk_container_add(GTK_CONTAINER(i), hb);
681         gtk_menu_append(GTK_MENU(m), i);
682     }
685 /**
686  * sp_marker_list_from_doc()
687  *
688  * \brief Pick up all markers from source, except those that are in
689  * current_doc (if non-NULL), and add items to the m menu
690  *
691  */
692 static void
693 sp_marker_list_from_doc (GtkWidget *m, SPDocument *current_doc, SPDocument *source, SPDocument *markers_doc, SPDocument *sandbox, gchar *menu_id)
695     GSList *ml = ink_marker_list_get(source);
696     GSList *clean_ml = NULL;
698     // Do this here, outside of loop, to speed up preview generation:
699     /* Create new arena */
700     NRArena const *arena = NRArena::create();
701     /* Create ArenaItem and set transform */
702     unsigned const visionkey = sp_item_display_key_new(1);
703     NRArenaItem *root =  sp_item_invoke_show( SP_ITEM(SP_DOCUMENT_ROOT (sandbox)), (NRArena *) arena, visionkey, SP_ITEM_SHOW_DISPLAY );
705     for (; ml != NULL; ml = ml->next) {
706         if (!SP_IS_MARKER(ml->data))
707             continue;
709 /*
710   Bug 980157 wants to have stock markers show up at the top of the dropdown menu
711   Thus we can skip all of this code, which simply looks for duplicate stock markers
713         Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) ml->data);
714         bool stock_dupe = false;
716         if (repr->attribute("inkscape:stockid")) {
717             GSList * markers_doc_ml = ink_marker_list_get(markers_doc);
718             for (; markers_doc_ml != NULL; markers_doc_ml = markers_doc_ml->next) {
719                 const gchar* stockid = SP_OBJECT_REPR(markers_doc_ml->data)->attribute("inkscape:stockid");
720                 if (stockid && !strcmp(repr->attribute("inkscape:stockid"), stockid))
721                     stock_dupe = true;
722             }
723         }
725         if (stock_dupe) // stock item, dont add to list from current doc
726             continue;
727 */
729         // Add to the list of markers we really do wish to show
730         clean_ml = g_slist_prepend (clean_ml, ml->data);
731     }
732     sp_marker_menu_build (m, clean_ml, source, sandbox, menu_id);
734     g_slist_free (ml);
735     g_slist_free (clean_ml);
739 /**
740  * Returns a new document containing default start, mid, and end markers.
741  */
742 SPDocument *
743 ink_markers_preview_doc ()
745 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\">"
746 "  <defs id=\"defs\" />"
748 "  <g id=\"marker-start\">"
749 "    <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:url(#sample);marker-mid:none;marker-end:none\""
750 "       d=\"M 12.5,13 L 25,13\" id=\"path1\" />"
751 "    <rect style=\"fill:none;stroke:none\" id=\"rect2\""
752 "       width=\"25\" height=\"25\" x=\"0\" y=\"0\" />"
753 "  </g>"
755 "  <g id=\"marker-mid\">"
756 "    <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:none;marker-mid:url(#sample);marker-end:none\""
757 "       d=\"M 0,113 L 12.5,113 L 25,113\" id=\"path11\" />"
758 "    <rect style=\"fill:none;stroke:none\" id=\"rect22\""
759 "       width=\"25\" height=\"25\" x=\"0\" y=\"100\" />"
760 "  </g>"
762 "  <g id=\"marker-end\">"
763 "    <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:none;marker-mid:none;marker-end:url(#sample)\""
764 "       d=\"M 0,213 L 12.5,213\" id=\"path111\" />"
765 "    <rect style=\"fill:none;stroke:none\" id=\"rect222\""
766 "       width=\"25\" height=\"25\" x=\"0\" y=\"200\" />"
767 "  </g>"
769 "</svg>";
771     return sp_document_new_from_mem (buffer, strlen(buffer), FALSE);
775 /**
776  * Creates a menu widget to display markers from markers.svg
777  */
778 static GtkWidget *
779 ink_marker_menu( GtkWidget *tbl, gchar *menu_id, SPDocument *sandbox)
781     SPDesktop *desktop = inkscape_active_desktop();
782     SPDocument *doc = sp_desktop_document(desktop);
783     GtkWidget *mnu = gtk_option_menu_new();
785     /* Create new menu widget */
786     GtkWidget *m = gtk_menu_new();
787     gtk_widget_show(m);
789     g_object_set_data(G_OBJECT(mnu), "updating", (gpointer) FALSE);
791     if (!doc) {
792         GtkWidget *i = gtk_menu_item_new_with_label(_("No document selected"));
793         gtk_widget_show(i);
794         gtk_menu_append(GTK_MENU(m), i);
795         gtk_widget_set_sensitive(mnu, FALSE);
797     } else {
799         // add "None"
800         {
801             GtkWidget *i = gtk_menu_item_new();
802             gtk_widget_show(i);
804             g_object_set_data(G_OBJECT(i), "marker", (void *) "none");
806             GtkWidget *hb = gtk_hbox_new(FALSE,  MARKER_ITEM_MARGIN);
807             gtk_widget_show(hb);
809             GtkWidget *l = gtk_label_new( _("None") );
810             gtk_widget_show(l);
811             gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.5);
813             gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0);
815             gtk_widget_show(hb);
816             gtk_container_add(GTK_CONTAINER(i), hb);
817             gtk_menu_append(GTK_MENU(m), i);
818         }
820         // find and load  markers.svg
821         static SPDocument *markers_doc = NULL;
822         char *markers_source = g_build_filename(INKSCAPE_MARKERSDIR, "markers.svg", NULL);
823         if (Inkscape::IO::file_test(markers_source, G_FILE_TEST_IS_REGULAR)) {
824             markers_doc = sp_document_new(markers_source, FALSE);
825         }
826         g_free(markers_source);
828         // suck in from current doc
829         sp_marker_list_from_doc ( m, NULL, doc, markers_doc, sandbox, menu_id );
831         // add separator
832         {
833             GtkWidget *i = gtk_separator_menu_item_new();
834             gtk_widget_show(i);
835             gtk_menu_append(GTK_MENU(m), i);
836         }
838         // suck in from markers.svg
839         if (markers_doc) {
840             sp_document_ensure_up_to_date(doc);
841             sp_marker_list_from_doc ( m, doc, markers_doc, NULL, sandbox, menu_id );
842         }
844         gtk_widget_set_sensitive(mnu, TRUE);
845     }
847     gtk_object_set_data(GTK_OBJECT(mnu), "menu_id", menu_id);
848     gtk_option_menu_set_menu(GTK_OPTION_MENU(mnu), m);
850     /* Set history */
851     gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0);
853     return mnu;
857 /**
858  * Handles when user selects one of the markers from the marker menu.
859  * Defines a uri string to refer to it, then applies it to all selected
860  * items in the current desktop.
861  */
862 static void
863 sp_marker_select(GtkOptionMenu *mnu, GtkWidget *spw)
865     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
866         return;
867     }
869     SPDesktop *desktop = inkscape_active_desktop();
870     SPDocument *document = sp_desktop_document(desktop);
871     if (!document) {
872         return;
873     }
875     /* Get Marker */
876     if (!g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))),
877                            "marker"))
878     {
879         return;
880     }
881     gchar *markid = (gchar *) g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))),
882                                                 "marker");
883     gchar *marker = "";
884     if (strcmp(markid, "none")){
885        gchar *stockid = (gchar *) g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))),
886                                                 "stockid");
888        gchar *markurn = markid;
889        if (!strcmp(stockid,"true")) markurn = g_strconcat("urn:inkscape:marker:",markid,NULL);
890        SPObject *mark = get_stock_item(markurn);
891        if (mark) {
892             Inkscape::XML::Node *repr = SP_OBJECT_REPR(mark);
893             marker = g_strconcat("url(#", repr->attribute("id"), ")", NULL);
894         }
895     } else {
896         marker = markid;
897     }
899     SPCSSAttr *css = sp_repr_css_attr_new();
900     gchar *menu_id = (gchar *) g_object_get_data(G_OBJECT(mnu), "menu_id");
901     sp_repr_css_set_property(css, menu_id, marker);
903      Inkscape::Selection *selection = sp_desktop_selection(desktop);
904      GSList const *items = selection->itemList();
905      for (; items != NULL; items = items->next) {
906          SPItem *item = (SPItem *) items->data;
907          if (!SP_IS_SHAPE(item) || SP_IS_RECT(item)) // can't set marker to rect, until it's converted to using <path>
908              continue;
909          Inkscape::XML::Node *selrepr = SP_OBJECT_REPR((SPItem *) items->data);
910          if (selrepr) {
911              sp_repr_css_change_recursive(selrepr, css, "style");
912          }
913          SP_OBJECT(items->data)->requestModified(SP_OBJECT_MODIFIED_FLAG);
914          SP_OBJECT(items->data)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
915      }
917     sp_repr_css_attr_unref(css);
919     sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE, 
920                      _("Set markers"));
923 /**
924  * Sets the stroke width units for all selected items.
925  * Also handles absolute and dimensionless units.
926  */
927 static gboolean stroke_width_set_unit(SPUnitSelector *,
928                                                  SPUnit const *old,
929                                                  SPUnit const *new_units,
930                                                  GObject *spw)
932     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
934     if (!desktop) {
935         return FALSE;
936     }
938     Inkscape::Selection *selection = sp_desktop_selection (desktop);
940     if (selection->isEmpty())
941         return FALSE;
943     GSList const *objects = selection->itemList();
945     if ((old->base == SP_UNIT_ABSOLUTE || old->base == SP_UNIT_DEVICE) &&
946        (new_units->base == SP_UNIT_DIMENSIONLESS)) {
948         /* Absolute to percentage */
949         g_object_set_data (spw, "update", GUINT_TO_POINTER (TRUE));
951         GtkAdjustment *a = GTK_ADJUSTMENT(g_object_get_data (spw, "width"));
952         float w = sp_units_get_pixels (a->value, *old);
954         gdouble average = stroke_average_width (objects);
956         if (average == NR_HUGE || average == 0)
957             return FALSE;
959         gtk_adjustment_set_value (a, 100.0 * w / average);
961         g_object_set_data (spw, "update", GUINT_TO_POINTER (FALSE));
962         return TRUE;
964     } else if ((old->base == SP_UNIT_DIMENSIONLESS) &&
965               (new_units->base == SP_UNIT_ABSOLUTE || new_units->base == SP_UNIT_DEVICE)) {
967         /* Percentage to absolute */
968         g_object_set_data (spw, "update", GUINT_TO_POINTER (TRUE));
970         GtkAdjustment *a = GTK_ADJUSTMENT(g_object_get_data (spw, "width"));
972         gdouble average = stroke_average_width (objects);
974         gtk_adjustment_set_value (a, sp_pixels_get_units (0.01 * a->value * average, *new_units));
976         g_object_set_data (spw, "update", GUINT_TO_POINTER (FALSE));
977         return TRUE;
978     }
980     return FALSE;
984 /**
985  * \brief  Creates a new widget for the line stroke style.
986  *
987  */
988 GtkWidget *
989 sp_stroke_style_line_widget_new(void)
991     GtkWidget *spw, *f, *t, *hb, *sb, *us, *tb, *ds;
992     GtkObject *a;
994     GtkTooltips *tt = gtk_tooltips_new();
996     spw = sp_widget_new_global(INKSCAPE);
998     f = gtk_hbox_new (FALSE, 0);
999     gtk_widget_show(f);
1000     gtk_container_add(GTK_CONTAINER(spw), f);
1002     t = gtk_table_new(3, 6, FALSE);
1003     gtk_widget_show(t);
1004     gtk_container_set_border_width(GTK_CONTAINER(t), 4);
1005     gtk_table_set_row_spacings(GTK_TABLE(t), 4);
1006     gtk_container_add(GTK_CONTAINER(f), t);
1007     gtk_object_set_data(GTK_OBJECT(spw), "stroke", t);
1009     gint i = 0;
1011     /* Stroke width */
1012     spw_label(t, _("Width:"), 0, i);
1014     hb = spw_hbox(t, 3, 1, i);
1016 // TODO: when this is gtkmmified, use an Inkscape::UI::Widget::ScalarUnit instead of the separate
1017 // spinbutton and unit selector for stroke width. In sp_stroke_style_line_update, use
1018 // setHundredPercent to remember the aeraged width corresponding to 100%. Then the
1019 // stroke_width_set_unit will be removed (because ScalarUnit takes care of conversions itself), and
1020 // with it, the two remaining calls of stroke_average_width, allowing us to get rid of that
1021 // function in desktop-style.
1023     a = gtk_adjustment_new(1.0, 0.0, 1000.0, 0.1, 10.0, 10.0);
1024     gtk_object_set_data(GTK_OBJECT(spw), "width", a);
1025     sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), 0.1, 3);
1026     gtk_tooltips_set_tip(tt, sb, _("Stroke width"), NULL);
1027     gtk_widget_show(sb);
1029     sp_dialog_defocus_on_enter(sb);
1031     gtk_box_pack_start(GTK_BOX(hb), sb, FALSE, FALSE, 0);
1032     us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
1033     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1034     if (desktop)
1035         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), sp_desktop_namedview(desktop)->doc_units);
1036     sp_unit_selector_add_unit(SP_UNIT_SELECTOR(us), &sp_unit_get_by_id(SP_UNIT_PERCENT), 0);
1037     g_signal_connect ( G_OBJECT (us), "set_unit", G_CALLBACK (stroke_width_set_unit), spw );
1038     gtk_widget_show(us);
1039     sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a) );
1040     gtk_box_pack_start(GTK_BOX(hb), us, FALSE, FALSE, 0);
1041     gtk_object_set_data(GTK_OBJECT(spw), "units", us);
1043     gtk_signal_connect( GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(sp_stroke_style_width_changed), spw );
1044     i++;
1046     /* Join type */
1047     // TRANSLATORS: The line join style specifies the shape to be used at the
1048     //  corners of paths. It can be "miter", "round" or "bevel".
1049     spw_label(t, _("Join:"), 0, i);
1051     hb = spw_hbox(t, 3, 1, i);
1053     tb = NULL;
1055     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_MITER,
1056                                 hb, spw, "join", "miter");
1058     // TRANSLATORS: Miter join: joining lines with a sharp (pointed) corner.
1059     //  For an example, draw a triangle with a large stroke width and modify the
1060     //  "Join" option (in the Fill and Stroke dialog).
1061     gtk_tooltips_set_tip(tt, tb, _("Miter join"), NULL);
1063     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_ROUND,
1064                                 hb, spw, "join", "round");
1066     // TRANSLATORS: Round join: joining lines with a rounded corner.
1067     //  For an example, draw a triangle with a large stroke width and modify the
1068     //  "Join" option (in the Fill and Stroke dialog).
1069     gtk_tooltips_set_tip(tt, tb, _("Round join"), NULL);
1071     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_BEVEL,
1072                                 hb, spw, "join", "bevel");
1074     // TRANSLATORS: Bevel join: joining lines with a blunted (flattened) corner.
1075     //  For an example, draw a triangle with a large stroke width and modify the
1076     //  "Join" option (in the Fill and Stroke dialog).
1077     gtk_tooltips_set_tip(tt, tb, _("Bevel join"), NULL);
1079     i++;
1081     /* Miterlimit  */
1082     // TRANSLATORS: Miter limit: only for "miter join", this limits the length
1083     //  of the sharp "spike" when the lines connect at too sharp an angle.
1084     // When two line segments meet at a sharp angle, a miter join results in a
1085     //  spike that extends well beyond the connection point. The purpose of the
1086     //  miter limit is to cut off such spikes (i.e. convert them into bevels)
1087     //  when they become too long.
1088     spw_label(t, _("Miter limit:"), 0, i);
1090     hb = spw_hbox(t, 3, 1, i);
1092     a = gtk_adjustment_new(4.0, 0.0, 100.0, 0.1, 10.0, 10.0);
1093     gtk_object_set_data(GTK_OBJECT(spw), "miterlimit", a);
1095     sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), 0.1, 2);
1096     gtk_tooltips_set_tip(tt, sb, _("Maximum length of the miter (in units of stroke width)"), NULL);
1097     gtk_widget_show(sb);
1098     gtk_object_set_data(GTK_OBJECT(spw), "miterlimit_sb", sb);
1099     sp_dialog_defocus_on_enter(sb);
1101     gtk_box_pack_start(GTK_BOX(hb), sb, FALSE, FALSE, 0);
1103     gtk_signal_connect( GTK_OBJECT(a), "value_changed",
1104                         GTK_SIGNAL_FUNC(sp_stroke_style_miterlimit_changed), spw );
1105     i++;
1107     /* Cap type */
1108     // TRANSLATORS: cap type specifies the shape for the ends of lines
1109     spw_label(t, _("Cap:"), 0, i);
1111     hb = spw_hbox(t, 3, 1, i);
1113     tb = NULL;
1115     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_BUTT,
1116                                 hb, spw, "cap", "butt");
1118     // TRANSLATORS: Butt cap: the line shape does not extend beyond the end point
1119     //  of the line; the ends of the line are square
1120     gtk_tooltips_set_tip(tt, tb, _("Butt cap"), NULL);
1122     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_ROUND,
1123                                 hb, spw, "cap", "round");
1125     // TRANSLATORS: Round cap: the line shape extends beyond the end point of the
1126     //  line; the ends of the line are rounded
1127     gtk_tooltips_set_tip(tt, tb, _("Round cap"), NULL);
1129     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_SQUARE,
1130                                 hb, spw, "cap", "square");
1132     // TRANSLATORS: Square cap: the line shape extends beyond the end point of the
1133     //  line; the ends of the line are square
1134     gtk_tooltips_set_tip(tt, tb, _("Square cap"), NULL);
1136     i++;
1139     /* Dash */
1140     spw_label(t, _("Dashes:"), 0, i);
1141     ds = sp_dash_selector_new( inkscape_get_repr( INKSCAPE,
1142                                                   "palette.dashes") );
1144     gtk_widget_show(ds);
1145     gtk_table_attach( GTK_TABLE(t), ds, 1, 4, i, i+1,
1146                       (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
1147                       (GtkAttachOptions)0, 0, 0 );
1148     gtk_object_set_data(GTK_OBJECT(spw), "dash", ds);
1149     gtk_signal_connect( GTK_OBJECT(ds), "changed",
1150                         GTK_SIGNAL_FUNC(sp_stroke_style_line_dash_changed),
1151                         spw );
1152     i++;
1154     /* Drop down marker selectors*/
1156     // doing this here once, instead of for each preview, to speed things up
1157     SPDocument *sandbox = ink_markers_preview_doc ();
1159     // TRANSLATORS: Path markers are an SVG feature that allows you to attach arbitrary shapes
1160     // (arrowheads, bullets, faces, whatever) to the start, end, or middle nodes of a path.
1161     spw_label(t, _("Start Markers:"), 0, i);
1162     marker_start_menu  = ink_marker_menu( spw ,"marker-start", sandbox);
1163     gtk_signal_connect( GTK_OBJECT(marker_start_menu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw );
1164     gtk_widget_show(marker_start_menu);
1165     gtk_table_attach( GTK_TABLE(t), marker_start_menu, 1, 4, i, i+1,
1166                       (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
1167                       (GtkAttachOptions)0, 0, 0 );
1168     gtk_object_set_data(GTK_OBJECT(spw), "start_mark_menu", marker_start_menu);
1170     i++;
1171     spw_label(t, _("Mid Markers:"), 0, i);
1172     marker_mid_menu = ink_marker_menu( spw ,"marker-mid", sandbox);
1173     gtk_signal_connect( GTK_OBJECT(marker_mid_menu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw );
1174     gtk_widget_show(marker_mid_menu);
1175     gtk_table_attach( GTK_TABLE(t), marker_mid_menu, 1, 4, i, i+1,
1176                       (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
1177                       (GtkAttachOptions)0, 0, 0 );
1178     gtk_object_set_data(GTK_OBJECT(spw), "mid_mark_menu", marker_mid_menu);
1180     i++;
1181     spw_label(t, _("End Markers:"), 0, i);
1182     marker_end_menu = ink_marker_menu( spw ,"marker-end", sandbox);
1183     gtk_signal_connect( GTK_OBJECT(marker_end_menu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw );
1184     gtk_widget_show(marker_end_menu);
1185     gtk_table_attach( GTK_TABLE(t), marker_end_menu, 1, 4, i, i+1,
1186                       (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
1187                       (GtkAttachOptions)0, 0, 0 );
1188     gtk_object_set_data(GTK_OBJECT(spw), "end_mark_menu", marker_end_menu);
1190     i++;
1192     gtk_signal_connect( GTK_OBJECT(spw), "construct",
1193                         GTK_SIGNAL_FUNC(sp_stroke_style_line_construct),
1194                         NULL );
1195     gtk_signal_connect( GTK_OBJECT(spw), "modify_selection",
1196                         GTK_SIGNAL_FUNC(sp_stroke_style_line_selection_modified),
1197                         NULL );
1198     gtk_signal_connect( GTK_OBJECT(spw), "change_selection",
1199                         GTK_SIGNAL_FUNC(sp_stroke_style_line_selection_changed),
1200                         NULL );
1202     sp_stroke_style_line_update( SP_WIDGET(spw), desktop ? sp_desktop_selection(desktop) : NULL);
1204     return spw;
1208 /**
1209  * Callback for when the stroke style widget is called.  It causes
1210  * the stroke line style to be updated.
1211  */
1212 static void
1213 sp_stroke_style_line_construct(SPWidget *spw, gpointer data)
1216 #ifdef SP_SS_VERBOSE
1217     g_print( "Stroke style widget constructed: inkscape %p repr %p\n",
1218              spw->inkscape, spw->repr );
1219 #endif
1220     if (spw->inkscape) {
1221         sp_stroke_style_line_update(spw,
1222                                     ( SP_ACTIVE_DESKTOP
1223                                       ? sp_desktop_selection(SP_ACTIVE_DESKTOP)
1224                                       : NULL ));
1225     } 
1228 /**
1229  * Callback for when stroke style widget is modified.  
1230  * Triggers update action.
1231  */
1232 static void
1233 sp_stroke_style_line_selection_modified ( SPWidget *spw,
1234                                        Inkscape::Selection *selection,
1235                                        guint flags,
1236                                        gpointer data )
1238     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1239         sp_stroke_style_line_update (spw, selection);
1240     }
1244 /**
1245  * Callback for when stroke style widget is changed.
1246  * Triggers update action.
1247  */
1248 static void
1249 sp_stroke_style_line_selection_changed ( SPWidget *spw,
1250                                        Inkscape::Selection *selection,
1251                                        gpointer data )
1253     sp_stroke_style_line_update (spw, selection);
1257 /**
1258  * Sets selector widgets' dash style from an SPStyle object.
1259  */
1260 static void
1261 sp_dash_selector_set_from_style (GtkWidget *dsel, SPStyle *style)
1263     if (style->stroke_dash.n_dash > 0) {
1264         double d[64];
1265         int len = MIN(style->stroke_dash.n_dash, 64);
1266         for (int i = 0; i < len; i++) {
1267             if (style->stroke_width.computed != 0)
1268                 d[i] = style->stroke_dash.dash[i] / style->stroke_width.computed;
1269             else
1270                 d[i] = style->stroke_dash.dash[i]; // is there a better thing to do for stroke_width==0?
1271         }
1272         sp_dash_selector_set_dash(SP_DASH_SELECTOR(dsel), len, d,
1273                style->stroke_width.computed != 0?
1274                     style->stroke_dash.offset / style->stroke_width.computed  :
1275                     style->stroke_dash.offset);
1276     } else {
1277         sp_dash_selector_set_dash(SP_DASH_SELECTOR(dsel), 0, NULL, 0.0);
1278     }
1281 /**
1282  * Sets the join type for a line, and updates the stroke style widget's buttons
1283  */
1284 static void
1285 sp_jointype_set (SPWidget *spw, unsigned const jointype)
1287     GtkWidget *tb = NULL;
1288     switch (jointype) {
1289         case SP_STROKE_LINEJOIN_MITER:
1290             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_MITER));
1291             break;
1292         case SP_STROKE_LINEJOIN_ROUND:
1293             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_ROUND));
1294             break;
1295         case SP_STROKE_LINEJOIN_BEVEL:
1296             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_BEVEL));
1297             break;
1298         default:
1299             break;
1300     }
1301     sp_stroke_style_set_join_buttons (spw, tb);
1304 /**
1305  * Sets the cap type for a line, and updates the stroke style widget's buttons
1306  */
1307 static void
1308 sp_captype_set (SPWidget *spw, unsigned const captype)
1310     GtkWidget *tb = NULL;
1311     switch (captype) {
1312         case SP_STROKE_LINECAP_BUTT:
1313             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_BUTT));
1314             break;
1315         case SP_STROKE_LINECAP_ROUND:
1316             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_ROUND));
1317             break;
1318         case SP_STROKE_LINECAP_SQUARE:
1319             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_SQUARE));
1320             break;
1321         default:
1322             break;
1323     }
1324     sp_stroke_style_set_cap_buttons (spw, tb);
1327 /**
1328  * Callback for when stroke style widget is updated, including markers, cap type,
1329  * join type, etc.
1330  */
1331 static void
1332 sp_stroke_style_line_update(SPWidget *spw, Inkscape::Selection *sel)
1334     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1335         return;
1336     }
1338     gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE));
1340     GtkWidget *sset = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "stroke"));
1341     GtkObject *width = GTK_OBJECT(gtk_object_get_data(GTK_OBJECT(spw), "width"));
1342     GtkObject *ml = GTK_OBJECT(gtk_object_get_data(GTK_OBJECT(spw), "miterlimit"));
1343     GtkWidget *us = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "units"));
1344     GtkWidget *dsel = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "dash"));
1346     // create temporary style
1347     SPStyle *query = sp_style_new ();
1348     // query into it
1349     int result_sw = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEWIDTH); 
1350     int result_ml = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEMITERLIMIT); 
1351     int result_cap = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKECAP); 
1352     int result_join = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEJOIN); 
1354     if (result_sw == QUERY_STYLE_NOTHING) {
1355         /* No objects stroked, set insensitive */
1356         gtk_widget_set_sensitive(sset, FALSE);
1358         gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE));
1359         return;
1360     } else {
1361         gtk_widget_set_sensitive(sset, TRUE);
1363         SPUnit const *unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
1365         if (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED) {
1366             sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), &sp_unit_get_by_id(SP_UNIT_PERCENT));
1367         } else {
1368             // same width, or only one object; no sense to keep percent, switch to absolute
1369             if (unit->base != SP_UNIT_ABSOLUTE && unit->base != SP_UNIT_DEVICE) {
1370                 sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), sp_desktop_namedview(SP_ACTIVE_DESKTOP)->doc_units);
1371             }
1372         }
1374         unit = sp_unit_selector_get_unit (SP_UNIT_SELECTOR (us));
1376         if (unit->base == SP_UNIT_ABSOLUTE || unit->base == SP_UNIT_DEVICE) {
1377             double avgwidth = sp_pixels_get_units (query->stroke_width.computed, *unit);
1378             gtk_adjustment_set_value(GTK_ADJUSTMENT(width), avgwidth);
1379         } else {
1380             gtk_adjustment_set_value(GTK_ADJUSTMENT(width), 100);
1381         }
1382     }
1384     if (result_ml != QUERY_STYLE_NOTHING)
1385         gtk_adjustment_set_value(GTK_ADJUSTMENT(ml), query->stroke_miterlimit.value); // TODO: reflect averagedness?
1387     if (result_join != QUERY_STYLE_MULTIPLE_DIFFERENT) {
1388         sp_jointype_set (spw, query->stroke_linejoin.value);
1389     } else {
1390         sp_stroke_style_set_join_buttons(spw, NULL);
1391     }
1393     if (result_cap != QUERY_STYLE_MULTIPLE_DIFFERENT) {
1394         sp_captype_set (spw, query->stroke_linecap.value);
1395     } else {
1396         sp_stroke_style_set_cap_buttons(spw, NULL);
1397     }
1399     g_free (query);
1401     if (!sel || sel->isEmpty())
1402         return;
1404     GSList const *objects = sel->itemList();
1405     SPObject * const object = SP_OBJECT(objects->data);
1406     SPStyle * const style = SP_OBJECT_STYLE(object);
1408     /* Markers */
1409     sp_stroke_style_update_marker_menus(spw, objects); // FIXME: make this desktop query too
1411     /* Dash */
1412     sp_dash_selector_set_from_style (dsel, style); // FIXME: make this desktop query too
1414     gtk_widget_set_sensitive(sset, TRUE);
1416     gtk_object_set_data(GTK_OBJECT(spw), "update",
1417                         GINT_TO_POINTER(FALSE));
1420 /**
1421  * Sets a line's dash properties in a CSS style object.
1422  */
1423 static void
1424 sp_stroke_style_set_scaled_dash(SPCSSAttr *css,
1425                                 int ndash, double *dash, double offset,
1426                                 double scale)
1428     if (ndash > 0) {
1429         Inkscape::CSSOStringStream osarray;
1430         for (int i = 0; i < ndash; i++) {
1431             osarray << dash[i] * scale;
1432             if (i < (ndash - 1)) {
1433                 osarray << ",";
1434             }
1435         }
1436         sp_repr_css_set_property(css, "stroke-dasharray", osarray.str().c_str());
1438         Inkscape::CSSOStringStream osoffset;
1439         osoffset << offset * scale;
1440         sp_repr_css_set_property(css, "stroke-dashoffset", osoffset.str().c_str());
1441     } else {
1442         sp_repr_css_set_property(css, "stroke-dasharray", "none");
1443         sp_repr_css_set_property(css, "stroke-dashoffset", NULL);
1444     }
1447 /**
1448  * Sets line properties like width, dashes, markers, etc. on all currently selected items.
1449  */
1450 static void
1451 sp_stroke_style_scale_line(SPWidget *spw)
1453     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1454         return;
1455     }
1457     gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE));
1459     GtkAdjustment *wadj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(spw), "width"));
1460     SPUnitSelector *us = SP_UNIT_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "units"));
1461     SPDashSelector *dsel = SP_DASH_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "dash"));
1462     GtkAdjustment *ml = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(spw), "miterlimit"));
1464     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1465     SPDocument *document = sp_desktop_document (desktop);
1466     Inkscape::Selection *selection = sp_desktop_selection (desktop);
1468     GSList const *items = selection->itemList();
1470     /* TODO: Create some standardized method */
1471     SPCSSAttr *css = sp_repr_css_attr_new();
1473     if (items) {
1475         double width_typed = wadj->value;
1476         double const miterlimit = ml->value;
1478         SPUnit const *const unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
1480         double *dash, offset;
1481         int ndash;
1482         sp_dash_selector_get_dash(dsel, &ndash, &dash, &offset);
1484         for (GSList const *i = items; i != NULL; i = i->next) {
1485             /* Set stroke width */
1486             double width;
1487             if (unit->base == SP_UNIT_ABSOLUTE || unit->base == SP_UNIT_DEVICE) {
1488                 width = sp_units_get_pixels (width_typed, *unit);
1489             } else { // percentage
1490                 gdouble old_w = SP_OBJECT_STYLE (i->data)->stroke_width.computed;
1491                 width = old_w * width_typed / 100;
1492             }
1494             {
1495                 Inkscape::CSSOStringStream os_width;
1496                 os_width << width;
1497                 sp_repr_css_set_property(css, "stroke-width", os_width.str().c_str());
1498             }
1500             {
1501                 Inkscape::CSSOStringStream os_ml;
1502                 os_ml << miterlimit;
1503                 sp_repr_css_set_property(css, "stroke-miterlimit", os_ml.str().c_str());
1504             }
1506             /* Set dash */
1507             sp_stroke_style_set_scaled_dash(css, ndash, dash, offset, width);
1509             sp_desktop_apply_css_recursive (SP_OBJECT(i->data), css, true);
1510         }
1512         g_free(dash);
1514         if (unit->base != SP_UNIT_ABSOLUTE && unit->base != SP_UNIT_DEVICE) {
1515             // reset to 100 percent
1516             gtk_adjustment_set_value (wadj, 100.0);
1517         }
1519     }
1521     // we have already changed the items, so set style without changing selection
1522     // FIXME: move the above stroke-setting stuff, including percentages, to desktop-style
1523     sp_desktop_set_style (desktop, css, false);
1525     sp_repr_css_attr_unref(css);
1527     sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE, 
1528                      _("Set stroke style"));
1530     gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE));
1534 /**
1535  * Callback for when the stroke style's width changes.  
1536  * Causes all line styles to be applied to all selected items.
1537  */
1538 static void
1539 sp_stroke_style_width_changed(GtkAdjustment *adj, SPWidget *spw)
1541     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1542         return;
1543     }
1545     sp_stroke_style_scale_line(spw);
1548 /**
1549  * Callback for when the stroke style's miterlimit changes.  
1550  * Causes all line styles to be applied to all selected items.
1551  */
1552 static void
1553 sp_stroke_style_miterlimit_changed(GtkAdjustment *adj, SPWidget *spw)
1555     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1556         return;
1557     }
1559     sp_stroke_style_scale_line(spw);
1562 /**
1563  * Callback for when the stroke style's dash changes.  
1564  * Causes all line styles to be applied to all selected items.
1565  */
1566 static void
1567 sp_stroke_style_line_dash_changed(SPDashSelector *dsel, SPWidget *spw)
1569     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1570         return;
1571     }
1573     sp_stroke_style_scale_line(spw);
1578 /**
1579  * \brief  This routine handles toggle events for buttons in the stroke style
1580  *         dialog.
1581  * When activated, this routine gets the data for the various widgets, and then
1582  * calls the respective routines to update css properties, etc.
1583  *
1584  */
1585 static void
1586 sp_stroke_style_any_toggled(GtkToggleButton *tb, SPWidget *spw)
1588     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1589         return;
1590     }
1592     if (gtk_toggle_button_get_active(tb)) {
1594         gchar const *join
1595             = static_cast<gchar const *>(gtk_object_get_data(GTK_OBJECT(tb), "join"));
1596         gchar const *cap
1597             = static_cast<gchar const *>(gtk_object_get_data(GTK_OBJECT(tb), "cap"));
1599         if (join) {
1600             GtkWidget *ml = GTK_WIDGET(g_object_get_data(G_OBJECT(spw), "miterlimit_sb"));
1601             gtk_widget_set_sensitive (ml, !strcmp(join, "miter"));
1602         }
1604         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1606         /* TODO: Create some standardized method */
1607         SPCSSAttr *css = sp_repr_css_attr_new();
1609         if (join) {
1610             sp_repr_css_set_property(css, "stroke-linejoin", join);
1612             sp_desktop_set_style (desktop, css);
1614             sp_stroke_style_set_join_buttons(spw, GTK_WIDGET(tb));
1615         } else if (cap) {
1616             sp_repr_css_set_property(css, "stroke-linecap", cap);
1618             sp_desktop_set_style (desktop, css);
1620             sp_stroke_style_set_cap_buttons(spw, GTK_WIDGET(tb));
1621         }
1623         sp_repr_css_attr_unref(css);
1625         sp_document_done(sp_desktop_document(desktop), SP_VERB_DIALOG_FILL_STROKE,
1626                          _("Set stroke style"));
1627     }
1631 /**
1632  * Updates the join style toggle buttons
1633  */
1634 static void
1635 sp_stroke_style_set_join_buttons(SPWidget *spw, GtkWidget *active)
1637     GtkWidget *tb;
1639     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1640                                          INKSCAPE_STOCK_JOIN_MITER) );
1641     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1643     GtkWidget *ml = GTK_WIDGET(g_object_get_data(G_OBJECT(spw), "miterlimit_sb"));
1644     gtk_widget_set_sensitive(ml, (active == tb));
1646     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1647                                          INKSCAPE_STOCK_JOIN_ROUND) );
1648     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1649     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1650                                          INKSCAPE_STOCK_JOIN_BEVEL) );
1651     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1656 /**
1657  * Updates the cap style toggle buttons
1658  */
1659 static void
1660 sp_stroke_style_set_cap_buttons(SPWidget *spw, GtkWidget *active)
1662     GtkWidget *tb;
1664     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1665                                          INKSCAPE_STOCK_CAP_BUTT));
1666     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1667     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1668                                          INKSCAPE_STOCK_CAP_ROUND) );
1669     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1670     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1671                                          INKSCAPE_STOCK_CAP_SQUARE) );
1672     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1675 /**
1676  * Sets the current marker in the marker menu.
1677  */
1678 static void
1679 ink_marker_menu_set_current(SPObject *marker, GtkOptionMenu *mnu)
1681     gtk_object_set_data(GTK_OBJECT(mnu), "update", GINT_TO_POINTER(TRUE));
1683     GtkMenu *m = GTK_MENU(gtk_option_menu_get_menu(mnu));
1684     if (marker != NULL) {
1685         bool mark_is_stock = false;
1686         if (SP_OBJECT_REPR(marker)->attribute("inkscape:stockid"))
1687             mark_is_stock = true;
1689         gchar *markname;
1690         if (mark_is_stock)
1691             markname = g_strdup(SP_OBJECT_REPR(marker)->attribute("inkscape:stockid"));
1692         else
1693             markname = g_strdup(SP_OBJECT_REPR(marker)->attribute("id"));
1695         int markpos = 0;
1696         GList *kids = GTK_MENU_SHELL(m)->children;
1697         int i = 0;
1698         for (; kids != NULL; kids = kids->next) {
1699             gchar *mark = (gchar *) g_object_get_data(G_OBJECT(kids->data), "marker");
1700             if ( mark && strcmp(mark, markname) == 0 ) {
1701                 if ( mark_is_stock && !strcmp((gchar *) g_object_get_data(G_OBJECT(kids->data), "stockid"), "true"))
1702                     markpos = i;
1703                 if ( !mark_is_stock && !strcmp((gchar *) g_object_get_data(G_OBJECT(kids->data), "stockid"), "false"))
1704                     markpos = i;
1705             }
1706             i++;
1707         }
1708         gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), markpos);
1710         g_free (markname);
1711     }
1712     else {
1713         gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0);
1714     }
1715     gtk_object_set_data(GTK_OBJECT(mnu), "update", GINT_TO_POINTER(FALSE));
1718 /**
1719  * Updates the marker menus to highlight the appropriate marker and scroll to 
1720  * that marker.
1721  */
1722 static void
1723 sp_stroke_style_update_marker_menus( SPWidget *spw,
1724                                      GSList const *objects)
1726     struct { char const *key; int loc; } const keyloc[] = {
1727         { "start_mark_menu", SP_MARKER_LOC_START },
1728         { "mid_mark_menu", SP_MARKER_LOC_MID },
1729         { "end_mark_menu", SP_MARKER_LOC_END }
1730     };
1732     bool all_texts = true;
1733     for (GSList *i = (GSList *) objects; i != NULL; i = i->next) {
1734         if (!SP_IS_TEXT (i->data)) {
1735             all_texts = false;
1736         }
1737     }
1739     for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) {
1740         GtkOptionMenu *mnu = (GtkOptionMenu *) g_object_get_data(G_OBJECT(spw), keyloc[i].key);
1741         if (all_texts) {
1742             // Per SVG spec, text objects cannot have markers; disable menus if only texts are selected
1743             gtk_widget_set_sensitive (GTK_WIDGET(mnu), FALSE);
1744         } else {
1745             gtk_widget_set_sensitive (GTK_WIDGET(mnu), TRUE);
1746         }
1747     }
1749     // We show markers of the first object in the list only
1750     // FIXME: use the first in the list that has the marker of each type, if any
1751     SPObject *object = SP_OBJECT(objects->data);
1753     for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) {
1754         // For all three marker types,
1756         // find the corresponding menu
1757         GtkOptionMenu *mnu = (GtkOptionMenu *) g_object_get_data(G_OBJECT(spw), keyloc[i].key);
1759         // Quit if we're in update state
1760         if (gtk_object_get_data(GTK_OBJECT(mnu), "update")) {
1761             return;
1762         }
1764         if (object->style->marker[keyloc[i].loc].value != NULL && !all_texts) {
1765             // If the object has this type of markers,
1767             // Extract the name of the marker that the object uses
1768             SPObject *marker = ink_extract_marker_name(object->style->marker[keyloc[i].loc].value);
1769             // Scroll the menu to that marker
1770             ink_marker_menu_set_current (marker, mnu);
1772         } else {
1773             gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0);
1774         }
1775     }
1779 /**
1780  * Extract the actual name of the link
1781  * e.g. get mTriangle from url(#mTriangle).
1782  * \return Buffer containing the actual name, allocated from GLib;
1783  * the caller should free the buffer when they no longer need it.
1784  */
1785 static SPObject*
1786 ink_extract_marker_name(gchar const *n)
1788     gchar const *p = n;
1789     while (*p != '\0' && *p != '#') {
1790         p++;
1791     }
1793     if (*p == '\0' || p[1] == '\0') {
1794         return NULL;
1795     }
1797     p++;
1798     int c = 0;
1799     while (p[c] != '\0' && p[c] != ')') {
1800         c++;
1801     }
1803     if (p[c] == '\0') {
1804         return NULL;
1805     }
1807     gchar* b = g_strdup(p);
1808     b[c] = '\0';
1811     SPDesktop *desktop = inkscape_active_desktop();
1812     SPDocument *doc = sp_desktop_document(desktop);
1813     SPObject *marker = doc->getObjectById(b);
1814     return marker;
1818 /*
1819   Local Variables:
1820   mode:c++
1821   c-file-style:"stroustrup"
1822   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1823   indent-tabs-mode:nil
1824   fill-column:99
1825   End:
1826 */
1827 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :