Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / widgets / gradient-vector.cpp
1 /*
2  * Gradient vector selection widget
3  *
4  * Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   bulia byak <buliabyak@users.sf.net>
7  *   MenTaLguY <mental@rydia.net>
8  *   Jon A. Cruz <jon@joncruz.org>
9  *   Abhishek Sharma
10  *
11  * Copyright (C) 2001-2002 Lauris Kaplinski
12  * Copyright (C) 2001 Ximian, Inc.
13  * Copyright (C) 2004 Monash University
14  * Copyright (C) 2004 David Turner
15  * Copyright (C) 2006 MenTaLguY
16  * Copyright (C) 2010 Jon A. Cruz
17  *
18  * Released under GNU GPL, read the file 'COPYING' for more information
19  *
20  */
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25 #ifdef HAVE_STRING_H
26 #endif
27 #include <gtk/gtk.h>
28 #include "macros.h"
29 #include <glibmm/i18n.h>
30 #include "../widgets/gradient-image.h"
31 #include "../inkscape.h"
32 #include "../document-private.h"
33 #include "../gradient-chemistry.h"
34 #include "gradient-vector.h"
35 #include "../helper/window.h"
37 #include "xml/repr.h"
39 #include "../dialogs/dialog-events.h"
40 #include "../preferences.h"
41 #include "svg/css-ostringstream.h"
42 #include "sp-stop.h"
44 #include <sigc++/functors/ptr_fun.h>
45 #include <sigc++/adaptors/bind.h>
47 using Inkscape::DocumentUndo;
49 enum {
50     VECTOR_SET,
51     LAST_SIGNAL
52 };
54 static void sp_gradient_vector_selector_class_init(SPGradientVectorSelectorClass *klass);
55 static void sp_gradient_vector_selector_init(SPGradientVectorSelector *gvs);
56 static void sp_gradient_vector_selector_destroy(GtkObject *object);
58 static void sp_gvs_gradient_release(SPObject *obj, SPGradientVectorSelector *gvs);
59 static void sp_gvs_defs_release(SPObject *defs, SPGradientVectorSelector *gvs);
60 static void sp_gvs_defs_modified(SPObject *defs, guint flags, SPGradientVectorSelector *gvs);
62 static void sp_gvs_rebuild_gui_full(SPGradientVectorSelector *gvs);
63 static void sp_gvs_gradient_activate(GtkMenuItem *mi, SPGradientVectorSelector *gvs);
65 static GtkVBoxClass *parent_class;
66 static guint signals[LAST_SIGNAL] = {0};
68 // TODO FIXME kill these globals!!!
69 static GtkWidget *dlg = NULL;
70 static win_data wd;
71 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
72 static Glib::ustring const prefs_path = "/dialogs/gradienteditor/";
74 GType sp_gradient_vector_selector_get_type(void)
75 {
76     static GType type = 0;
77     if (!type) {
78         static const GTypeInfo info = {
79             sizeof(SPGradientVectorSelectorClass),
80             NULL, /* base_init */
81             NULL, /* base_finalize */
82             reinterpret_cast<GClassInitFunc>(sp_gradient_vector_selector_class_init),
83             NULL, /* class_finalize */
84             NULL, /* class_data */
85             sizeof(SPGradientVectorSelector),
86             0,    /* n_preallocs */
87             reinterpret_cast<GInstanceInitFunc>(sp_gradient_vector_selector_init),
88             0,    /* value_table */
89         };
91         type = g_type_register_static( GTK_TYPE_VBOX,
92                                        "SPGradientVectorSelector",
93                                        &info,
94                                        static_cast< GTypeFlags >(0) );
95     }
96     return type;
97 }
99 static void sp_gradient_vector_selector_class_init(SPGradientVectorSelectorClass *klass)
101     GtkObjectClass *object_class;
103     object_class = GTK_OBJECT_CLASS(klass);
105     parent_class = static_cast<GtkVBoxClass*>(gtk_type_class(GTK_TYPE_VBOX));
107     signals[VECTOR_SET] = gtk_signal_new( "vector_set",
108                                           GTK_RUN_LAST,
109                                           GTK_CLASS_TYPE(object_class),
110                                           GTK_SIGNAL_OFFSET(SPGradientVectorSelectorClass, vector_set),
111                                           gtk_marshal_NONE__POINTER,
112                                           GTK_TYPE_NONE, 1,
113                                           GTK_TYPE_POINTER);
115     object_class->destroy = sp_gradient_vector_selector_destroy;
118 static void sp_gradient_vector_selector_init(SPGradientVectorSelector *gvs)
120     gvs->idlabel = TRUE;
122     gvs->swatched = false;
124     gvs->doc = NULL;
125     gvs->gr = NULL;
127     new (&gvs->gradient_release_connection) sigc::connection();
128     new (&gvs->defs_release_connection) sigc::connection();
129     new (&gvs->defs_modified_connection) sigc::connection();
131     gvs->menu = gtk_option_menu_new();
132     gtk_widget_show(gvs->menu);
133     gtk_box_pack_start(GTK_BOX(gvs), gvs->menu, TRUE, TRUE, 0);
136 static void sp_gradient_vector_selector_destroy(GtkObject *object)
138     SPGradientVectorSelector *gvs = SP_GRADIENT_VECTOR_SELECTOR(object);
140     if (gvs->gr) {
141         gvs->gradient_release_connection.disconnect();
142         gvs->gr = NULL;
143     }
145     if (gvs->doc) {
146         gvs->defs_release_connection.disconnect();
147         gvs->defs_modified_connection.disconnect();
148         gvs->doc = NULL;
149     }
151     gvs->gradient_release_connection.~connection();
152     gvs->defs_release_connection.~connection();
153     gvs->defs_modified_connection.~connection();
155     if ((reinterpret_cast<GtkObjectClass *>(parent_class))->destroy) {
156         (* (reinterpret_cast<GtkObjectClass *>(parent_class))->destroy) (object);
157     }
160 GtkWidget *sp_gradient_vector_selector_new(SPDocument *doc, SPGradient *gr)
162     GtkWidget *gvs;
164     g_return_val_if_fail(!gr || SP_IS_GRADIENT(gr), NULL);
165     g_return_val_if_fail(!gr || (SP_OBJECT_DOCUMENT(gr) == doc), NULL);
167     gvs = static_cast<GtkWidget*>(gtk_type_new(SP_TYPE_GRADIENT_VECTOR_SELECTOR));
169     if (doc) {
170         sp_gradient_vector_selector_set_gradient(SP_GRADIENT_VECTOR_SELECTOR(gvs), doc, gr);
171     } else {
172         sp_gvs_rebuild_gui_full(SP_GRADIENT_VECTOR_SELECTOR(gvs));
173     }
175     return gvs;
178 void sp_gradient_vector_selector_set_gradient(SPGradientVectorSelector *gvs, SPDocument *doc, SPGradient *gr)
180 //     g_message("sp_gradient_vector_selector_set_gradient(%p, %p, %p) [%s] %d %d", gvs, doc, gr,
181 //               (gr ? gr->getId():"N/A"),
182 //               (gr ? gr->isSwatch() : -1),
183 //               (gr ? gr->isSolid() : -1));
184     static gboolean suppress = FALSE;
186     g_return_if_fail(gvs != NULL);
187     g_return_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs));
188     g_return_if_fail(!gr || (doc != NULL));
189     g_return_if_fail(!gr || SP_IS_GRADIENT(gr));
190     g_return_if_fail(!gr || (SP_OBJECT_DOCUMENT(gr) == doc));
191     g_return_if_fail(!gr || gr->hasStops());
193     if (doc != gvs->doc) {
194         /* Disconnect signals */
195         if (gvs->gr) {
196             gvs->gradient_release_connection.disconnect();
197             gvs->gr = NULL;
198         }
199         if (gvs->doc) {
200             gvs->defs_release_connection.disconnect();
201             gvs->defs_modified_connection.disconnect();
202             gvs->doc = NULL;
203         }
205         // Connect signals
206         if (doc) {
207             gvs->defs_release_connection = SP_DOCUMENT_DEFS(doc)->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gvs_defs_release), gvs));
208             gvs->defs_modified_connection = SP_DOCUMENT_DEFS(doc)->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_gvs_defs_modified), gvs));
209         }
210         if (gr) {
211             gvs->gradient_release_connection = gr->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gvs_gradient_release), gvs));
212         }
213         gvs->doc = doc;
214         gvs->gr = gr;
215         sp_gvs_rebuild_gui_full(gvs);
216         if (!suppress) g_signal_emit(G_OBJECT(gvs), signals[VECTOR_SET], 0, gr);
217     } else if (gr != gvs->gr) {
218         // Harder case - keep document, rebuild menus and stuff
219         // fixme: (Lauris)
220         suppress = TRUE;
221         sp_gradient_vector_selector_set_gradient(gvs, NULL, NULL);
222         sp_gradient_vector_selector_set_gradient(gvs, doc, gr);
223         suppress = FALSE;
224         g_signal_emit(G_OBJECT(gvs), signals[VECTOR_SET], 0, gr);
225     }
226     /* The case of setting NULL -> NULL is not very interesting */
229 SPDocument *sp_gradient_vector_selector_get_document(SPGradientVectorSelector *gvs)
231     g_return_val_if_fail(gvs != NULL, NULL);
232     g_return_val_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs), NULL);
234     return gvs->doc;
237 SPGradient *sp_gradient_vector_selector_get_gradient(SPGradientVectorSelector *gvs)
239     g_return_val_if_fail(gvs != NULL, NULL);
240     g_return_val_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs), NULL);
242     return gvs->gr;
245 static void sp_gvs_rebuild_gui_full(SPGradientVectorSelector *gvs)
247     /* Clear old menu, if there is any */
248     if (gtk_option_menu_get_menu(GTK_OPTION_MENU(gvs->menu))) {
249         gtk_option_menu_remove_menu(GTK_OPTION_MENU(gvs->menu));
250     }
252     /* Create new menu widget */
253     GtkWidget *m = gtk_menu_new();
254     gtk_widget_show(m);
256     /* Pick up all gradients with vectors */
257     GSList *gl = NULL;
258     if (gvs->gr) {
259         const GSList *gradients = SP_OBJECT_DOCUMENT(gvs->gr)->getResourceList("gradient");
260         for (const GSList *curr = gradients; curr; curr = curr->next) {
261             SPGradient* grad = SP_GRADIENT(curr->data);
262             if ( grad->hasStops() && (grad->isSwatch() == gvs->swatched) ) {
263                 gl = g_slist_prepend(gl, curr->data);
264             }
265         }
266     }
267     gl = g_slist_reverse(gl);
269     gint pos = 0;
270     gint idx = 0;
272     if (!gvs->doc) {
273         GtkWidget *i;
274         i = gtk_menu_item_new_with_label(_("No document selected"));
275         gtk_widget_show(i);
276         gtk_menu_append(GTK_MENU(m), i);
277         gtk_widget_set_sensitive(gvs->menu, FALSE);
278     } else if (!gl) {
279         GtkWidget *i;
280         i = gtk_menu_item_new_with_label(_("No gradients in document"));
281         gtk_widget_show(i);
282         gtk_menu_append(GTK_MENU(m), i);
283         gtk_widget_set_sensitive(gvs->menu, FALSE);
284     } else if (!gvs->gr) {
285         GtkWidget *i;
286         i = gtk_menu_item_new_with_label(_("No gradient selected"));
287         gtk_widget_show(i);
288         gtk_menu_append(GTK_MENU(m), i);
289         gtk_widget_set_sensitive(gvs->menu, FALSE);
290     } else {
291         while (gl) {
292             SPGradient *gr;
293             GtkWidget *i, *w;
294             gr = SP_GRADIENT(gl->data);
295             gl = g_slist_remove(gl, gr);
297             /* We have to know: */
298             /* Gradient destroy */
299             /* Gradient name change */
300             i = gtk_menu_item_new();
301             gtk_widget_show(i);
302             g_object_set_data(G_OBJECT(i), "gradient", gr);
303             g_signal_connect(G_OBJECT(i), "activate", G_CALLBACK(sp_gvs_gradient_activate), gvs);
305             w = sp_gradient_image_new(gr);
306             gtk_widget_show(w);
308             if (gvs->idlabel) {
309                 GtkWidget *hb, *l;
310                 hb = gtk_hbox_new(FALSE, 4);
311                 gtk_widget_show(hb);
312                 l = gtk_label_new(gr->getId());
313                 gtk_widget_show(l);
314                 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
315                 gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0);
316                 gtk_box_pack_start(GTK_BOX(hb), w, FALSE, FALSE, 0);
317                 w = hb;
318             }
320             gtk_container_add(GTK_CONTAINER(i), w);
322             gtk_menu_append(GTK_MENU(m), i);
324             if (gr == gvs->gr) {
325                 pos = idx;
326             }
327             idx += 1;
328         }
329         gtk_widget_set_sensitive(gvs->menu, TRUE);
330     }
332     gtk_option_menu_set_menu(GTK_OPTION_MENU(gvs->menu), m);
333     /* Set history */
334     gtk_option_menu_set_history(GTK_OPTION_MENU(gvs->menu), pos);
337 static void sp_gvs_gradient_activate(GtkMenuItem *mi, SPGradientVectorSelector *gvs)
339     SPGradient *gr, *norm;
341     gr = (SPGradient*)g_object_get_data(G_OBJECT(mi), "gradient");
342     /* Hmmm... bad things may happen here, if actual gradient is something new */
343     /* Namely - menuitems etc. will be fucked up */
344     /* Hmmm - probably we can just re-set it as menuitem data (Lauris) */
346     //g_print("SPGradientVectorSelector: gradient %s activated\n", gr->getId());
347     //g_message("Setting to gradient %p   swatch:%d   solid:%d", gr, gr->isSwatch(), gr->isSolid());
349     norm = sp_gradient_ensure_vector_normalized(gr);
350     if (norm != gr) {
351         //g_print("SPGradientVectorSelector: become %s after normalization\n", norm->getId());
352         /* But be careful that we do not have gradient saved anywhere else */
353         g_object_set_data(G_OBJECT(mi), "gradient", norm);
354     }
356     /* fixme: Really we would want to use _set_vector */
357     /* Detach old */
358     if (gvs->gr) {
359         gvs->gradient_release_connection.disconnect();
360         gvs->gr = NULL;
361     }
362     /* Attach new */
363     if (norm) {
364         gvs->gradient_release_connection = norm->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gvs_gradient_release), gvs));
365         gvs->gr = norm;
366     }
368     g_signal_emit(G_OBJECT(gvs), signals[VECTOR_SET], 0, norm);
370     if (norm != gr) {
371         /* We do extra undo push here */
372         /* If handler has already done it, it is just NOP */
373         // FIXME: looks like this is never a valid undo step, consider removing this
374         DocumentUndo::done(SP_OBJECT_DOCUMENT(norm), SP_VERB_CONTEXT_GRADIENT,
375                            /* TODO: annotate */ "gradient-vector.cpp:350");
376     }
379 static void sp_gvs_gradient_release(SPObject */*obj*/, SPGradientVectorSelector *gvs)
381     /* Disconnect gradient */
382     if (gvs->gr) {
383         gvs->gradient_release_connection.disconnect();
384         gvs->gr = NULL;
385     }
387     /* Rebuild GUI */
388     sp_gvs_rebuild_gui_full(gvs);
391 static void sp_gvs_defs_release(SPObject */*defs*/, SPGradientVectorSelector *gvs)
393     gvs->doc = NULL;
395     gvs->defs_release_connection.disconnect();
396     gvs->defs_modified_connection.disconnect();
398     /* Disconnect gradient as well */
399     if (gvs->gr) {
400         gvs->gradient_release_connection.disconnect();
401         gvs->gr = NULL;
402     }
404     /* Rebuild GUI */
405     sp_gvs_rebuild_gui_full(gvs);
408 static void sp_gvs_defs_modified(SPObject */*defs*/, guint /*flags*/, SPGradientVectorSelector *gvs)
410     /* fixme: We probably have to check some flags here (Lauris) */
412     sp_gvs_rebuild_gui_full(gvs);
415 void SPGradientVectorSelector::setSwatched()
417     swatched = true;
418     sp_gvs_rebuild_gui_full(this);
421 /*##################################################################
422   ###                 Vector Editing Widget
423   ##################################################################*/
425 #include "../widgets/sp-color-notebook.h"
426 #include "../widgets/sp-color-preview.h"
427 #include "../widgets/widget-sizes.h"
428 #include "../xml/node-event-vector.h"
429 #include "../svg/svg-color.h"
432 #define PAD 4
434 static GtkWidget *sp_gradient_vector_widget_new(SPGradient *gradient, SPStop *stop);
436 static void sp_gradient_vector_widget_load_gradient(GtkWidget *widget, SPGradient *gradient);
437 static gint sp_gradient_vector_dialog_delete(GtkWidget *widget, GdkEvent *event, GtkWidget *dialog);
438 static void sp_gradient_vector_dialog_destroy(GtkObject *object, gpointer data);
440 static void sp_gradient_vector_widget_destroy(GtkObject *object, gpointer data);
441 static void sp_gradient_vector_gradient_release(SPObject *obj, GtkWidget *widget);
442 static void sp_gradient_vector_gradient_modified(SPObject *obj, guint flags, GtkWidget *widget);
443 static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GtkObject *object);
444 static void sp_gradient_vector_color_changed(SPColorSelector *csel, GtkObject *object);
445 static void update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop);
447 static gboolean blocked = FALSE;
449 static void grad_edit_dia_stop_added_or_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node */*child*/, Inkscape::XML::Node */*ref*/, gpointer data)
451     GtkWidget *vb = GTK_WIDGET(data);
452     GtkWidget *mnu = (GtkWidget *)g_object_get_data(G_OBJECT(vb), "stopmenu");
453     SPGradient *gradient = (SPGradient *)g_object_get_data(G_OBJECT(vb), "gradient");
454     update_stop_list(mnu, gradient, NULL);
457 //FIXME!!! We must also listen to attr changes on all children (i.e. stops) too,
458 //otherwise the dialog does not reflect undoing color or offset change. This is a major
459 //hassle, unless we have a "one of the descendants changed in some way" signal.
460 static Inkscape::XML::NodeEventVector grad_edit_dia_repr_events =
462     grad_edit_dia_stop_added_or_removed, /* child_added */
463     grad_edit_dia_stop_added_or_removed, /* child_removed */
464     NULL, /* attr_changed*/
465     NULL, /* content_changed */
466     NULL  /* order_changed */
467 };
469 static void verify_grad(SPGradient *gradient)
471     int i = 0;
472     SPStop *stop = NULL;
473     /* count stops */
474     for ( SPObject *ochild = gradient->firstChild() ; ochild ; ochild = ochild->getNext() ) {
475         if (SP_IS_STOP(ochild)) {
476             i++;
477             stop = SP_STOP(ochild);
478         }
479     }
481     Inkscape::XML::Document *xml_doc;
482     xml_doc = SP_OBJECT_REPR(gradient)->document();
484     if (i < 1) {
485         Inkscape::CSSOStringStream os;
486         os << "stop-color: #000000;stop-opacity:" << 1.0 << ";";
488         Inkscape::XML::Node *child;
490         child = xml_doc->createElement("svg:stop");
491         sp_repr_set_css_double(child, "offset", 0.0);
492         child->setAttribute("style", os.str().c_str());
493         SP_OBJECT_REPR(gradient)->addChild(child, NULL);
494         Inkscape::GC::release(child);
496         child = xml_doc->createElement("svg:stop");
497         sp_repr_set_css_double(child, "offset", 1.0);
498         child->setAttribute("style", os.str().c_str());
499         SP_OBJECT_REPR(gradient)->addChild(child, NULL);
500         Inkscape::GC::release(child);
501     }
502     if (i < 2) {
503         sp_repr_set_css_double(SP_OBJECT_REPR(stop), "offset", 0.0);
504         Inkscape::XML::Node *child = SP_OBJECT_REPR(stop)->duplicate(SP_OBJECT_REPR(gradient)->document());
505         sp_repr_set_css_double(child, "offset", 1.0);
506         SP_OBJECT_REPR(gradient)->addChild(child, SP_OBJECT_REPR(stop));
507         Inkscape::GC::release(child);
508     }
511 static void select_stop_in_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop)
513     int i = 0;
514     for ( SPObject *ochild = gradient->firstChild() ; ochild ; ochild = ochild->getNext() ) {
515         if (SP_IS_STOP(ochild)) {
516             if (SP_OBJECT(ochild) == SP_OBJECT(new_stop)) {
517                 gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), i);
518                 break;
519             }
520             i++;
521         }
522     }
525 static void update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop)
528     if (!SP_IS_GRADIENT(gradient)) {
529         return;
530     }
532     blocked = TRUE;
534     /* Clear old menu, if there is any */
535     if (gtk_option_menu_get_menu(GTK_OPTION_MENU(mnu))) {
536         gtk_option_menu_remove_menu(GTK_OPTION_MENU(mnu));
537     }
539     /* Create new menu widget */
540     GtkWidget *m = gtk_menu_new();
541     gtk_widget_show(m);
542     GSList *sl = NULL;
543     if ( gradient->hasStops() ) {
544         for ( SPObject *ochild = gradient->firstChild() ; ochild ; ochild = ochild->getNext() ) {
545             if (SP_IS_STOP(ochild)) {
546                 sl = g_slist_append(sl, ochild);
547             }
548         }
549     }
550     if (!sl) {
551         GtkWidget *i = gtk_menu_item_new_with_label(_("No stops in gradient"));
552         gtk_widget_show(i);
553         gtk_menu_append(GTK_MENU(m), i);
554         gtk_widget_set_sensitive(mnu, FALSE);
555     } else {
557         for (; sl != NULL; sl = sl->next){
558             if (SP_IS_STOP(sl->data)){
559                 SPStop *stop = SP_STOP(sl->data);
560                 GtkWidget *i = gtk_menu_item_new();
561                 gtk_widget_show(i);
562                 g_object_set_data(G_OBJECT(i), "stop", stop);
563                 GtkWidget *hb = gtk_hbox_new(FALSE, 4);
564                 GtkWidget *cpv = sp_color_preview_new(sp_stop_get_rgba32(stop));
565                 gtk_widget_show(cpv);
566                 gtk_container_add( GTK_CONTAINER(hb), cpv );
567                 g_object_set_data( G_OBJECT(i), "preview", cpv );
568                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) sl->data);
569                 GtkWidget *l = gtk_label_new(repr->attribute("id"));
570                 gtk_widget_show(l);
571                 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
572                 gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0);
573                 gtk_widget_show(hb);
574                 gtk_container_add(GTK_CONTAINER(i), hb);
575                 gtk_menu_append(GTK_MENU(m), i);
576             }
577         }
579         gtk_widget_set_sensitive(mnu, TRUE);
580     }
581     gtk_option_menu_set_menu(GTK_OPTION_MENU(mnu), m);
583     /* Set history */
584     if (new_stop == NULL) {
585         gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0);
586     } else {
587         select_stop_in_list(mnu, gradient, new_stop);
588     }
590     blocked = FALSE;
594 // user selected existing stop from list
595 static void sp_grad_edit_select(GtkOptionMenu *mnu, GtkWidget *tbl)
597     GObject *item = G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu))));
598     SPStop *stop = SP_STOP(g_object_get_data(item, "stop"));
599     if (!stop) {
600         return;
601     }
603     blocked = TRUE;
605     SPColorSelector *csel = (SPColorSelector*)g_object_get_data(G_OBJECT(tbl), "cselector");
606     // set its color, from the stored array
607     csel->base->setColorAlpha( stop->getEffectiveColor(), stop->opacity );
608     GtkWidget *offspin = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offspn"));
609     GtkWidget *offslide =GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offslide"));
611     GtkAdjustment *adj = static_cast<GtkAdjustment*>(gtk_object_get_data(GTK_OBJECT(tbl), "offset"));
613     bool isEndStop = false;
615     SPStop *prev = NULL;
616     prev = stop->getPrevStop();
617     if (prev != NULL )  {
618         adj->lower = prev->offset;
619     } else {
620         isEndStop = true;
621         adj->lower = 0;
622     }
624     SPStop *next = NULL;
625     next = stop->getNextStop();
626     if (next != NULL ) {
627         adj->upper = next->offset;
628     } else {
629         isEndStop = true;
630         adj->upper = 1.0;
631     }
633     //fixme: does this work on all possible input gradients?
634     if (!isEndStop) {
635         gtk_widget_set_sensitive(offslide, TRUE);
636         gtk_widget_set_sensitive(GTK_WIDGET(offspin), TRUE);
637     } else {
638         gtk_widget_set_sensitive(offslide, FALSE);
639         gtk_widget_set_sensitive(GTK_WIDGET(offspin), FALSE);
640     }
642     gtk_adjustment_set_value(adj, stop->offset);
644     gtk_adjustment_changed(adj);
646     blocked = FALSE;
652 static void offadjustmentChanged( GtkAdjustment *adjustment, GtkWidget *vb)
654     if (!blocked) {
655         blocked = TRUE;
657         GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(vb), "stopmenu"));
658         if ( g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop") ) {
659             SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop"));
661             stop->offset = adjustment->value;
662             sp_repr_set_css_double(SP_OBJECT_REPR(stop), "offset", stop->offset);
664             DocumentUndo::maybeDone(SP_OBJECT_DOCUMENT(stop), "gradient:stop:offset", SP_VERB_CONTEXT_GRADIENT,
665                                     _("Change gradient stop offset"));
667             blocked = FALSE;
668         }
669     }
672 guint32 sp_average_color(guint32 c1, guint32 c2, gdouble p = 0.5)
674     guint32 r = (guint32) (SP_RGBA32_R_U(c1) * p + SP_RGBA32_R_U(c2) * (1 - p));
675     guint32 g = (guint32) (SP_RGBA32_G_U(c1) * p + SP_RGBA32_G_U(c2) * (1 - p));
676     guint32 b = (guint32) (SP_RGBA32_B_U(c1) * p + SP_RGBA32_B_U(c2) * (1 - p));
677     guint32 a = (guint32) (SP_RGBA32_A_U(c1) * p + SP_RGBA32_A_U(c2) * (1 - p));
679     return SP_RGBA32_U_COMPOSE(r, g, b, a);
683 static void sp_grd_ed_add_stop(GtkWidget */*widget*/,  GtkWidget *vb)
685     SPGradient *gradient = static_cast<SPGradient *>(g_object_get_data(G_OBJECT(vb), "gradient"));
686     verify_grad(gradient);
687     GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(vb), "stopmenu"));
689     SPStop *stop = static_cast<SPStop *>(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop"));
691     if (stop == NULL) {
692         return;
693     }
695     Inkscape::XML::Node *new_stop_repr = NULL;
697     SPStop *next = stop->getNextStop();
699     if (next == NULL) {
700         SPStop *prev = stop->getPrevStop();
701         if (prev != NULL) {
702             next = stop;
703             stop = prev;
704         }
705     }
707     if (next != NULL) {
708         new_stop_repr = SP_OBJECT_REPR(stop)->duplicate(SP_OBJECT_REPR(gradient)->document());
709         SP_OBJECT_REPR(gradient)->addChild(new_stop_repr, SP_OBJECT_REPR(stop));
710     } else {
711         next = stop;
712         new_stop_repr = SP_OBJECT_REPR(stop->getPrevStop())->duplicate(SP_OBJECT_REPR(gradient)->document());
713         SP_OBJECT_REPR(gradient)->addChild(new_stop_repr, SP_OBJECT_REPR(stop->getPrevStop()));
714     }
716     SPStop *newstop = (SPStop *) SP_OBJECT_DOCUMENT(gradient)->getObjectByRepr(new_stop_repr);
718     newstop->offset = (stop->offset + next->offset) * 0.5 ;
720     guint32 const c1 = sp_stop_get_rgba32(stop);
721     guint32 const c2 = sp_stop_get_rgba32(next);
722     guint32 cnew = sp_average_color(c1, c2);
724     Inkscape::CSSOStringStream os;
725     gchar c[64];
726     sp_svg_write_color(c, sizeof(c), cnew);
727     gdouble opacity = static_cast<gdouble>(SP_RGBA32_A_F(cnew));
728     os << "stop-color:" << c << ";stop-opacity:" << opacity <<";";
729     SP_OBJECT_REPR (newstop)->setAttribute("style", os.str().c_str());
730     sp_repr_set_css_double( SP_OBJECT_REPR(newstop), "offset", (double)newstop->offset);
732     sp_gradient_vector_widget_load_gradient(vb, gradient);
733     Inkscape::GC::release(new_stop_repr);
734     update_stop_list(GTK_WIDGET(mnu), gradient, newstop);
735     GtkWidget *offspin = GTK_WIDGET(g_object_get_data(G_OBJECT(vb), "offspn"));
736     GtkWidget *offslide =GTK_WIDGET(g_object_get_data(G_OBJECT(vb), "offslide"));
737     gtk_widget_set_sensitive(offslide, TRUE);
738     gtk_widget_set_sensitive(GTK_WIDGET(offspin), TRUE);
739     DocumentUndo::done(SP_OBJECT_DOCUMENT(gradient), SP_VERB_CONTEXT_GRADIENT,
740                        _("Add gradient stop"));
743 static void sp_grd_ed_del_stop(GtkWidget */*widget*/,  GtkWidget *vb)
745     SPGradient *gradient = static_cast<SPGradient *>(g_object_get_data(G_OBJECT(vb), "gradient"));
747     GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(vb), "stopmenu"));
748     if (!g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")) return;
749     SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop"));
750     if (gradient->vector.stops.size() > 2) { // 2 is the minimum
752         // if we delete first or last stop, move the next/previous to the edge
753         if (stop->offset == 0) {
754             SPStop *next = stop->getNextStop();
755             if (next) {
756                 next->offset = 0;
757                 sp_repr_set_css_double(SP_OBJECT_REPR(next), "offset", 0);
758             }
759         } else if (stop->offset == 1) {
760             SPStop *prev = stop->getPrevStop();
761             if (prev) {
762                 prev->offset = 1;
763                 sp_repr_set_css_double(SP_OBJECT_REPR(prev), "offset", 1);
764             }
765         }
767         SP_OBJECT_REPR(gradient)->removeChild(SP_OBJECT_REPR(stop));
768         sp_gradient_vector_widget_load_gradient(vb, gradient);
769         update_stop_list(GTK_WIDGET(mnu), gradient, NULL);
770         DocumentUndo::done(SP_OBJECT_DOCUMENT(gradient), SP_VERB_CONTEXT_GRADIENT,
771                            _("Delete gradient stop"));
772     }
776 static GtkWidget * sp_gradient_vector_widget_new(SPGradient *gradient, SPStop *select_stop)
778     GtkWidget *vb, *w, *f, *csel;
780     g_return_val_if_fail(!gradient || SP_IS_GRADIENT(gradient), NULL);
782     vb = gtk_vbox_new(FALSE, PAD);
783     g_signal_connect(G_OBJECT(vb), "destroy", G_CALLBACK(sp_gradient_vector_widget_destroy), NULL);
785     w = sp_gradient_image_new(gradient);
786     g_object_set_data(G_OBJECT(vb), "preview", w);
787     gtk_widget_show(w);
788     gtk_box_pack_start(GTK_BOX(vb), w, TRUE, TRUE, PAD);
790     sp_repr_add_listener(SP_OBJECT_REPR(gradient), &grad_edit_dia_repr_events, vb);
791     GtkTooltips *tt = gtk_tooltips_new();
793     /* Stop list */
794     GtkWidget *mnu = gtk_option_menu_new();
795     /* Create new menu widget */
796     update_stop_list(GTK_WIDGET(mnu), gradient, NULL);
797     gtk_signal_connect(GTK_OBJECT(mnu), "changed", GTK_SIGNAL_FUNC(sp_grad_edit_select), vb);
798     gtk_widget_show(mnu);
799     gtk_object_set_data(GTK_OBJECT(vb), "stopmenu", mnu);
800     gtk_box_pack_start(GTK_BOX(vb), mnu, FALSE, FALSE, 0);
802     /* Add and Remove buttons */
803     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
804     // TRANSLATORS: "Stop" means: a "phase" of a gradient
805     GtkWidget *b = gtk_button_new_with_label(_("Add stop"));
806     gtk_widget_show(b);
807     gtk_container_add(GTK_CONTAINER(hb), b);
808     gtk_tooltips_set_tip(tt, b, _("Add another control stop to gradient"), NULL);
809     gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_grd_ed_add_stop), vb);
810     b = gtk_button_new_with_label(_("Delete stop"));
811     gtk_widget_show(b);
812     gtk_container_add(GTK_CONTAINER(hb), b);
813     gtk_tooltips_set_tip(tt, b, _("Delete current control stop from gradient"), NULL);
814     gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_grd_ed_del_stop), vb);
816     gtk_widget_show(hb);
817     gtk_box_pack_start(GTK_BOX(vb),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
820     /*  Offset Slider and stuff   */
821     hb = gtk_hbox_new(FALSE, 0);
823     /* Label */
824     GtkWidget *l = gtk_label_new(_("Offset:"));
825     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
826     gtk_box_pack_start(GTK_BOX(hb),l, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
827     gtk_widget_show(l);
829     /* Adjustment */
830     GtkAdjustment *Offset_adj = NULL;
831     Offset_adj= (GtkAdjustment *) gtk_adjustment_new(0.0, 0.0, 1.0, 0.01, 0.01, 0.0);
832     gtk_object_set_data(GTK_OBJECT(vb), "offset", Offset_adj);
833     GtkMenu *m = GTK_MENU(gtk_option_menu_get_menu(GTK_OPTION_MENU(mnu)));
834     SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(m)), "stop"));
835     gtk_adjustment_set_value(Offset_adj, stop->offset);
837     /* Slider */
838     GtkWidget *slider = gtk_hscale_new(Offset_adj);
839     gtk_scale_set_draw_value( GTK_SCALE(slider), FALSE );
840     gtk_widget_show(slider);
841     gtk_box_pack_start(GTK_BOX(hb),slider, TRUE, TRUE, AUX_BETWEEN_BUTTON_GROUPS);
842     gtk_object_set_data(GTK_OBJECT(vb), "offslide", slider);
844     /* Spinbutton */
845     GtkWidget *sbtn = gtk_spin_button_new(GTK_ADJUSTMENT(Offset_adj), 0.01, 2);
846     sp_dialog_defocus_on_enter(sbtn);
847     gtk_widget_show(sbtn);
848     gtk_box_pack_start(GTK_BOX(hb),sbtn, FALSE, TRUE, AUX_BETWEEN_BUTTON_GROUPS);
849     gtk_object_set_data(GTK_OBJECT(vb), "offspn", sbtn);
851     if (stop->offset>0 && stop->offset<1) {
852         gtk_widget_set_sensitive(slider, TRUE);
853         gtk_widget_set_sensitive(GTK_WIDGET(sbtn), TRUE);
854     } else {
855         gtk_widget_set_sensitive(slider, FALSE);
856         gtk_widget_set_sensitive(GTK_WIDGET(sbtn), FALSE);
857     }
860     /* Signals */
861     gtk_signal_connect(GTK_OBJECT(Offset_adj), "value_changed",
862                         GTK_SIGNAL_FUNC(offadjustmentChanged), vb);
864     // gtk_signal_connect(GTK_OBJECT(slider), "changed",  GTK_SIGNAL_FUNC(offsliderChanged), vb);
865     gtk_widget_show(hb);
866     gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, PAD);
868     // TRANSLATORS: "Stop" means: a "phase" of a gradient
869     f = gtk_frame_new(_("Stop Color"));
870     gtk_widget_show(f);
871     gtk_box_pack_start(GTK_BOX(vb), f, TRUE, TRUE, PAD);
872     csel = static_cast<GtkWidget*>(sp_color_selector_new(SP_TYPE_COLOR_NOTEBOOK));
873     g_object_set_data(G_OBJECT(vb), "cselector", csel);
874     gtk_widget_show(csel);
875     gtk_container_add(GTK_CONTAINER(f), csel);
876     g_signal_connect(G_OBJECT(csel), "dragged", G_CALLBACK(sp_gradient_vector_color_dragged), vb);
877     g_signal_connect(G_OBJECT(csel), "changed", G_CALLBACK(sp_gradient_vector_color_changed), vb);
879     gtk_widget_show(vb);
881     sp_gradient_vector_widget_load_gradient(vb, gradient);
883     if (select_stop) {
884         select_stop_in_list(GTK_WIDGET(mnu), gradient, select_stop);
885     }
887     return vb;
892 GtkWidget * sp_gradient_vector_editor_new(SPGradient *gradient, SPStop *stop)
894     GtkWidget *wid;
896     if (dlg == NULL) {
897         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
899         dlg = sp_window_new(_("Gradient editor"), TRUE);
900         if (x == -1000 || y == -1000) {
901             x = prefs->getInt(prefs_path + "x", -1000);
902             y = prefs->getInt(prefs_path + "y", -1000);
903         }
904         if (w ==0 || h == 0) {
905             w = prefs->getInt(prefs_path + "w", 0);
906             h = prefs->getInt(prefs_path + "h", 0);
907         }
909         if (x<0) {
910             x=0;
911         }
912         if (y<0) {
913             y=0;
914         }
916         if (x != 0 || y != 0) {
917             gtk_window_move(reinterpret_cast<GtkWindow *>(dlg), x, y);
918         } else {
919             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
920         }
921         if (w && h) {
922             gtk_window_resize(reinterpret_cast<GtkWindow *>(dlg), w, h);
923         }
924         sp_transientize(dlg);
925         wd.win = dlg;
926         wd.stop = 0;
927         g_signal_connect(G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_transientize_callback), &wd);
928         gtk_signal_connect(GTK_OBJECT(dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg);
929         gtk_signal_connect(GTK_OBJECT(dlg), "destroy", G_CALLBACK(sp_gradient_vector_dialog_destroy), dlg);
930         gtk_signal_connect(GTK_OBJECT(dlg), "delete_event", G_CALLBACK(sp_gradient_vector_dialog_delete), dlg);
931         g_signal_connect(G_OBJECT(INKSCAPE), "shut_down", G_CALLBACK(sp_gradient_vector_dialog_delete), dlg);
932         g_signal_connect( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(sp_dialog_hide), dlg );
933         g_signal_connect( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(sp_dialog_unhide), dlg );
935         gtk_container_set_border_width(GTK_CONTAINER(dlg), PAD);
937         wid = static_cast<GtkWidget*>(sp_gradient_vector_widget_new(gradient, stop));
938         g_object_set_data(G_OBJECT(dlg), "gradient-vector-widget", wid);
939         /* Connect signals */
940         gtk_widget_show(wid);
941         gtk_container_add(GTK_CONTAINER(dlg), wid);
942     } else {
943         // FIXME: temp fix for 0.38
944         // Simply load_gradient into the editor does not work for multi-stop gradients,
945         // as the stop list and other widgets are in a wrong state and crash readily.
946         // Instead we just delete the window (by sending the delete signal)
947         // and call sp_gradient_vector_editor_new again, so it creates the window anew.
949         GdkEventAny event;
950         GtkWidget *widget = static_cast<GtkWidget *>(dlg);
951         event.type = GDK_DELETE;
952         event.window = widget->window;
953         event.send_event = TRUE;
954         g_object_ref(G_OBJECT(event.window));
955         gtk_main_do_event(reinterpret_cast<GdkEvent*>(&event));
956         g_object_unref(G_OBJECT(event.window));
958         g_assert(dlg == NULL);
959         sp_gradient_vector_editor_new(gradient, stop);
960     }
962     return dlg;
965 static void sp_gradient_vector_widget_load_gradient(GtkWidget *widget, SPGradient *gradient)
967     blocked = TRUE;
969     SPGradient *old;
971     old = (SPGradient*)g_object_get_data(G_OBJECT(widget), "gradient");
973     if (old != gradient) {
974         sigc::connection *release_connection;
975         sigc::connection *modified_connection;
977         release_connection = (sigc::connection *)g_object_get_data(G_OBJECT(widget), "gradient_release_connection");
978         modified_connection = (sigc::connection *)g_object_get_data(G_OBJECT(widget), "gradient_modified_connection");
980         if (old) {
981             g_assert( release_connection != NULL );
982             g_assert( modified_connection != NULL );
983             release_connection->disconnect();
984             modified_connection->disconnect();
985             sp_signal_disconnect_by_data(old, widget);
986         }
988         if (gradient) {
989             if (!release_connection) {
990                 release_connection = new sigc::connection();
991             }
992             if (!modified_connection) {
993                 modified_connection = new sigc::connection();
994             }
995             *release_connection = gradient->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gradient_vector_gradient_release), widget));
996             *modified_connection = gradient->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_gradient_vector_gradient_modified), widget));
997         } else {
998             if (release_connection) {
999                 delete release_connection;
1000                 release_connection = NULL;
1001             }
1002             if (modified_connection) {
1003                 delete modified_connection;
1004                 modified_connection = NULL;
1005             }
1006         }
1008         g_object_set_data(G_OBJECT(widget), "gradient_release_connection", release_connection);
1009         g_object_set_data(G_OBJECT(widget), "gradient_modified_connection", modified_connection);
1010     }
1012     g_object_set_data(G_OBJECT(widget), "gradient", gradient);
1014     if (gradient) {
1015         gtk_widget_set_sensitive(widget, TRUE);
1017         gradient->ensureVector();
1019         GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(widget), "stopmenu"));
1020         SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop"));
1022         // get the color selector
1023         SPColorSelector *csel = SP_COLOR_SELECTOR(g_object_get_data(G_OBJECT(widget), "cselector"));
1025         csel->base->setColorAlpha( stop->getEffectiveColor(), stop->opacity );
1027         /* Fill preview */
1028         GtkWidget *w = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(widget), "preview"));
1029         sp_gradient_image_set_gradient(SP_GRADIENT_IMAGE(w), gradient);
1031         update_stop_list(GTK_WIDGET(mnu), gradient, NULL);
1033         // Once the user edits a gradient, it stops being auto-collectable
1034         if (SP_OBJECT_REPR(gradient)->attribute("inkscape:collect")) {
1035             SPDocument *document = SP_OBJECT_DOCUMENT(gradient);
1036             bool saved = DocumentUndo::getUndoSensitive(document);
1037             DocumentUndo::setUndoSensitive(document, false);
1038             SP_OBJECT_REPR(gradient)->setAttribute("inkscape:collect", NULL);
1039             DocumentUndo::setUndoSensitive(document, saved);
1040         }
1041     } else { // no gradient, disable everything
1042         gtk_widget_set_sensitive(widget, FALSE);
1043     }
1045     blocked = FALSE;
1048 static void sp_gradient_vector_dialog_destroy(GtkObject */*object*/, gpointer /*data*/)
1050     sp_signal_disconnect_by_data(INKSCAPE, dlg);
1051     wd.win = dlg = NULL;
1052     wd.stop = 0;
1055 static gboolean sp_gradient_vector_dialog_delete(GtkWidget */*widget*/, GdkEvent */*event*/, GtkWidget */*dialog*/)
1057     gtk_window_get_position(GTK_WINDOW(dlg), &x, &y);
1058     gtk_window_get_size(GTK_WINDOW(dlg), &w, &h);
1060     if (x<0) {
1061         x=0;
1062     }
1063     if (y<0) {
1064         y=0;
1065     }
1067     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1068     prefs->setInt(prefs_path + "x", x);
1069     prefs->setInt(prefs_path + "y", y);
1070     prefs->setInt(prefs_path + "w", w);
1071     prefs->setInt(prefs_path + "h", h);
1073     return FALSE; // which means, go ahead and destroy it
1076 /* Widget destroy handler */
1078 static void sp_gradient_vector_widget_destroy(GtkObject *object, gpointer /*data*/)
1080     GObject *gradient;
1082     gradient = (GObject*)g_object_get_data(G_OBJECT(object), "gradient");
1084     sigc::connection *release_connection = (sigc::connection *)g_object_get_data(G_OBJECT(object), "gradient_release_connection");
1085     sigc::connection *modified_connection = (sigc::connection *)g_object_get_data(G_OBJECT(object), "gradient_modified_connection");
1087     if (gradient) {
1088         g_assert( release_connection != NULL );
1089         g_assert( modified_connection != NULL );
1090         release_connection->disconnect();
1091         modified_connection->disconnect();
1092         sp_signal_disconnect_by_data(gradient, object);
1093     }
1095     if (gradient && SP_OBJECT_REPR(gradient)) {
1096         sp_repr_remove_listener_by_data(SP_OBJECT_REPR(gradient), object);
1097     }
1100 static void sp_gradient_vector_gradient_release(SPObject */*object*/, GtkWidget *widget)
1102     sp_gradient_vector_widget_load_gradient(widget, NULL);
1105 static void sp_gradient_vector_gradient_modified(SPObject *object, guint /*flags*/, GtkWidget *widget)
1107     SPGradient *gradient=SP_GRADIENT(object);
1108     if (!blocked) {
1109         blocked = TRUE;
1110         sp_gradient_vector_widget_load_gradient(widget, gradient);
1111         blocked = FALSE;
1112     }
1115 static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GtkObject *object)
1117     SPGradient *gradient, *ngr;
1119     if (blocked) {
1120         return;
1121     }
1123     gradient = static_cast<SPGradient*>(g_object_get_data(G_OBJECT(object), "gradient"));
1124     if (!gradient) {
1125         return;
1126     }
1128     blocked = TRUE;
1130     ngr = sp_gradient_ensure_vector_normalized(gradient);
1131     if (ngr != gradient) {
1132         /* Our master gradient has changed */
1133         sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr);
1134     }
1136     ngr->ensureVector();
1138     GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(object), "stopmenu"));
1139     SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop"));
1142     csel->base->getColorAlpha(stop->specified_color, stop->opacity);
1143     stop->currentColor = false;
1145     blocked = FALSE;
1148 static void sp_gradient_vector_color_changed(SPColorSelector *csel, GtkObject *object)
1150     if (blocked) {
1151         return;
1152     }
1154     SPGradient *gradient = static_cast<SPGradient*>(g_object_get_data(G_OBJECT(object), "gradient"));
1155     if (!gradient) {
1156         return;
1157     }
1159     blocked = TRUE;
1161     SPGradient *ngr = sp_gradient_ensure_vector_normalized(gradient);
1162     if (ngr != gradient) {
1163         /* Our master gradient has changed */
1164         sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr);
1165     }
1167     ngr->ensureVector();
1169     /* Set start parameters */
1170     /* We rely on normalized vector, i.e. stops HAVE to exist */
1171     g_return_if_fail(ngr->getFirstStop() != NULL);
1173     GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(object), "stopmenu"));
1174     SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop"));
1176     csel = static_cast<SPColorSelector*>(g_object_get_data(G_OBJECT(object), "cselector"));
1177     SPColor color;
1178     float alpha = 0;
1179     csel->base->getColorAlpha( color, alpha );
1181     sp_repr_set_css_double(SP_OBJECT_REPR(stop), "offset", stop->offset);
1182     Inkscape::CSSOStringStream os;
1183     os << "stop-color:" << color.toString() << ";stop-opacity:" << static_cast<gdouble>(alpha) <<";";
1184     SP_OBJECT_REPR(stop)->setAttribute("style", os.str().c_str());
1185     // g_snprintf(c, 256, "stop-color:#%06x;stop-opacity:%g;", rgb >> 8, static_cast<gdouble>(alpha));
1186     //SP_OBJECT_REPR(stop)->setAttribute("style", c);
1188     DocumentUndo::done(SP_OBJECT_DOCUMENT(ngr), SP_VERB_CONTEXT_GRADIENT,
1189                        _("Change gradient stop color"));
1191     blocked = FALSE;
1193     SPColorPreview *cpv = static_cast<SPColorPreview *>(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "preview"));
1194     sp_color_preview_set_rgba32(cpv, sp_stop_get_rgba32(stop));
1197 /*
1198   Local Variables:
1199   mode:c++
1200   c-file-style:"stroustrup"
1201   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1202   indent-tabs-mode:nil
1203   fill-column:99
1204   End:
1205 */
1206 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :