Code

Completes fix for 980157. Markers in current document's defs is now
[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;
82 static Inkscape::UI::Cache::SvgPreview svg_preview_cache;
84 /**
85  * Create the stroke style widget, and hook up all the signals.
86  */
87 GtkWidget *
88 sp_stroke_style_paint_widget_new(void)
89 {
90     GtkWidget *spw, *psel;
92     spw = sp_widget_new_global(INKSCAPE);
94     psel = sp_paint_selector_new(false); // without fillrule selector
95     gtk_widget_show(psel);
96     gtk_container_add(GTK_CONTAINER(spw), psel);
97     gtk_object_set_data(GTK_OBJECT(spw), "paint-selector", psel);
99     gtk_signal_connect(GTK_OBJECT(spw), "construct",
100                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_construct),
101                        psel);
102     gtk_signal_connect(GTK_OBJECT(spw), "modify_selection",
103                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_selection_modified),
104                        psel);
105     gtk_signal_connect(GTK_OBJECT(spw), "change_selection",
106                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_selection_changed),
107                        psel);
109     g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_stroke_style_widget_change_subselection), spw);
111     gtk_signal_connect(GTK_OBJECT(psel), "mode_changed",
112                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_mode_changed),
113                        spw);
114     gtk_signal_connect(GTK_OBJECT(psel), "dragged",
115                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_dragged),
116                        spw);
117     gtk_signal_connect(GTK_OBJECT(psel), "changed",
118                        GTK_SIGNAL_FUNC(sp_stroke_style_paint_changed),
119                        spw);
121     sp_stroke_style_paint_update (SP_WIDGET(spw));
122     return spw;
125 /**
126  * On construction, simply does an update of the stroke style paint object.
127  */
128 static void
129 sp_stroke_style_paint_construct(SPWidget *spw, SPPaintSelector *psel)
131 #ifdef SP_SS_VERBOSE
132     g_print( "Stroke style widget constructed: inkscape %p repr %p\n",
133              spw->inkscape, spw->repr );
134 #endif
135     if (spw->inkscape) {
136         sp_stroke_style_paint_update (spw);
137     } 
140 /**
141  * On signal modified, invokes an update of the stroke style paint object.
142  */
143 static void
144 sp_stroke_style_paint_selection_modified ( SPWidget *spw,
145                                         Inkscape::Selection *selection,
146                                         guint flags,
147                                         SPPaintSelector *psel)
149     if (flags & ( SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG |
150                   SP_OBJECT_STYLE_MODIFIED_FLAG) ) {
151         sp_stroke_style_paint_update(spw);
152     }
156 /**
157  * On signal selection changed, invokes an update of the stroke style paint object.
158  */
159 static void
160 sp_stroke_style_paint_selection_changed ( SPWidget *spw,
161                                         Inkscape::Selection *selection,
162                                         SPPaintSelector *psel )
164     sp_stroke_style_paint_update (spw);
168 /**
169  * On signal change subselection, invoke an update of the stroke style widget.
170  */
171 static void
172 sp_stroke_style_widget_change_subselection ( Inkscape::Application *inkscape, 
173                                         SPDesktop *desktop,
174                                         SPWidget *spw )
176     sp_stroke_style_paint_update (spw);
179 /**
180  * Gets the active stroke style property, then sets the appropriate color, alpha, gradient,
181  * pattern, etc. for the paint-selector.
182  */
183 static void
184 sp_stroke_style_paint_update (SPWidget *spw)
186     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
187         return;
188     }
190     gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE));
192     SPPaintSelector *psel = SP_PAINT_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "paint-selector"));
194     // create temporary style
195     SPStyle *query = sp_style_new ();
196     // query into it
197     int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKE); 
199     switch (result) {
200         case QUERY_STYLE_NOTHING:
201         {
202             /* No paint at all */
203             sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY);
204             break;
205         }
207         case QUERY_STYLE_SINGLE:
208         case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector
209         case QUERY_STYLE_MULTIPLE_SAME:
210         {
211             SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, false);
212             sp_paint_selector_set_mode (psel, pselmode);
214             if (query->stroke.set && query->stroke.type == SP_PAINT_TYPE_COLOR) {
215                 gfloat d[3];
216                 sp_color_get_rgb_floatv (&query->stroke.value.color, d);
217                 SPColor color;
218                 sp_color_set_rgb_float (&color, d[0], d[1], d[2]);
219                 sp_paint_selector_set_color_alpha (psel, &color, SP_SCALE24_TO_FLOAT (query->stroke_opacity.value));
221             } else if (query->stroke.set && query->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
223                 SPPaintServer *server = SP_STYLE_STROKE_SERVER (query);
225                 if (SP_IS_LINEARGRADIENT (server)) {
226                     SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
227                     sp_paint_selector_set_gradient_linear (psel, vector);
229                     SPLinearGradient *lg = SP_LINEARGRADIENT (server);
230                     sp_paint_selector_set_gradient_properties (psel,
231                                                        SP_GRADIENT_UNITS (lg),
232                                                        SP_GRADIENT_SPREAD (lg));
233                 } else if (SP_IS_RADIALGRADIENT (server)) {
234                     SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
235                     sp_paint_selector_set_gradient_radial (psel, vector);
237                     SPRadialGradient *rg = SP_RADIALGRADIENT (server);
238                     sp_paint_selector_set_gradient_properties (psel,
239                                                        SP_GRADIENT_UNITS (rg),
240                                                        SP_GRADIENT_SPREAD (rg));
241                 } else if (SP_IS_PATTERN (server)) {
242                     SPPattern *pat = pattern_getroot (SP_PATTERN (server));
243                     sp_update_pattern_list (psel, pat);
244                 }
245             }
246             break;
247         }
249         case QUERY_STYLE_MULTIPLE_DIFFERENT:
250         {
251             sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE);
252             break;
253         }
254     }
256     g_free (query);
258     gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE));
261 /**
262  * When the mode is changed, invoke a regular changed handler.
263  */
264 static void
265 sp_stroke_style_paint_mode_changed( SPPaintSelector *psel,
266                                     SPPaintSelectorMode mode,
267                                     SPWidget *spw )
269     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
270         return;
271     }
273     /* TODO: Does this work?
274      * Not really, here we have to get old color back from object
275      * Instead of relying on paint widget having meaningful colors set
276      */
277     sp_stroke_style_paint_changed(psel, spw);
280 static gchar *undo_label_1 = "stroke:flatcolor:1";
281 static gchar *undo_label_2 = "stroke:flatcolor:2";
282 static gchar *undo_label = undo_label_1;
284 /**
285  * When a drag callback occurs on a paint selector object, if it is a RGB or CMYK 
286  * color mode, then set the stroke opacity to psel's flat color.
287  */
288 static void
289 sp_stroke_style_paint_dragged(SPPaintSelector *psel, SPWidget *spw)
291     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
292         return;
293     }
295     switch (psel->mode) {
296         case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
297         case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
298         {
299             sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "stroke", "stroke-opacity");
300             sp_document_maybe_done (sp_desktop_document(SP_ACTIVE_DESKTOP), undo_label, SP_VERB_DIALOG_FILL_STROKE, 
301                                     _("Set stroke color"));
302             break;
303         }
305         default:
306             g_warning( "file %s: line %d: Paint %d should not emit 'dragged'",
307                        __FILE__, __LINE__, psel->mode);
308             break;
309     }
312 /**
313  * When the stroke style's paint settings change, this handler updates the
314  * repr's stroke css style and applies the style to relevant drawing items.
315  */
316 static void
317 sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw)
319     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
320         return;
321     }
322     g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
324     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
325     SPDocument *document = sp_desktop_document (desktop);
326     Inkscape::Selection *selection = sp_desktop_selection (desktop);
328     GSList const *items = selection->itemList();
330     switch (psel->mode) {
331         case SP_PAINT_SELECTOR_MODE_EMPTY:
332             // This should not happen.
333             g_warning ( "file %s: line %d: Paint %d should not emit 'changed'",
334                         __FILE__, __LINE__, psel->mode);
335             break;
336         case SP_PAINT_SELECTOR_MODE_MULTIPLE:
337             // This happens when you switch multiple objects with different gradients to flat color;
338             // nothing to do here.
339             break;
341         case SP_PAINT_SELECTOR_MODE_NONE:
342         {
343             SPCSSAttr *css = sp_repr_css_attr_new();
344             sp_repr_css_set_property(css, "stroke", "none");
346             sp_desktop_set_style (desktop, css);
348             sp_repr_css_attr_unref(css);
350             sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE, 
351                              _("Remove stroke"));
352             break;
353         }
355         case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
356         case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
357         {
358             sp_paint_selector_set_flat_color (psel, desktop, "stroke", "stroke-opacity");
359             sp_document_maybe_done (sp_desktop_document(desktop), undo_label, SP_VERB_DIALOG_FILL_STROKE, 
360                                     _("Set stroke color"));
362             // on release, toggle undo_label so that the next drag will not be lumped with this one
363             if (undo_label == undo_label_1)
364                 undo_label = undo_label_2;
365             else 
366                 undo_label = undo_label_1;
368             break;
369         }
371         case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR:
372         case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL:
373             if (items) {
374                 SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR
375                                                        ? SP_GRADIENT_TYPE_LINEAR
376                                                        : SP_GRADIENT_TYPE_RADIAL );
377                 SPGradient *vector = sp_paint_selector_get_gradient_vector(psel);
378                 if (!vector) {
379                     /* No vector in paint selector should mean that we just changed mode */
381                     SPStyle *query = sp_style_new ();
382                     int result = objects_query_fillstroke ((GSList *) items, query, false); 
383                     guint32 common_rgb = 0;
384                     if (result == QUERY_STYLE_MULTIPLE_SAME) {
385                         if (query->fill.type != SP_PAINT_TYPE_COLOR) {
386                             common_rgb = sp_desktop_get_color(desktop, false);
387                         } else {
388                             common_rgb = sp_color_get_rgba32_ualpha(&query->stroke.value.color, 0xff);
389                         }
390                         vector = sp_document_default_gradient_vector(document, common_rgb);
391                     }
392                     g_free (query);
394                     for (GSList const *i = items; i != NULL; i = i->next) {
395                         if (!vector) {
396                             sp_item_set_gradient(SP_ITEM(i->data), 
397                                                  sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), false),
398                                                  gradient_type, false);
399                         } else {
400                             sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, false);
401                         }
402                     }
403                 } else {
404                     vector = sp_gradient_ensure_vector_normalized(vector);
405                     for (GSList const *i = items; i != NULL; i = i->next) {
406                         SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, false);
407                         sp_gradient_selector_attrs_to_gradient(gr, psel);
408                     }
409                 }
411                 sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE, 
412                                  _("Set gradient on stroke"));
413             }
414             break;
416         case SP_PAINT_SELECTOR_MODE_PATTERN:
418             if (items) {
420                 SPPattern *pattern = sp_paint_selector_get_pattern (psel);
421                 if (!pattern) {
423                     /* No Pattern in paint selector should mean that we just
424                      * changed mode - dont do jack.
425                      */
427                 } else {
428                     Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern);
429                     SPCSSAttr *css = sp_repr_css_attr_new ();
430                     gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id"));
431                     sp_repr_css_set_property (css, "stroke", urltext);
433                     for (GSList const *i = items; i != NULL; i = i->next) {
434                          Inkscape::XML::Node *selrepr = SP_OBJECT_REPR (i->data);
435                          SPObject *selobj = SP_OBJECT (i->data);
436                          if (!selrepr)
437                              continue;
439                          SPStyle *style = SP_OBJECT_STYLE (selobj);
440                          if (style && style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
441                              SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (selobj);
442                              if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern)
443                                  // only if this object's pattern is not rooted in our selected pattern, apply
444                                  continue;
445                          }
447                          sp_repr_css_change_recursive (selrepr, css, "style");
448                      }
450                     sp_repr_css_attr_unref (css);
451                     g_free (urltext);
453                 } // end if
455                 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, 
456                                   _("Set pattern on stroke"));
457             } // end if
459             break;
461         case SP_PAINT_SELECTOR_MODE_UNSET:
462             if (items) {
463                     SPCSSAttr *css = sp_repr_css_attr_new ();
464                     sp_repr_css_unset_property (css, "stroke");
466                     sp_desktop_set_style (desktop, css);
467                     sp_repr_css_attr_unref (css);
469                     sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, 
470                                       _("Unset stroke"));
471             }
472             break;
474         default:
475             g_warning( "file %s: line %d: Paint selector should not be in "
476                        "mode %d",
477                        __FILE__, __LINE__,
478                        psel->mode );
479             break;
480     }
482     g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
489 /* Line */
491 static void sp_stroke_style_line_construct(SPWidget *spw, gpointer data);
492 static void sp_stroke_style_line_selection_modified (SPWidget *spw,
493                                                   Inkscape::Selection *selection,
494                                                   guint flags,
495                                                   gpointer data);
497 static void sp_stroke_style_line_selection_changed (SPWidget *spw,
498                                                    Inkscape::Selection *selection,
499                                                    gpointer data );
501 static void sp_stroke_style_line_update(SPWidget *spw, Inkscape::Selection *sel);
503 static void sp_stroke_style_set_join_buttons(SPWidget *spw,
504                                              GtkWidget *active);
506 static void sp_stroke_style_set_cap_buttons(SPWidget *spw,
507                                             GtkWidget *active);
509 static void sp_stroke_style_width_changed(GtkAdjustment *adj, SPWidget *spw);
510 static void sp_stroke_style_miterlimit_changed(GtkAdjustment *adj, SPWidget *spw);
511 static void sp_stroke_style_any_toggled(GtkToggleButton *tb, SPWidget *spw);
512 static void sp_stroke_style_line_dash_changed(SPDashSelector *dsel,
513                                               SPWidget *spw);
515 static void sp_stroke_style_update_marker_menus(SPWidget *spw, GSList const *objects);
517 static SPObject *ink_extract_marker_name(gchar const *n);
520 /**
521  * Helper function for creating radio buttons.  This should probably be re-thought out
522  * when reimplementing this with Gtkmm.
523  */
524 static GtkWidget *
525 sp_stroke_radio_button(GtkWidget *tb, char const *icon,
526                        GtkWidget *hb, GtkWidget *spw,
527                        gchar const *key, gchar const *data)
529     g_assert(icon != NULL);
530     g_assert(hb  != NULL);
531     g_assert(spw != NULL);
533     if (tb == NULL) {
534         tb = gtk_radio_button_new(NULL);
535     } else {
536         tb = gtk_radio_button_new(gtk_radio_button_group(GTK_RADIO_BUTTON(tb)) );
537     }
539     gtk_widget_show(tb);
540     gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(tb), FALSE);
541     gtk_box_pack_start(GTK_BOX(hb), tb, FALSE, FALSE, 0);
542     gtk_object_set_data(GTK_OBJECT(spw), icon, tb);
543     gtk_object_set_data(GTK_OBJECT(tb), key, (gpointer*)data);
544     gtk_signal_connect(GTK_OBJECT(tb), "toggled",
545                        GTK_SIGNAL_FUNC(sp_stroke_style_any_toggled),
546                        spw);
547     GtkWidget *px = sp_icon_new(Inkscape::ICON_SIZE_LARGE_TOOLBAR, icon);
548     g_assert(px != NULL);
549     gtk_widget_show(px);
550     gtk_container_add(GTK_CONTAINER(tb), px);
552     return tb;
556 /**
557  * Creates a copy of the marker named mname, determines its visible and renderable
558  * area in menu_id's bounding box, and then renders it.  This allows us to fill in
559  * preview images of each marker in the marker menu.
560  */
561 static GtkWidget *
562 sp_marker_prev_new(unsigned psize, gchar const *mname,
563                    SPDocument *source, SPDocument *sandbox,
564                    gchar *menu_id, NRArena const *arena, unsigned visionkey, NRArenaItem *root)
566     // Retrieve the marker named 'mname' from the source SVG document
567     SPObject const *marker = source->getObjectById(mname);
568     if (marker == NULL)
569         return NULL;
571     // Create a copy repr of the marker with id="sample"
572     Inkscape::XML::Node *mrepr = SP_OBJECT_REPR (marker)->duplicate();
573     mrepr->setAttribute("id", "sample");
575     // Replace the old sample in the sandbox by the new one
576     Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (sandbox->getObjectById("defs"));
577     SPObject *oldmarker = sandbox->getObjectById("sample");
578     if (oldmarker)
579         oldmarker->deleteObject(false);
580     defsrepr->appendChild(mrepr);
581     Inkscape::GC::release(mrepr);
583 // Uncomment this to get the sandbox documents saved (useful for debugging)
584     //FILE *fp = fopen (g_strconcat(mname, ".svg", NULL), "w");
585     //sp_repr_save_stream (sp_document_repr_doc (sandbox), fp);
586     //fclose (fp);
588     // object to render; note that the id is the same as that of the menu we're building
589     SPObject *object = sandbox->getObjectById(menu_id);
590     sp_document_root (sandbox)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
591     sp_document_ensure_up_to_date(sandbox);
593     if (object == NULL || !SP_IS_ITEM(object))
594         return NULL; // sandbox broken?
596     // Find object's bbox in document
597     NR::Matrix const i2doc(sp_item_i2doc_affine(SP_ITEM(object)));
598     NR::Rect const dbox = SP_ITEM(object)->invokeBbox(i2doc);
600     if (dbox.isEmpty()) {
601         return NULL;
602     }
604     /* Update to renderable state */
605     double sf = 0.8;
606     GdkPixbuf* pixbuf = NULL;
608     Glib::ustring key = svg_preview_cache.cache_key(mname, psize);
609     pixbuf = svg_preview_cache.get_preview_from_cache(key);
611     if (pixbuf == NULL) {
612         pixbuf = render_pixbuf(root, sf, dbox, psize);
613         svg_preview_cache.set_preview_in_cache(key, pixbuf);
614     }
616     // Create widget
617     GtkWidget *pb = gtk_image_new_from_pixbuf(pixbuf);
619     return pb;
623 /**
624  *  Returns a list of markers in the defs of the given source document as a GSList object
625  *  Returns NULL if there are no markers in the document.
626  */
627 GSList *
628 ink_marker_list_get (SPDocument *source)
630     if (source == NULL)
631         return NULL;
633     GSList *ml   = NULL;
634     SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS (source);
635     for ( SPObject *child = sp_object_first_child(SP_OBJECT(defs));
636           child != NULL;
637           child = SP_OBJECT_NEXT (child) )
638     {
639         if (SP_IS_MARKER(child)) {
640             ml = g_slist_prepend (ml, child);
641         }
642     }
643     return ml;
646 #define MARKER_ITEM_MARGIN 0
648 /**
649  * Adds previews of markers in marker_list to the given menu widget
650  */
651 static void
652 sp_marker_menu_build (GtkWidget *m, GSList *marker_list, SPDocument *source, SPDocument *sandbox, gchar *menu_id)
654     // Do this here, outside of loop, to speed up preview generation:
655     NRArena const *arena = NRArena::create();
656     unsigned const visionkey = sp_item_display_key_new(1);
657     NRArenaItem *root =  sp_item_invoke_show( SP_ITEM(SP_DOCUMENT_ROOT (sandbox)), (NRArena *) arena, visionkey, SP_ITEM_SHOW_DISPLAY );
659     for (; marker_list != NULL; marker_list = marker_list->next) {
660         Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) marker_list->data);
661         GtkWidget *i = gtk_menu_item_new();
662         gtk_widget_show(i);
664         if (repr->attribute("inkscape:stockid"))
665             g_object_set_data (G_OBJECT(i), "stockid", (void *) "true");
666         else
667             g_object_set_data (G_OBJECT(i), "stockid", (void *) "false");
669         gchar const *markid = repr->attribute("id");
670         g_object_set_data (G_OBJECT(i), "marker", (void *) markid);
672         GtkWidget *hb = gtk_hbox_new(FALSE, MARKER_ITEM_MARGIN);
673         gtk_widget_show(hb);
675         // generate preview
676         GtkWidget *prv = sp_marker_prev_new (22, markid, source, sandbox, menu_id, arena, visionkey, root);
677         gtk_widget_show(prv);
678         gtk_box_pack_start(GTK_BOX(hb), prv, FALSE, FALSE, 6);
680         // create label
681         GtkWidget *l = gtk_label_new(repr->attribute("id"));
682         gtk_widget_show(l);
683         gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.5);
685         gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0);
687         gtk_widget_show(hb);
688         gtk_container_add(GTK_CONTAINER(i), hb);
690         gtk_menu_append(GTK_MENU(m), i);
691     }
694 /**
695  * sp_marker_list_from_doc()
696  *
697  * \brief Pick up all markers from source, except those that are in
698  * current_doc (if non-NULL), and add items to the m menu
699  *
700  */
701 static void
702 sp_marker_list_from_doc (GtkWidget *m, SPDocument *current_doc, SPDocument *source, SPDocument *markers_doc, SPDocument *sandbox, gchar *menu_id)
704     GSList *ml = ink_marker_list_get(source);
705     GSList *clean_ml = NULL;
707     // Do this here, outside of loop, to speed up preview generation:
708     /* Create new arena */
709     NRArena const *arena = NRArena::create();
710     /* Create ArenaItem and set transform */
711     unsigned const visionkey = sp_item_display_key_new(1);
712 /*
713     NRArenaItem *root =  sp_item_invoke_show( SP_ITEM(SP_DOCUMENT_ROOT (sandbox)), (NRArena *) arena, visionkey, SP_ITEM_SHOW_DISPLAY );
714 */
716     for (; ml != NULL; ml = ml->next) {
717         if (!SP_IS_MARKER(ml->data))
718             continue;
720 /*
721   Bug 980157 wants to have stock markers show up at the top of the dropdown menu
722   Thus we can skip all of this code, which simply looks for duplicate stock markers
724         Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) ml->data);
725         bool stock_dupe = false;
727         if (repr->attribute("inkscape:stockid")) {
728             GSList * markers_doc_ml = ink_marker_list_get(markers_doc);
729             for (; markers_doc_ml != NULL; markers_doc_ml = markers_doc_ml->next) {
730                 const gchar* stockid = SP_OBJECT_REPR(markers_doc_ml->data)->attribute("inkscape:stockid");
731                 if (stockid && !strcmp(repr->attribute("inkscape:stockid"), stockid))
732                     stock_dupe = true;
733             }
734         }
736         if (stock_dupe) // stock item, dont add to list from current doc
737             continue;
738 */
740         // Add to the list of markers we really do wish to show
741         clean_ml = g_slist_prepend (clean_ml, ml->data);
742     }
743     sp_marker_menu_build (m, clean_ml, source, sandbox, menu_id);
745     g_slist_free (ml);
746     g_slist_free (clean_ml);
750 /**
751  * Returns a new document containing default start, mid, and end markers.
752  */
753 SPDocument *
754 ink_markers_preview_doc ()
756 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\">"
757 "  <defs id=\"defs\" />"
759 "  <g id=\"marker-start\">"
760 "    <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:url(#sample);marker-mid:none;marker-end:none\""
761 "       d=\"M 12.5,13 L 25,13\" id=\"path1\" />"
762 "    <rect style=\"fill:none;stroke:none\" id=\"rect2\""
763 "       width=\"25\" height=\"25\" x=\"0\" y=\"0\" />"
764 "  </g>"
766 "  <g id=\"marker-mid\">"
767 "    <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:none;marker-mid:url(#sample);marker-end:none\""
768 "       d=\"M 0,113 L 12.5,113 L 25,113\" id=\"path11\" />"
769 "    <rect style=\"fill:none;stroke:none\" id=\"rect22\""
770 "       width=\"25\" height=\"25\" x=\"0\" y=\"100\" />"
771 "  </g>"
773 "  <g id=\"marker-end\">"
774 "    <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:none;marker-mid:none;marker-end:url(#sample)\""
775 "       d=\"M 0,213 L 12.5,213\" id=\"path111\" />"
776 "    <rect style=\"fill:none;stroke:none\" id=\"rect222\""
777 "       width=\"25\" height=\"25\" x=\"0\" y=\"200\" />"
778 "  </g>"
780 "</svg>";
782     return sp_document_new_from_mem (buffer, strlen(buffer), FALSE);
785 static void
786 ink_marker_menu_create_menu(GtkWidget *m, gchar *menu_id, SPDocument *doc, SPDocument *sandbox)
788     static SPDocument *markers_doc = NULL;
790     // add "None"
791     GtkWidget *i = gtk_menu_item_new();
792     gtk_widget_show(i);
794 //    g_object_set_data(G_OBJECT(i), "marker", (void *) "none");
796     GtkWidget *hb = gtk_hbox_new(FALSE,  MARKER_ITEM_MARGIN);
797     gtk_widget_show(hb);
799     GtkWidget *l = gtk_label_new( _("None") );
800     gtk_widget_show(l);
801     gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.5);
803     gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0);
805     gtk_widget_show(hb);
806     gtk_container_add(GTK_CONTAINER(i), hb);
807     gtk_menu_append(GTK_MENU(m), i);
809     // find and load  markers.svg
810     if (markers_doc == NULL) {
811         g_warning("Reloading markers_doc");
812         char *markers_source = g_build_filename(INKSCAPE_MARKERSDIR, "markers.svg", NULL);
813         if (Inkscape::IO::file_test(markers_source, G_FILE_TEST_IS_REGULAR)) {
814             markers_doc = sp_document_new(markers_source, FALSE);
815         }
816         g_free(markers_source);
817     }
819     // suck in from current doc
820     sp_marker_list_from_doc ( m, NULL, doc, markers_doc, sandbox, menu_id );
821     
822     // add separator
823     {
824         GtkWidget *i = gtk_separator_menu_item_new();
825         gtk_widget_show(i);
826         gtk_menu_append(GTK_MENU(m), i);
827     }
829     // suck in from markers.svg
830     if (markers_doc) {
831         sp_document_ensure_up_to_date(doc);
832         sp_marker_list_from_doc ( m, doc, markers_doc, NULL, sandbox, menu_id );
833     }
837 /**
838  * Creates a menu widget to display markers from markers.svg
839  */
840 static GtkWidget *
841 ink_marker_menu( GtkWidget *tbl, gchar *menu_id, SPDocument *sandbox)
843     SPDesktop *desktop = inkscape_active_desktop();
844     SPDocument *doc = sp_desktop_document(desktop);
845     GtkWidget *mnu = gtk_option_menu_new();
847     /* Create new menu widget */
848     GtkWidget *m = gtk_menu_new();
849     gtk_widget_show(m);
851     g_object_set_data(G_OBJECT(mnu), "updating", (gpointer) FALSE);
853     if (!doc) {
854         GtkWidget *i = gtk_menu_item_new_with_label(_("No document selected"));
855         gtk_widget_show(i);
856         gtk_menu_append(GTK_MENU(m), i);
857         gtk_widget_set_sensitive(mnu, FALSE);
859     } else {
860         ink_marker_menu_create_menu(m, menu_id, doc, sandbox);
862         gtk_widget_set_sensitive(mnu, TRUE);
863     }
865     gtk_object_set_data(GTK_OBJECT(mnu), "menu_id", menu_id);
866     gtk_option_menu_set_menu(GTK_OPTION_MENU(mnu), m);
868     /* Set history */
869     gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0);
871     return mnu;
875 /**
876  * Handles when user selects one of the markers from the marker menu.
877  * Defines a uri string to refer to it, then applies it to all selected
878  * items in the current desktop.
879  */
880 static void
881 sp_marker_select(GtkOptionMenu *mnu, GtkWidget *spw)
883     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
884         return;
885     }
887     SPDesktop *desktop = inkscape_active_desktop();
888     SPDocument *document = sp_desktop_document(desktop);
889     if (!document) {
890         return;
891     }
893     /* Get Marker */
894     if (!g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))),
895                            "marker"))
896     {
897         return;
898     }
899     gchar *markid = (gchar *) g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))),
900                                                 "marker");
901     gchar *marker = "";
902     if (strcmp(markid, "none")){
903        gchar *stockid = (gchar *) g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))),
904                                                 "stockid");
906        gchar *markurn = markid;
907        if (!strcmp(stockid,"true")) markurn = g_strconcat("urn:inkscape:marker:",markid,NULL);
908        SPObject *mark = get_stock_item(markurn);
909        if (mark) {
910             Inkscape::XML::Node *repr = SP_OBJECT_REPR(mark);
911             marker = g_strconcat("url(#", repr->attribute("id"), ")", NULL);
912         }
913     } else {
914         marker = markid;
915     }
917     SPCSSAttr *css = sp_repr_css_attr_new();
918     gchar *menu_id = (gchar *) g_object_get_data(G_OBJECT(mnu), "menu_id");
919     sp_repr_css_set_property(css, menu_id, marker);
921      Inkscape::Selection *selection = sp_desktop_selection(desktop);
922      GSList const *items = selection->itemList();
923      for (; items != NULL; items = items->next) {
924          SPItem *item = (SPItem *) items->data;
925          if (!SP_IS_SHAPE(item) || SP_IS_RECT(item)) // can't set marker to rect, until it's converted to using <path>
926              continue;
927          Inkscape::XML::Node *selrepr = SP_OBJECT_REPR((SPItem *) items->data);
928          if (selrepr) {
929              sp_repr_css_change_recursive(selrepr, css, "style");
930          }
931          SP_OBJECT(items->data)->requestModified(SP_OBJECT_MODIFIED_FLAG);
932          SP_OBJECT(items->data)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
933      }
935     sp_repr_css_attr_unref(css);
937     sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE, 
938                      _("Set markers"));
940     // Lastly, also update the marker dropdown menus, so the document's markers
941     // show up at the top of the menu
942     SPDocument *sandbox = ink_markers_preview_doc ();
943     GtkWidget *m;
945     m = gtk_menu_new();
946     gtk_widget_show(m);
947     ink_marker_menu_create_menu(m, "marker-start", document, sandbox);
948     gtk_option_menu_remove_menu(GTK_OPTION_MENU(marker_start_menu));
949     gtk_option_menu_set_menu(GTK_OPTION_MENU(marker_start_menu), m);
951     m = gtk_menu_new();
952     gtk_widget_show(m);
953     ink_marker_menu_create_menu(m, "marker-mid", document, sandbox);
954     gtk_option_menu_remove_menu(GTK_OPTION_MENU(marker_mid_menu));
955     gtk_option_menu_set_menu(GTK_OPTION_MENU(marker_mid_menu), m);
957     m = gtk_menu_new();
958     gtk_widget_show(m);
959     ink_marker_menu_create_menu(m, "marker-end", document, sandbox);
960     gtk_option_menu_remove_menu(GTK_OPTION_MENU(marker_end_menu));
961     gtk_option_menu_set_menu(GTK_OPTION_MENU(marker_end_menu), m);
964 /**
965  * Sets the stroke width units for all selected items.
966  * Also handles absolute and dimensionless units.
967  */
968 static gboolean stroke_width_set_unit(SPUnitSelector *,
969                                                  SPUnit const *old,
970                                                  SPUnit const *new_units,
971                                                  GObject *spw)
973     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
975     if (!desktop) {
976         return FALSE;
977     }
979     Inkscape::Selection *selection = sp_desktop_selection (desktop);
981     if (selection->isEmpty())
982         return FALSE;
984     GSList const *objects = selection->itemList();
986     if ((old->base == SP_UNIT_ABSOLUTE || old->base == SP_UNIT_DEVICE) &&
987        (new_units->base == SP_UNIT_DIMENSIONLESS)) {
989         /* Absolute to percentage */
990         g_object_set_data (spw, "update", GUINT_TO_POINTER (TRUE));
992         GtkAdjustment *a = GTK_ADJUSTMENT(g_object_get_data (spw, "width"));
993         float w = sp_units_get_pixels (a->value, *old);
995         gdouble average = stroke_average_width (objects);
997         if (average == NR_HUGE || average == 0)
998             return FALSE;
1000         gtk_adjustment_set_value (a, 100.0 * w / average);
1002         g_object_set_data (spw, "update", GUINT_TO_POINTER (FALSE));
1003         return TRUE;
1005     } else if ((old->base == SP_UNIT_DIMENSIONLESS) &&
1006               (new_units->base == SP_UNIT_ABSOLUTE || new_units->base == SP_UNIT_DEVICE)) {
1008         /* Percentage to absolute */
1009         g_object_set_data (spw, "update", GUINT_TO_POINTER (TRUE));
1011         GtkAdjustment *a = GTK_ADJUSTMENT(g_object_get_data (spw, "width"));
1013         gdouble average = stroke_average_width (objects);
1015         gtk_adjustment_set_value (a, sp_pixels_get_units (0.01 * a->value * average, *new_units));
1017         g_object_set_data (spw, "update", GUINT_TO_POINTER (FALSE));
1018         return TRUE;
1019     }
1021     return FALSE;
1025 /**
1026  * \brief  Creates a new widget for the line stroke style.
1027  *
1028  */
1029 GtkWidget *
1030 sp_stroke_style_line_widget_new(void)
1032     GtkWidget *spw, *f, *t, *hb, *sb, *us, *tb, *ds;
1033     GtkObject *a;
1035     GtkTooltips *tt = gtk_tooltips_new();
1037     spw = sp_widget_new_global(INKSCAPE);
1039     f = gtk_hbox_new (FALSE, 0);
1040     gtk_widget_show(f);
1041     gtk_container_add(GTK_CONTAINER(spw), f);
1043     t = gtk_table_new(3, 6, FALSE);
1044     gtk_widget_show(t);
1045     gtk_container_set_border_width(GTK_CONTAINER(t), 4);
1046     gtk_table_set_row_spacings(GTK_TABLE(t), 4);
1047     gtk_container_add(GTK_CONTAINER(f), t);
1048     gtk_object_set_data(GTK_OBJECT(spw), "stroke", t);
1050     gint i = 0;
1052     /* Stroke width */
1053     spw_label(t, _("Width:"), 0, i);
1055     hb = spw_hbox(t, 3, 1, i);
1057 // TODO: when this is gtkmmified, use an Inkscape::UI::Widget::ScalarUnit instead of the separate
1058 // spinbutton and unit selector for stroke width. In sp_stroke_style_line_update, use
1059 // setHundredPercent to remember the aeraged width corresponding to 100%. Then the
1060 // stroke_width_set_unit will be removed (because ScalarUnit takes care of conversions itself), and
1061 // with it, the two remaining calls of stroke_average_width, allowing us to get rid of that
1062 // function in desktop-style.
1064     a = gtk_adjustment_new(1.0, 0.0, 1000.0, 0.1, 10.0, 10.0);
1065     gtk_object_set_data(GTK_OBJECT(spw), "width", a);
1066     sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), 0.1, 3);
1067     gtk_tooltips_set_tip(tt, sb, _("Stroke width"), NULL);
1068     gtk_widget_show(sb);
1070     sp_dialog_defocus_on_enter(sb);
1072     gtk_box_pack_start(GTK_BOX(hb), sb, FALSE, FALSE, 0);
1073     us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
1074     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1075     if (desktop)
1076         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), sp_desktop_namedview(desktop)->doc_units);
1077     sp_unit_selector_add_unit(SP_UNIT_SELECTOR(us), &sp_unit_get_by_id(SP_UNIT_PERCENT), 0);
1078     g_signal_connect ( G_OBJECT (us), "set_unit", G_CALLBACK (stroke_width_set_unit), spw );
1079     gtk_widget_show(us);
1080     sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a) );
1081     gtk_box_pack_start(GTK_BOX(hb), us, FALSE, FALSE, 0);
1082     gtk_object_set_data(GTK_OBJECT(spw), "units", us);
1084     gtk_signal_connect( GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(sp_stroke_style_width_changed), spw );
1085     i++;
1087     /* Join type */
1088     // TRANSLATORS: The line join style specifies the shape to be used at the
1089     //  corners of paths. It can be "miter", "round" or "bevel".
1090     spw_label(t, _("Join:"), 0, i);
1092     hb = spw_hbox(t, 3, 1, i);
1094     tb = NULL;
1096     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_MITER,
1097                                 hb, spw, "join", "miter");
1099     // TRANSLATORS: Miter join: joining lines with a sharp (pointed) corner.
1100     //  For an example, draw a triangle with a large stroke width and modify the
1101     //  "Join" option (in the Fill and Stroke dialog).
1102     gtk_tooltips_set_tip(tt, tb, _("Miter join"), NULL);
1104     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_ROUND,
1105                                 hb, spw, "join", "round");
1107     // TRANSLATORS: Round join: joining lines with a rounded corner.
1108     //  For an example, draw a triangle with a large stroke width and modify the
1109     //  "Join" option (in the Fill and Stroke dialog).
1110     gtk_tooltips_set_tip(tt, tb, _("Round join"), NULL);
1112     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_BEVEL,
1113                                 hb, spw, "join", "bevel");
1115     // TRANSLATORS: Bevel join: joining lines with a blunted (flattened) corner.
1116     //  For an example, draw a triangle with a large stroke width and modify the
1117     //  "Join" option (in the Fill and Stroke dialog).
1118     gtk_tooltips_set_tip(tt, tb, _("Bevel join"), NULL);
1120     i++;
1122     /* Miterlimit  */
1123     // TRANSLATORS: Miter limit: only for "miter join", this limits the length
1124     //  of the sharp "spike" when the lines connect at too sharp an angle.
1125     // When two line segments meet at a sharp angle, a miter join results in a
1126     //  spike that extends well beyond the connection point. The purpose of the
1127     //  miter limit is to cut off such spikes (i.e. convert them into bevels)
1128     //  when they become too long.
1129     spw_label(t, _("Miter limit:"), 0, i);
1131     hb = spw_hbox(t, 3, 1, i);
1133     a = gtk_adjustment_new(4.0, 0.0, 100.0, 0.1, 10.0, 10.0);
1134     gtk_object_set_data(GTK_OBJECT(spw), "miterlimit", a);
1136     sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), 0.1, 2);
1137     gtk_tooltips_set_tip(tt, sb, _("Maximum length of the miter (in units of stroke width)"), NULL);
1138     gtk_widget_show(sb);
1139     gtk_object_set_data(GTK_OBJECT(spw), "miterlimit_sb", sb);
1140     sp_dialog_defocus_on_enter(sb);
1142     gtk_box_pack_start(GTK_BOX(hb), sb, FALSE, FALSE, 0);
1144     gtk_signal_connect( GTK_OBJECT(a), "value_changed",
1145                         GTK_SIGNAL_FUNC(sp_stroke_style_miterlimit_changed), spw );
1146     i++;
1148     /* Cap type */
1149     // TRANSLATORS: cap type specifies the shape for the ends of lines
1150     spw_label(t, _("Cap:"), 0, i);
1152     hb = spw_hbox(t, 3, 1, i);
1154     tb = NULL;
1156     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_BUTT,
1157                                 hb, spw, "cap", "butt");
1159     // TRANSLATORS: Butt cap: the line shape does not extend beyond the end point
1160     //  of the line; the ends of the line are square
1161     gtk_tooltips_set_tip(tt, tb, _("Butt cap"), NULL);
1163     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_ROUND,
1164                                 hb, spw, "cap", "round");
1166     // TRANSLATORS: Round cap: the line shape extends beyond the end point of the
1167     //  line; the ends of the line are rounded
1168     gtk_tooltips_set_tip(tt, tb, _("Round cap"), NULL);
1170     tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_SQUARE,
1171                                 hb, spw, "cap", "square");
1173     // TRANSLATORS: Square cap: the line shape extends beyond the end point of the
1174     //  line; the ends of the line are square
1175     gtk_tooltips_set_tip(tt, tb, _("Square cap"), NULL);
1177     i++;
1180     /* Dash */
1181     spw_label(t, _("Dashes:"), 0, i);
1182     ds = sp_dash_selector_new( inkscape_get_repr( INKSCAPE,
1183                                                   "palette.dashes") );
1185     gtk_widget_show(ds);
1186     gtk_table_attach( GTK_TABLE(t), ds, 1, 4, i, i+1,
1187                       (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
1188                       (GtkAttachOptions)0, 0, 0 );
1189     gtk_object_set_data(GTK_OBJECT(spw), "dash", ds);
1190     gtk_signal_connect( GTK_OBJECT(ds), "changed",
1191                         GTK_SIGNAL_FUNC(sp_stroke_style_line_dash_changed),
1192                         spw );
1193     i++;
1195     /* Drop down marker selectors*/
1197     // doing this here once, instead of for each preview, to speed things up
1198     SPDocument *sandbox = ink_markers_preview_doc ();
1200     // TRANSLATORS: Path markers are an SVG feature that allows you to attach arbitrary shapes
1201     // (arrowheads, bullets, faces, whatever) to the start, end, or middle nodes of a path.
1202     spw_label(t, _("Start Markers:"), 0, i);
1203     marker_start_menu  = ink_marker_menu( spw ,"marker-start", sandbox);
1204     gtk_signal_connect( GTK_OBJECT(marker_start_menu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw );
1205     gtk_widget_show(marker_start_menu);
1206     gtk_table_attach( GTK_TABLE(t), marker_start_menu, 1, 4, i, i+1,
1207                       (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
1208                       (GtkAttachOptions)0, 0, 0 );
1209     gtk_object_set_data(GTK_OBJECT(spw), "start_mark_menu", marker_start_menu);
1211     i++;
1212     spw_label(t, _("Mid Markers:"), 0, i);
1213     marker_mid_menu = ink_marker_menu( spw ,"marker-mid", sandbox);
1214     gtk_signal_connect( GTK_OBJECT(marker_mid_menu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw );
1215     gtk_widget_show(marker_mid_menu);
1216     gtk_table_attach( GTK_TABLE(t), marker_mid_menu, 1, 4, i, i+1,
1217                       (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
1218                       (GtkAttachOptions)0, 0, 0 );
1219     gtk_object_set_data(GTK_OBJECT(spw), "mid_mark_menu", marker_mid_menu);
1221     i++;
1222     spw_label(t, _("End Markers:"), 0, i);
1223     marker_end_menu = ink_marker_menu( spw ,"marker-end", sandbox);
1224     gtk_signal_connect( GTK_OBJECT(marker_end_menu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw );
1225     gtk_widget_show(marker_end_menu);
1226     gtk_table_attach( GTK_TABLE(t), marker_end_menu, 1, 4, i, i+1,
1227                       (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
1228                       (GtkAttachOptions)0, 0, 0 );
1229     gtk_object_set_data(GTK_OBJECT(spw), "end_mark_menu", marker_end_menu);
1231     i++;
1233     gtk_signal_connect( GTK_OBJECT(spw), "construct",
1234                         GTK_SIGNAL_FUNC(sp_stroke_style_line_construct),
1235                         NULL );
1236     gtk_signal_connect( GTK_OBJECT(spw), "modify_selection",
1237                         GTK_SIGNAL_FUNC(sp_stroke_style_line_selection_modified),
1238                         NULL );
1239     gtk_signal_connect( GTK_OBJECT(spw), "change_selection",
1240                         GTK_SIGNAL_FUNC(sp_stroke_style_line_selection_changed),
1241                         NULL );
1243     sp_stroke_style_line_update( SP_WIDGET(spw), desktop ? sp_desktop_selection(desktop) : NULL);
1245     return spw;
1249 /**
1250  * Callback for when the stroke style widget is called.  It causes
1251  * the stroke line style to be updated.
1252  */
1253 static void
1254 sp_stroke_style_line_construct(SPWidget *spw, gpointer data)
1257 #ifdef SP_SS_VERBOSE
1258     g_print( "Stroke style widget constructed: inkscape %p repr %p\n",
1259              spw->inkscape, spw->repr );
1260 #endif
1261     if (spw->inkscape) {
1262         sp_stroke_style_line_update(spw,
1263                                     ( SP_ACTIVE_DESKTOP
1264                                       ? sp_desktop_selection(SP_ACTIVE_DESKTOP)
1265                                       : NULL ));
1266     } 
1269 /**
1270  * Callback for when stroke style widget is modified.  
1271  * Triggers update action.
1272  */
1273 static void
1274 sp_stroke_style_line_selection_modified ( SPWidget *spw,
1275                                        Inkscape::Selection *selection,
1276                                        guint flags,
1277                                        gpointer data )
1279     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1280         sp_stroke_style_line_update (spw, selection);
1281     }
1285 /**
1286  * Callback for when stroke style widget is changed.
1287  * Triggers update action.
1288  */
1289 static void
1290 sp_stroke_style_line_selection_changed ( SPWidget *spw,
1291                                        Inkscape::Selection *selection,
1292                                        gpointer data )
1294     sp_stroke_style_line_update (spw, selection);
1298 /**
1299  * Sets selector widgets' dash style from an SPStyle object.
1300  */
1301 static void
1302 sp_dash_selector_set_from_style (GtkWidget *dsel, SPStyle *style)
1304     if (style->stroke_dash.n_dash > 0) {
1305         double d[64];
1306         int len = MIN(style->stroke_dash.n_dash, 64);
1307         for (int i = 0; i < len; i++) {
1308             if (style->stroke_width.computed != 0)
1309                 d[i] = style->stroke_dash.dash[i] / style->stroke_width.computed;
1310             else
1311                 d[i] = style->stroke_dash.dash[i]; // is there a better thing to do for stroke_width==0?
1312         }
1313         sp_dash_selector_set_dash(SP_DASH_SELECTOR(dsel), len, d,
1314                style->stroke_width.computed != 0?
1315                     style->stroke_dash.offset / style->stroke_width.computed  :
1316                     style->stroke_dash.offset);
1317     } else {
1318         sp_dash_selector_set_dash(SP_DASH_SELECTOR(dsel), 0, NULL, 0.0);
1319     }
1322 /**
1323  * Sets the join type for a line, and updates the stroke style widget's buttons
1324  */
1325 static void
1326 sp_jointype_set (SPWidget *spw, unsigned const jointype)
1328     GtkWidget *tb = NULL;
1329     switch (jointype) {
1330         case SP_STROKE_LINEJOIN_MITER:
1331             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_MITER));
1332             break;
1333         case SP_STROKE_LINEJOIN_ROUND:
1334             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_ROUND));
1335             break;
1336         case SP_STROKE_LINEJOIN_BEVEL:
1337             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_BEVEL));
1338             break;
1339         default:
1340             break;
1341     }
1342     sp_stroke_style_set_join_buttons (spw, tb);
1345 /**
1346  * Sets the cap type for a line, and updates the stroke style widget's buttons
1347  */
1348 static void
1349 sp_captype_set (SPWidget *spw, unsigned const captype)
1351     GtkWidget *tb = NULL;
1352     switch (captype) {
1353         case SP_STROKE_LINECAP_BUTT:
1354             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_BUTT));
1355             break;
1356         case SP_STROKE_LINECAP_ROUND:
1357             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_ROUND));
1358             break;
1359         case SP_STROKE_LINECAP_SQUARE:
1360             tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_SQUARE));
1361             break;
1362         default:
1363             break;
1364     }
1365     sp_stroke_style_set_cap_buttons (spw, tb);
1368 /**
1369  * Callback for when stroke style widget is updated, including markers, cap type,
1370  * join type, etc.
1371  */
1372 static void
1373 sp_stroke_style_line_update(SPWidget *spw, Inkscape::Selection *sel)
1375     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1376         return;
1377     }
1379     gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE));
1381     GtkWidget *sset = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "stroke"));
1382     GtkObject *width = GTK_OBJECT(gtk_object_get_data(GTK_OBJECT(spw), "width"));
1383     GtkObject *ml = GTK_OBJECT(gtk_object_get_data(GTK_OBJECT(spw), "miterlimit"));
1384     GtkWidget *us = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "units"));
1385     GtkWidget *dsel = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "dash"));
1387     // create temporary style
1388     SPStyle *query = sp_style_new ();
1389     // query into it
1390     int result_sw = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEWIDTH); 
1391     int result_ml = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEMITERLIMIT); 
1392     int result_cap = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKECAP); 
1393     int result_join = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEJOIN); 
1395     if (result_sw == QUERY_STYLE_NOTHING) {
1396         /* No objects stroked, set insensitive */
1397         gtk_widget_set_sensitive(sset, FALSE);
1399         gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE));
1400         return;
1401     } else {
1402         gtk_widget_set_sensitive(sset, TRUE);
1404         SPUnit const *unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
1406         if (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED) {
1407             sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), &sp_unit_get_by_id(SP_UNIT_PERCENT));
1408         } else {
1409             // same width, or only one object; no sense to keep percent, switch to absolute
1410             if (unit->base != SP_UNIT_ABSOLUTE && unit->base != SP_UNIT_DEVICE) {
1411                 sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), sp_desktop_namedview(SP_ACTIVE_DESKTOP)->doc_units);
1412             }
1413         }
1415         unit = sp_unit_selector_get_unit (SP_UNIT_SELECTOR (us));
1417         if (unit->base == SP_UNIT_ABSOLUTE || unit->base == SP_UNIT_DEVICE) {
1418             double avgwidth = sp_pixels_get_units (query->stroke_width.computed, *unit);
1419             gtk_adjustment_set_value(GTK_ADJUSTMENT(width), avgwidth);
1420         } else {
1421             gtk_adjustment_set_value(GTK_ADJUSTMENT(width), 100);
1422         }
1423     }
1425     if (result_ml != QUERY_STYLE_NOTHING)
1426         gtk_adjustment_set_value(GTK_ADJUSTMENT(ml), query->stroke_miterlimit.value); // TODO: reflect averagedness?
1428     if (result_join != QUERY_STYLE_MULTIPLE_DIFFERENT) {
1429         sp_jointype_set (spw, query->stroke_linejoin.value);
1430     } else {
1431         sp_stroke_style_set_join_buttons(spw, NULL);
1432     }
1434     if (result_cap != QUERY_STYLE_MULTIPLE_DIFFERENT) {
1435         sp_captype_set (spw, query->stroke_linecap.value);
1436     } else {
1437         sp_stroke_style_set_cap_buttons(spw, NULL);
1438     }
1440     g_free (query);
1442     if (!sel || sel->isEmpty())
1443         return;
1445     GSList const *objects = sel->itemList();
1446     SPObject * const object = SP_OBJECT(objects->data);
1447     SPStyle * const style = SP_OBJECT_STYLE(object);
1449     /* Markers */
1450     sp_stroke_style_update_marker_menus(spw, objects); // FIXME: make this desktop query too
1452     /* Dash */
1453     sp_dash_selector_set_from_style (dsel, style); // FIXME: make this desktop query too
1455     gtk_widget_set_sensitive(sset, TRUE);
1457     gtk_object_set_data(GTK_OBJECT(spw), "update",
1458                         GINT_TO_POINTER(FALSE));
1461 /**
1462  * Sets a line's dash properties in a CSS style object.
1463  */
1464 static void
1465 sp_stroke_style_set_scaled_dash(SPCSSAttr *css,
1466                                 int ndash, double *dash, double offset,
1467                                 double scale)
1469     if (ndash > 0) {
1470         Inkscape::CSSOStringStream osarray;
1471         for (int i = 0; i < ndash; i++) {
1472             osarray << dash[i] * scale;
1473             if (i < (ndash - 1)) {
1474                 osarray << ",";
1475             }
1476         }
1477         sp_repr_css_set_property(css, "stroke-dasharray", osarray.str().c_str());
1479         Inkscape::CSSOStringStream osoffset;
1480         osoffset << offset * scale;
1481         sp_repr_css_set_property(css, "stroke-dashoffset", osoffset.str().c_str());
1482     } else {
1483         sp_repr_css_set_property(css, "stroke-dasharray", "none");
1484         sp_repr_css_set_property(css, "stroke-dashoffset", NULL);
1485     }
1488 /**
1489  * Sets line properties like width, dashes, markers, etc. on all currently selected items.
1490  */
1491 static void
1492 sp_stroke_style_scale_line(SPWidget *spw)
1494     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1495         return;
1496     }
1498     gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE));
1500     GtkAdjustment *wadj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(spw), "width"));
1501     SPUnitSelector *us = SP_UNIT_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "units"));
1502     SPDashSelector *dsel = SP_DASH_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "dash"));
1503     GtkAdjustment *ml = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(spw), "miterlimit"));
1505     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1506     SPDocument *document = sp_desktop_document (desktop);
1507     Inkscape::Selection *selection = sp_desktop_selection (desktop);
1509     GSList const *items = selection->itemList();
1511     /* TODO: Create some standardized method */
1512     SPCSSAttr *css = sp_repr_css_attr_new();
1514     if (items) {
1516         double width_typed = wadj->value;
1517         double const miterlimit = ml->value;
1519         SPUnit const *const unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
1521         double *dash, offset;
1522         int ndash;
1523         sp_dash_selector_get_dash(dsel, &ndash, &dash, &offset);
1525         for (GSList const *i = items; i != NULL; i = i->next) {
1526             /* Set stroke width */
1527             double width;
1528             if (unit->base == SP_UNIT_ABSOLUTE || unit->base == SP_UNIT_DEVICE) {
1529                 width = sp_units_get_pixels (width_typed, *unit);
1530             } else { // percentage
1531                 gdouble old_w = SP_OBJECT_STYLE (i->data)->stroke_width.computed;
1532                 width = old_w * width_typed / 100;
1533             }
1535             {
1536                 Inkscape::CSSOStringStream os_width;
1537                 os_width << width;
1538                 sp_repr_css_set_property(css, "stroke-width", os_width.str().c_str());
1539             }
1541             {
1542                 Inkscape::CSSOStringStream os_ml;
1543                 os_ml << miterlimit;
1544                 sp_repr_css_set_property(css, "stroke-miterlimit", os_ml.str().c_str());
1545             }
1547             /* Set dash */
1548             sp_stroke_style_set_scaled_dash(css, ndash, dash, offset, width);
1550             sp_desktop_apply_css_recursive (SP_OBJECT(i->data), css, true);
1551         }
1553         g_free(dash);
1555         if (unit->base != SP_UNIT_ABSOLUTE && unit->base != SP_UNIT_DEVICE) {
1556             // reset to 100 percent
1557             gtk_adjustment_set_value (wadj, 100.0);
1558         }
1560     }
1562     // we have already changed the items, so set style without changing selection
1563     // FIXME: move the above stroke-setting stuff, including percentages, to desktop-style
1564     sp_desktop_set_style (desktop, css, false);
1566     sp_repr_css_attr_unref(css);
1568     sp_document_done(document, SP_VERB_DIALOG_FILL_STROKE, 
1569                      _("Set stroke style"));
1571     gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE));
1575 /**
1576  * Callback for when the stroke style's width changes.  
1577  * Causes all line styles to be applied to all selected items.
1578  */
1579 static void
1580 sp_stroke_style_width_changed(GtkAdjustment *adj, SPWidget *spw)
1582     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1583         return;
1584     }
1586     sp_stroke_style_scale_line(spw);
1589 /**
1590  * Callback for when the stroke style's miterlimit changes.  
1591  * Causes all line styles to be applied to all selected items.
1592  */
1593 static void
1594 sp_stroke_style_miterlimit_changed(GtkAdjustment *adj, SPWidget *spw)
1596     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1597         return;
1598     }
1600     sp_stroke_style_scale_line(spw);
1603 /**
1604  * Callback for when the stroke style's dash changes.  
1605  * Causes all line styles to be applied to all selected items.
1606  */
1607 static void
1608 sp_stroke_style_line_dash_changed(SPDashSelector *dsel, SPWidget *spw)
1610     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1611         return;
1612     }
1614     sp_stroke_style_scale_line(spw);
1619 /**
1620  * \brief  This routine handles toggle events for buttons in the stroke style
1621  *         dialog.
1622  * When activated, this routine gets the data for the various widgets, and then
1623  * calls the respective routines to update css properties, etc.
1624  *
1625  */
1626 static void
1627 sp_stroke_style_any_toggled(GtkToggleButton *tb, SPWidget *spw)
1629     if (gtk_object_get_data(GTK_OBJECT(spw), "update")) {
1630         return;
1631     }
1633     if (gtk_toggle_button_get_active(tb)) {
1635         gchar const *join
1636             = static_cast<gchar const *>(gtk_object_get_data(GTK_OBJECT(tb), "join"));
1637         gchar const *cap
1638             = static_cast<gchar const *>(gtk_object_get_data(GTK_OBJECT(tb), "cap"));
1640         if (join) {
1641             GtkWidget *ml = GTK_WIDGET(g_object_get_data(G_OBJECT(spw), "miterlimit_sb"));
1642             gtk_widget_set_sensitive (ml, !strcmp(join, "miter"));
1643         }
1645         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1647         /* TODO: Create some standardized method */
1648         SPCSSAttr *css = sp_repr_css_attr_new();
1650         if (join) {
1651             sp_repr_css_set_property(css, "stroke-linejoin", join);
1653             sp_desktop_set_style (desktop, css);
1655             sp_stroke_style_set_join_buttons(spw, GTK_WIDGET(tb));
1656         } else if (cap) {
1657             sp_repr_css_set_property(css, "stroke-linecap", cap);
1659             sp_desktop_set_style (desktop, css);
1661             sp_stroke_style_set_cap_buttons(spw, GTK_WIDGET(tb));
1662         }
1664         sp_repr_css_attr_unref(css);
1666         sp_document_done(sp_desktop_document(desktop), SP_VERB_DIALOG_FILL_STROKE,
1667                          _("Set stroke style"));
1668     }
1672 /**
1673  * Updates the join style toggle buttons
1674  */
1675 static void
1676 sp_stroke_style_set_join_buttons(SPWidget *spw, GtkWidget *active)
1678     GtkWidget *tb;
1680     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1681                                          INKSCAPE_STOCK_JOIN_MITER) );
1682     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1684     GtkWidget *ml = GTK_WIDGET(g_object_get_data(G_OBJECT(spw), "miterlimit_sb"));
1685     gtk_widget_set_sensitive(ml, (active == tb));
1687     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1688                                          INKSCAPE_STOCK_JOIN_ROUND) );
1689     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1690     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1691                                          INKSCAPE_STOCK_JOIN_BEVEL) );
1692     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1697 /**
1698  * Updates the cap style toggle buttons
1699  */
1700 static void
1701 sp_stroke_style_set_cap_buttons(SPWidget *spw, GtkWidget *active)
1703     GtkWidget *tb;
1705     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1706                                          INKSCAPE_STOCK_CAP_BUTT));
1707     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1708     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1709                                          INKSCAPE_STOCK_CAP_ROUND) );
1710     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1711     tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw),
1712                                          INKSCAPE_STOCK_CAP_SQUARE) );
1713     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb));
1716 /**
1717  * Sets the current marker in the marker menu.
1718  */
1719 static void
1720 ink_marker_menu_set_current(SPObject *marker, GtkOptionMenu *mnu)
1722     gtk_object_set_data(GTK_OBJECT(mnu), "update", GINT_TO_POINTER(TRUE));
1724     GtkMenu *m = GTK_MENU(gtk_option_menu_get_menu(mnu));
1725     if (marker != NULL) {
1726         bool mark_is_stock = false;
1727         if (SP_OBJECT_REPR(marker)->attribute("inkscape:stockid"))
1728             mark_is_stock = true;
1730         gchar *markname;
1731         if (mark_is_stock)
1732             markname = g_strdup(SP_OBJECT_REPR(marker)->attribute("inkscape:stockid"));
1733         else
1734             markname = g_strdup(SP_OBJECT_REPR(marker)->attribute("id"));
1736         int markpos = 0;
1737         GList *kids = GTK_MENU_SHELL(m)->children;
1738         int i = 0;
1739         for (; kids != NULL; kids = kids->next) {
1740             gchar *mark = (gchar *) g_object_get_data(G_OBJECT(kids->data), "marker");
1741             if ( mark && strcmp(mark, markname) == 0 ) {
1742                 if ( mark_is_stock && !strcmp((gchar *) g_object_get_data(G_OBJECT(kids->data), "stockid"), "true"))
1743                     markpos = i;
1744                 if ( !mark_is_stock && !strcmp((gchar *) g_object_get_data(G_OBJECT(kids->data), "stockid"), "false"))
1745                     markpos = i;
1746             }
1747             i++;
1748         }
1749         gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), markpos);
1751         g_free (markname);
1752     }
1753     else {
1754         gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0);
1755     }
1756     gtk_object_set_data(GTK_OBJECT(mnu), "update", GINT_TO_POINTER(FALSE));
1759 /**
1760  * Updates the marker menus to highlight the appropriate marker and scroll to 
1761  * that marker.
1762  */
1763 static void
1764 sp_stroke_style_update_marker_menus( SPWidget *spw,
1765                                      GSList const *objects)
1767     struct { char const *key; int loc; } const keyloc[] = {
1768         { "start_mark_menu", SP_MARKER_LOC_START },
1769         { "mid_mark_menu", SP_MARKER_LOC_MID },
1770         { "end_mark_menu", SP_MARKER_LOC_END }
1771     };
1773     bool all_texts = true;
1774     for (GSList *i = (GSList *) objects; i != NULL; i = i->next) {
1775         if (!SP_IS_TEXT (i->data)) {
1776             all_texts = false;
1777         }
1778     }
1780     for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) {
1781         GtkOptionMenu *mnu = (GtkOptionMenu *) g_object_get_data(G_OBJECT(spw), keyloc[i].key);
1782         if (all_texts) {
1783             // Per SVG spec, text objects cannot have markers; disable menus if only texts are selected
1784             gtk_widget_set_sensitive (GTK_WIDGET(mnu), FALSE);
1785         } else {
1786             gtk_widget_set_sensitive (GTK_WIDGET(mnu), TRUE);
1787         }
1788     }
1790     // We show markers of the first object in the list only
1791     // FIXME: use the first in the list that has the marker of each type, if any
1792     SPObject *object = SP_OBJECT(objects->data);
1794     for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) {
1795         // For all three marker types,
1797         // find the corresponding menu
1798         GtkOptionMenu *mnu = (GtkOptionMenu *) g_object_get_data(G_OBJECT(spw), keyloc[i].key);
1800         // Quit if we're in update state
1801         if (gtk_object_get_data(GTK_OBJECT(mnu), "update")) {
1802             return;
1803         }
1805         if (object->style->marker[keyloc[i].loc].value != NULL && !all_texts) {
1806             // If the object has this type of markers,
1808             // Extract the name of the marker that the object uses
1809             SPObject *marker = ink_extract_marker_name(object->style->marker[keyloc[i].loc].value);
1810             // Scroll the menu to that marker
1811             ink_marker_menu_set_current (marker, mnu);
1813         } else {
1814             gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0);
1815         }
1816     }
1820 /**
1821  * Extract the actual name of the link
1822  * e.g. get mTriangle from url(#mTriangle).
1823  * \return Buffer containing the actual name, allocated from GLib;
1824  * the caller should free the buffer when they no longer need it.
1825  */
1826 static SPObject*
1827 ink_extract_marker_name(gchar const *n)
1829     gchar const *p = n;
1830     while (*p != '\0' && *p != '#') {
1831         p++;
1832     }
1834     if (*p == '\0' || p[1] == '\0') {
1835         return NULL;
1836     }
1838     p++;
1839     int c = 0;
1840     while (p[c] != '\0' && p[c] != ')') {
1841         c++;
1842     }
1844     if (p[c] == '\0') {
1845         return NULL;
1846     }
1848     gchar* b = g_strdup(p);
1849     b[c] = '\0';
1852     SPDesktop *desktop = inkscape_active_desktop();
1853     SPDocument *doc = sp_desktop_document(desktop);
1854     SPObject *marker = doc->getObjectById(b);
1855     return marker;
1859 /*
1860   Local Variables:
1861   mode:c++
1862   c-file-style:"stroustrup"
1863   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1864   indent-tabs-mode:nil
1865   fill-column:99
1866   End:
1867 */
1868 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :