/* * Gradient vector selection widget * * Authors: * Lauris Kaplinski * bulia byak * MenTaLguY * Jon A. Cruz * Abhishek Sharma * * Copyright (C) 2001-2002 Lauris Kaplinski * Copyright (C) 2001 Ximian, Inc. * Copyright (C) 2004 Monash University * Copyright (C) 2004 David Turner * Copyright (C) 2006 MenTaLguY * Copyright (C) 2010 Jon A. Cruz * * Released under GNU GPL, read the file 'COPYING' for more information * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H #endif #include #include "macros.h" #include #include "../widgets/gradient-image.h" #include "../inkscape.h" #include "../document-private.h" #include "../gradient-chemistry.h" #include "gradient-vector.h" #include "../helper/window.h" #include "xml/repr.h" #include "../dialogs/dialog-events.h" #include "../preferences.h" #include "svg/css-ostringstream.h" #include "sp-stop.h" #include #include using Inkscape::DocumentUndo; enum { VECTOR_SET, LAST_SIGNAL }; static void sp_gradient_vector_selector_class_init(SPGradientVectorSelectorClass *klass); static void sp_gradient_vector_selector_init(SPGradientVectorSelector *gvs); static void sp_gradient_vector_selector_destroy(GtkObject *object); static void sp_gvs_gradient_release(SPObject *obj, SPGradientVectorSelector *gvs); static void sp_gvs_defs_release(SPObject *defs, SPGradientVectorSelector *gvs); static void sp_gvs_defs_modified(SPObject *defs, guint flags, SPGradientVectorSelector *gvs); static void sp_gvs_rebuild_gui_full(SPGradientVectorSelector *gvs); static void sp_gvs_gradient_activate(GtkMenuItem *mi, SPGradientVectorSelector *gvs); static GtkVBoxClass *parent_class; static guint signals[LAST_SIGNAL] = {0}; // TODO FIXME kill these globals!!! static GtkWidget *dlg = NULL; static win_data wd; static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs static Glib::ustring const prefs_path = "/dialogs/gradienteditor/"; GType sp_gradient_vector_selector_get_type(void) { static GType type = 0; if (!type) { static const GTypeInfo info = { sizeof(SPGradientVectorSelectorClass), NULL, /* base_init */ NULL, /* base_finalize */ reinterpret_cast(sp_gradient_vector_selector_class_init), NULL, /* class_finalize */ NULL, /* class_data */ sizeof(SPGradientVectorSelector), 0, /* n_preallocs */ reinterpret_cast(sp_gradient_vector_selector_init), 0, /* value_table */ }; type = g_type_register_static( GTK_TYPE_VBOX, "SPGradientVectorSelector", &info, static_cast< GTypeFlags >(0) ); } return type; } static void sp_gradient_vector_selector_class_init(SPGradientVectorSelectorClass *klass) { GtkObjectClass *object_class; object_class = GTK_OBJECT_CLASS(klass); parent_class = static_cast(gtk_type_class(GTK_TYPE_VBOX)); signals[VECTOR_SET] = gtk_signal_new( "vector_set", GTK_RUN_LAST, GTK_CLASS_TYPE(object_class), GTK_SIGNAL_OFFSET(SPGradientVectorSelectorClass, vector_set), gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); object_class->destroy = sp_gradient_vector_selector_destroy; } static void sp_gradient_vector_selector_init(SPGradientVectorSelector *gvs) { gvs->idlabel = TRUE; gvs->swatched = false; gvs->doc = NULL; gvs->gr = NULL; new (&gvs->gradient_release_connection) sigc::connection(); new (&gvs->defs_release_connection) sigc::connection(); new (&gvs->defs_modified_connection) sigc::connection(); gvs->menu = gtk_option_menu_new(); gtk_widget_show(gvs->menu); gtk_box_pack_start(GTK_BOX(gvs), gvs->menu, TRUE, TRUE, 0); } static void sp_gradient_vector_selector_destroy(GtkObject *object) { SPGradientVectorSelector *gvs = SP_GRADIENT_VECTOR_SELECTOR(object); if (gvs->gr) { gvs->gradient_release_connection.disconnect(); gvs->gr = NULL; } if (gvs->doc) { gvs->defs_release_connection.disconnect(); gvs->defs_modified_connection.disconnect(); gvs->doc = NULL; } gvs->gradient_release_connection.~connection(); gvs->defs_release_connection.~connection(); gvs->defs_modified_connection.~connection(); if ((reinterpret_cast(parent_class))->destroy) { (* (reinterpret_cast(parent_class))->destroy) (object); } } GtkWidget *sp_gradient_vector_selector_new(SPDocument *doc, SPGradient *gr) { GtkWidget *gvs; g_return_val_if_fail(!gr || SP_IS_GRADIENT(gr), NULL); g_return_val_if_fail(!gr || (gr->document == doc), NULL); gvs = static_cast(gtk_type_new(SP_TYPE_GRADIENT_VECTOR_SELECTOR)); if (doc) { sp_gradient_vector_selector_set_gradient(SP_GRADIENT_VECTOR_SELECTOR(gvs), doc, gr); } else { sp_gvs_rebuild_gui_full(SP_GRADIENT_VECTOR_SELECTOR(gvs)); } return gvs; } void sp_gradient_vector_selector_set_gradient(SPGradientVectorSelector *gvs, SPDocument *doc, SPGradient *gr) { // g_message("sp_gradient_vector_selector_set_gradient(%p, %p, %p) [%s] %d %d", gvs, doc, gr, // (gr ? gr->getId():"N/A"), // (gr ? gr->isSwatch() : -1), // (gr ? gr->isSolid() : -1)); static gboolean suppress = FALSE; g_return_if_fail(gvs != NULL); g_return_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs)); g_return_if_fail(!gr || (doc != NULL)); g_return_if_fail(!gr || SP_IS_GRADIENT(gr)); g_return_if_fail(!gr || (gr->document == doc)); g_return_if_fail(!gr || gr->hasStops()); if (doc != gvs->doc) { /* Disconnect signals */ if (gvs->gr) { gvs->gradient_release_connection.disconnect(); gvs->gr = NULL; } if (gvs->doc) { gvs->defs_release_connection.disconnect(); gvs->defs_modified_connection.disconnect(); gvs->doc = NULL; } // Connect signals if (doc) { gvs->defs_release_connection = doc->getDefs()->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gvs_defs_release), gvs)); gvs->defs_modified_connection = doc->getDefs()->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_gvs_defs_modified), gvs)); } if (gr) { gvs->gradient_release_connection = gr->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gvs_gradient_release), gvs)); } gvs->doc = doc; gvs->gr = gr; sp_gvs_rebuild_gui_full(gvs); if (!suppress) g_signal_emit(G_OBJECT(gvs), signals[VECTOR_SET], 0, gr); } else if (gr != gvs->gr) { // Harder case - keep document, rebuild menus and stuff // fixme: (Lauris) suppress = TRUE; sp_gradient_vector_selector_set_gradient(gvs, NULL, NULL); sp_gradient_vector_selector_set_gradient(gvs, doc, gr); suppress = FALSE; g_signal_emit(G_OBJECT(gvs), signals[VECTOR_SET], 0, gr); } /* The case of setting NULL -> NULL is not very interesting */ } SPDocument *sp_gradient_vector_selector_get_document(SPGradientVectorSelector *gvs) { g_return_val_if_fail(gvs != NULL, NULL); g_return_val_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs), NULL); return gvs->doc; } SPGradient *sp_gradient_vector_selector_get_gradient(SPGradientVectorSelector *gvs) { g_return_val_if_fail(gvs != NULL, NULL); g_return_val_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs), NULL); return gvs->gr; } static void sp_gvs_rebuild_gui_full(SPGradientVectorSelector *gvs) { /* Clear old menu, if there is any */ if (gtk_option_menu_get_menu(GTK_OPTION_MENU(gvs->menu))) { gtk_option_menu_remove_menu(GTK_OPTION_MENU(gvs->menu)); } /* Create new menu widget */ GtkWidget *m = gtk_menu_new(); gtk_widget_show(m); /* Pick up all gradients with vectors */ GSList *gl = NULL; if (gvs->gr) { const GSList *gradients = gvs->gr->document->getResourceList("gradient"); for (const GSList *curr = gradients; curr; curr = curr->next) { SPGradient* grad = SP_GRADIENT(curr->data); if ( grad->hasStops() && (grad->isSwatch() == gvs->swatched) ) { gl = g_slist_prepend(gl, curr->data); } } } gl = g_slist_reverse(gl); gint pos = 0; gint idx = 0; if (!gvs->doc) { GtkWidget *i; i = gtk_menu_item_new_with_label(_("No document selected")); gtk_widget_show(i); gtk_menu_append(GTK_MENU(m), i); gtk_widget_set_sensitive(gvs->menu, FALSE); } else if (!gl) { GtkWidget *i; i = gtk_menu_item_new_with_label(_("No gradients in document")); gtk_widget_show(i); gtk_menu_append(GTK_MENU(m), i); gtk_widget_set_sensitive(gvs->menu, FALSE); } else if (!gvs->gr) { GtkWidget *i; i = gtk_menu_item_new_with_label(_("No gradient selected")); gtk_widget_show(i); gtk_menu_append(GTK_MENU(m), i); gtk_widget_set_sensitive(gvs->menu, FALSE); } else { while (gl) { SPGradient *gr; GtkWidget *i, *w; gr = SP_GRADIENT(gl->data); gl = g_slist_remove(gl, gr); /* We have to know: */ /* Gradient destroy */ /* Gradient name change */ i = gtk_menu_item_new(); gtk_widget_show(i); g_object_set_data(G_OBJECT(i), "gradient", gr); g_signal_connect(G_OBJECT(i), "activate", G_CALLBACK(sp_gvs_gradient_activate), gvs); w = sp_gradient_image_new(gr); gtk_widget_show(w); if (gvs->idlabel) { GtkWidget *hb, *l; hb = gtk_hbox_new(FALSE, 4); gtk_widget_show(hb); l = gtk_label_new(gr->getId()); gtk_widget_show(l); gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5); gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hb), w, FALSE, FALSE, 0); w = hb; } gtk_container_add(GTK_CONTAINER(i), w); gtk_menu_append(GTK_MENU(m), i); if (gr == gvs->gr) { pos = idx; } idx += 1; } gtk_widget_set_sensitive(gvs->menu, TRUE); } gtk_option_menu_set_menu(GTK_OPTION_MENU(gvs->menu), m); /* Set history */ gtk_option_menu_set_history(GTK_OPTION_MENU(gvs->menu), pos); } static void sp_gvs_gradient_activate(GtkMenuItem *mi, SPGradientVectorSelector *gvs) { SPGradient *gr, *norm; gr = (SPGradient*)g_object_get_data(G_OBJECT(mi), "gradient"); /* Hmmm... bad things may happen here, if actual gradient is something new */ /* Namely - menuitems etc. will be fucked up */ /* Hmmm - probably we can just re-set it as menuitem data (Lauris) */ //g_print("SPGradientVectorSelector: gradient %s activated\n", gr->getId()); //g_message("Setting to gradient %p swatch:%d solid:%d", gr, gr->isSwatch(), gr->isSolid()); norm = sp_gradient_ensure_vector_normalized(gr); if (norm != gr) { //g_print("SPGradientVectorSelector: become %s after normalization\n", norm->getId()); /* But be careful that we do not have gradient saved anywhere else */ g_object_set_data(G_OBJECT(mi), "gradient", norm); } /* fixme: Really we would want to use _set_vector */ /* Detach old */ if (gvs->gr) { gvs->gradient_release_connection.disconnect(); gvs->gr = NULL; } /* Attach new */ if (norm) { gvs->gradient_release_connection = norm->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gvs_gradient_release), gvs)); gvs->gr = norm; } g_signal_emit(G_OBJECT(gvs), signals[VECTOR_SET], 0, norm); if (norm != gr) { /* We do extra undo push here */ /* If handler has already done it, it is just NOP */ // FIXME: looks like this is never a valid undo step, consider removing this DocumentUndo::done(norm->document, SP_VERB_CONTEXT_GRADIENT, /* TODO: annotate */ "gradient-vector.cpp:350"); } } static void sp_gvs_gradient_release(SPObject */*obj*/, SPGradientVectorSelector *gvs) { /* Disconnect gradient */ if (gvs->gr) { gvs->gradient_release_connection.disconnect(); gvs->gr = NULL; } /* Rebuild GUI */ sp_gvs_rebuild_gui_full(gvs); } static void sp_gvs_defs_release(SPObject */*defs*/, SPGradientVectorSelector *gvs) { gvs->doc = NULL; gvs->defs_release_connection.disconnect(); gvs->defs_modified_connection.disconnect(); /* Disconnect gradient as well */ if (gvs->gr) { gvs->gradient_release_connection.disconnect(); gvs->gr = NULL; } /* Rebuild GUI */ sp_gvs_rebuild_gui_full(gvs); } static void sp_gvs_defs_modified(SPObject */*defs*/, guint /*flags*/, SPGradientVectorSelector *gvs) { /* fixme: We probably have to check some flags here (Lauris) */ sp_gvs_rebuild_gui_full(gvs); } void SPGradientVectorSelector::setSwatched() { swatched = true; sp_gvs_rebuild_gui_full(this); } /*################################################################## ### Vector Editing Widget ##################################################################*/ #include "../widgets/sp-color-notebook.h" #include "../widgets/sp-color-preview.h" #include "../widgets/widget-sizes.h" #include "../xml/node-event-vector.h" #include "../svg/svg-color.h" #define PAD 4 static GtkWidget *sp_gradient_vector_widget_new(SPGradient *gradient, SPStop *stop); static void sp_gradient_vector_widget_load_gradient(GtkWidget *widget, SPGradient *gradient); static gint sp_gradient_vector_dialog_delete(GtkWidget *widget, GdkEvent *event, GtkWidget *dialog); static void sp_gradient_vector_dialog_destroy(GtkObject *object, gpointer data); static void sp_gradient_vector_widget_destroy(GtkObject *object, gpointer data); static void sp_gradient_vector_gradient_release(SPObject *obj, GtkWidget *widget); static void sp_gradient_vector_gradient_modified(SPObject *obj, guint flags, GtkWidget *widget); static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GtkObject *object); static void sp_gradient_vector_color_changed(SPColorSelector *csel, GtkObject *object); static void update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop); static gboolean blocked = FALSE; static void grad_edit_dia_stop_added_or_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node */*child*/, Inkscape::XML::Node */*ref*/, gpointer data) { GtkWidget *vb = GTK_WIDGET(data); GtkWidget *mnu = (GtkWidget *)g_object_get_data(G_OBJECT(vb), "stopmenu"); SPGradient *gradient = (SPGradient *)g_object_get_data(G_OBJECT(vb), "gradient"); update_stop_list(mnu, gradient, NULL); } //FIXME!!! We must also listen to attr changes on all children (i.e. stops) too, //otherwise the dialog does not reflect undoing color or offset change. This is a major //hassle, unless we have a "one of the descendants changed in some way" signal. static Inkscape::XML::NodeEventVector grad_edit_dia_repr_events = { grad_edit_dia_stop_added_or_removed, /* child_added */ grad_edit_dia_stop_added_or_removed, /* child_removed */ NULL, /* attr_changed*/ NULL, /* content_changed */ NULL /* order_changed */ }; static void verify_grad(SPGradient *gradient) { int i = 0; SPStop *stop = NULL; /* count stops */ for ( SPObject *ochild = gradient->firstChild() ; ochild ; ochild = ochild->getNext() ) { if (SP_IS_STOP(ochild)) { i++; stop = SP_STOP(ochild); } } Inkscape::XML::Document *xml_doc; xml_doc = gradient->getRepr()->document(); if (i < 1) { Inkscape::CSSOStringStream os; os << "stop-color: #000000;stop-opacity:" << 1.0 << ";"; Inkscape::XML::Node *child; child = xml_doc->createElement("svg:stop"); sp_repr_set_css_double(child, "offset", 0.0); child->setAttribute("style", os.str().c_str()); gradient->getRepr()->addChild(child, NULL); Inkscape::GC::release(child); child = xml_doc->createElement("svg:stop"); sp_repr_set_css_double(child, "offset", 1.0); child->setAttribute("style", os.str().c_str()); gradient->getRepr()->addChild(child, NULL); Inkscape::GC::release(child); } if (i < 2) { sp_repr_set_css_double(stop->getRepr(), "offset", 0.0); Inkscape::XML::Node *child = stop->getRepr()->duplicate(gradient->getRepr()->document()); sp_repr_set_css_double(child, "offset", 1.0); gradient->getRepr()->addChild(child, stop->getRepr()); Inkscape::GC::release(child); } } static void select_stop_in_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop) { int i = 0; for ( SPObject *ochild = gradient->firstChild() ; ochild ; ochild = ochild->getNext() ) { if (SP_IS_STOP(ochild)) { if (ochild == new_stop) { gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), i); break; } i++; } } } static void update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop) { if (!SP_IS_GRADIENT(gradient)) { return; } blocked = TRUE; /* Clear old menu, if there is any */ if (gtk_option_menu_get_menu(GTK_OPTION_MENU(mnu))) { gtk_option_menu_remove_menu(GTK_OPTION_MENU(mnu)); } /* Create new menu widget */ GtkWidget *m = gtk_menu_new(); gtk_widget_show(m); GSList *sl = NULL; if ( gradient->hasStops() ) { for ( SPObject *ochild = gradient->firstChild() ; ochild ; ochild = ochild->getNext() ) { if (SP_IS_STOP(ochild)) { sl = g_slist_append(sl, ochild); } } } if (!sl) { GtkWidget *i = gtk_menu_item_new_with_label(_("No stops in gradient")); gtk_widget_show(i); gtk_menu_append(GTK_MENU(m), i); gtk_widget_set_sensitive(mnu, FALSE); } else { for (; sl != NULL; sl = sl->next){ if (SP_IS_STOP(sl->data)){ SPStop *stop = SP_STOP(sl->data); GtkWidget *i = gtk_menu_item_new(); gtk_widget_show(i); g_object_set_data(G_OBJECT(i), "stop", stop); GtkWidget *hb = gtk_hbox_new(FALSE, 4); GtkWidget *cpv = sp_color_preview_new(sp_stop_get_rgba32(stop)); gtk_widget_show(cpv); gtk_container_add( GTK_CONTAINER(hb), cpv ); g_object_set_data( G_OBJECT(i), "preview", cpv ); Inkscape::XML::Node *repr = reinterpret_cast(sl->data)->getRepr(); GtkWidget *l = gtk_label_new(repr->attribute("id")); gtk_widget_show(l); gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5); gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0); gtk_widget_show(hb); gtk_container_add(GTK_CONTAINER(i), hb); gtk_menu_append(GTK_MENU(m), i); } } gtk_widget_set_sensitive(mnu, TRUE); } gtk_option_menu_set_menu(GTK_OPTION_MENU(mnu), m); /* Set history */ if (new_stop == NULL) { gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0); } else { select_stop_in_list(mnu, gradient, new_stop); } blocked = FALSE; } // user selected existing stop from list static void sp_grad_edit_select(GtkOptionMenu *mnu, GtkWidget *tbl) { GObject *item = G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))); SPStop *stop = SP_STOP(g_object_get_data(item, "stop")); if (!stop) { return; } blocked = TRUE; SPColorSelector *csel = (SPColorSelector*)g_object_get_data(G_OBJECT(tbl), "cselector"); // set its color, from the stored array csel->base->setColorAlpha( stop->getEffectiveColor(), stop->opacity ); GtkWidget *offspin = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offspn")); GtkWidget *offslide =GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offslide")); GtkAdjustment *adj = static_cast(gtk_object_get_data(GTK_OBJECT(tbl), "offset")); bool isEndStop = false; SPStop *prev = NULL; prev = stop->getPrevStop(); if (prev != NULL ) { adj->lower = prev->offset; } else { isEndStop = true; adj->lower = 0; } SPStop *next = NULL; next = stop->getNextStop(); if (next != NULL ) { adj->upper = next->offset; } else { isEndStop = true; adj->upper = 1.0; } //fixme: does this work on all possible input gradients? if (!isEndStop) { gtk_widget_set_sensitive(offslide, TRUE); gtk_widget_set_sensitive(GTK_WIDGET(offspin), TRUE); } else { gtk_widget_set_sensitive(offslide, FALSE); gtk_widget_set_sensitive(GTK_WIDGET(offspin), FALSE); } gtk_adjustment_set_value(adj, stop->offset); gtk_adjustment_changed(adj); blocked = FALSE; } static void offadjustmentChanged( GtkAdjustment *adjustment, GtkWidget *vb) { if (!blocked) { blocked = TRUE; GtkOptionMenu *mnu = static_cast(g_object_get_data(G_OBJECT(vb), "stopmenu")); if ( g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop") ) { SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); stop->offset = adjustment->value; sp_repr_set_css_double(stop->getRepr(), "offset", stop->offset); DocumentUndo::maybeDone(stop->document, "gradient:stop:offset", SP_VERB_CONTEXT_GRADIENT, _("Change gradient stop offset")); blocked = FALSE; } } } guint32 sp_average_color(guint32 c1, guint32 c2, gdouble p = 0.5) { guint32 r = (guint32) (SP_RGBA32_R_U(c1) * p + SP_RGBA32_R_U(c2) * (1 - p)); guint32 g = (guint32) (SP_RGBA32_G_U(c1) * p + SP_RGBA32_G_U(c2) * (1 - p)); guint32 b = (guint32) (SP_RGBA32_B_U(c1) * p + SP_RGBA32_B_U(c2) * (1 - p)); guint32 a = (guint32) (SP_RGBA32_A_U(c1) * p + SP_RGBA32_A_U(c2) * (1 - p)); return SP_RGBA32_U_COMPOSE(r, g, b, a); } static void sp_grd_ed_add_stop(GtkWidget */*widget*/, GtkWidget *vb) { SPGradient *gradient = static_cast(g_object_get_data(G_OBJECT(vb), "gradient")); verify_grad(gradient); GtkOptionMenu *mnu = static_cast(g_object_get_data(G_OBJECT(vb), "stopmenu")); SPStop *stop = static_cast(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); if (stop == NULL) { return; } Inkscape::XML::Node *new_stop_repr = NULL; SPStop *next = stop->getNextStop(); if (next == NULL) { SPStop *prev = stop->getPrevStop(); if (prev != NULL) { next = stop; stop = prev; } } if (next != NULL) { new_stop_repr = stop->getRepr()->duplicate(gradient->getRepr()->document()); gradient->getRepr()->addChild(new_stop_repr, stop->getRepr()); } else { next = stop; new_stop_repr = stop->getPrevStop()->getRepr()->duplicate(gradient->getRepr()->document()); gradient->getRepr()->addChild(new_stop_repr, stop->getPrevStop()->getRepr()); } SPStop *newstop = reinterpret_cast(gradient->document->getObjectByRepr(new_stop_repr)); newstop->offset = (stop->offset + next->offset) * 0.5 ; guint32 const c1 = sp_stop_get_rgba32(stop); guint32 const c2 = sp_stop_get_rgba32(next); guint32 cnew = sp_average_color(c1, c2); Inkscape::CSSOStringStream os; gchar c[64]; sp_svg_write_color(c, sizeof(c), cnew); gdouble opacity = static_cast(SP_RGBA32_A_F(cnew)); os << "stop-color:" << c << ";stop-opacity:" << opacity <<";"; newstop->getRepr()->setAttribute("style", os.str().c_str()); sp_repr_set_css_double( newstop->getRepr(), "offset", (double)newstop->offset); sp_gradient_vector_widget_load_gradient(vb, gradient); Inkscape::GC::release(new_stop_repr); update_stop_list(GTK_WIDGET(mnu), gradient, newstop); GtkWidget *offspin = GTK_WIDGET(g_object_get_data(G_OBJECT(vb), "offspn")); GtkWidget *offslide =GTK_WIDGET(g_object_get_data(G_OBJECT(vb), "offslide")); gtk_widget_set_sensitive(offslide, TRUE); gtk_widget_set_sensitive(GTK_WIDGET(offspin), TRUE); DocumentUndo::done(gradient->document, SP_VERB_CONTEXT_GRADIENT, _("Add gradient stop")); } static void sp_grd_ed_del_stop(GtkWidget */*widget*/, GtkWidget *vb) { SPGradient *gradient = static_cast(g_object_get_data(G_OBJECT(vb), "gradient")); GtkOptionMenu *mnu = static_cast(g_object_get_data(G_OBJECT(vb), "stopmenu")); if (!g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")) return; SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); if (gradient->vector.stops.size() > 2) { // 2 is the minimum // if we delete first or last stop, move the next/previous to the edge if (stop->offset == 0) { SPStop *next = stop->getNextStop(); if (next) { next->offset = 0; sp_repr_set_css_double(next->getRepr(), "offset", 0); } } else if (stop->offset == 1) { SPStop *prev = stop->getPrevStop(); if (prev) { prev->offset = 1; sp_repr_set_css_double(prev->getRepr(), "offset", 1); } } gradient->getRepr()->removeChild(stop->getRepr()); sp_gradient_vector_widget_load_gradient(vb, gradient); update_stop_list(GTK_WIDGET(mnu), gradient, NULL); DocumentUndo::done(gradient->document, SP_VERB_CONTEXT_GRADIENT, _("Delete gradient stop")); } } static GtkWidget * sp_gradient_vector_widget_new(SPGradient *gradient, SPStop *select_stop) { GtkWidget *vb, *w, *f, *csel; g_return_val_if_fail(!gradient || SP_IS_GRADIENT(gradient), NULL); vb = gtk_vbox_new(FALSE, PAD); g_signal_connect(G_OBJECT(vb), "destroy", G_CALLBACK(sp_gradient_vector_widget_destroy), NULL); w = sp_gradient_image_new(gradient); g_object_set_data(G_OBJECT(vb), "preview", w); gtk_widget_show(w); gtk_box_pack_start(GTK_BOX(vb), w, TRUE, TRUE, PAD); sp_repr_add_listener(gradient->getRepr(), &grad_edit_dia_repr_events, vb); GtkTooltips *tt = gtk_tooltips_new(); /* Stop list */ GtkWidget *mnu = gtk_option_menu_new(); /* Create new menu widget */ update_stop_list(GTK_WIDGET(mnu), gradient, NULL); gtk_signal_connect(GTK_OBJECT(mnu), "changed", GTK_SIGNAL_FUNC(sp_grad_edit_select), vb); gtk_widget_show(mnu); gtk_object_set_data(GTK_OBJECT(vb), "stopmenu", mnu); gtk_box_pack_start(GTK_BOX(vb), mnu, FALSE, FALSE, 0); /* Add and Remove buttons */ GtkWidget *hb = gtk_hbox_new(FALSE, 1); // TRANSLATORS: "Stop" means: a "phase" of a gradient GtkWidget *b = gtk_button_new_with_label(_("Add stop")); gtk_widget_show(b); gtk_container_add(GTK_CONTAINER(hb), b); gtk_tooltips_set_tip(tt, b, _("Add another control stop to gradient"), NULL); gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_grd_ed_add_stop), vb); b = gtk_button_new_with_label(_("Delete stop")); gtk_widget_show(b); gtk_container_add(GTK_CONTAINER(hb), b); gtk_tooltips_set_tip(tt, b, _("Delete current control stop from gradient"), NULL); gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_grd_ed_del_stop), vb); gtk_widget_show(hb); gtk_box_pack_start(GTK_BOX(vb),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); /* Offset Slider and stuff */ hb = gtk_hbox_new(FALSE, 0); /* Label */ GtkWidget *l = gtk_label_new(_("Offset:")); gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5); gtk_box_pack_start(GTK_BOX(hb),l, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); gtk_widget_show(l); /* Adjustment */ GtkAdjustment *Offset_adj = NULL; Offset_adj= (GtkAdjustment *) gtk_adjustment_new(0.0, 0.0, 1.0, 0.01, 0.01, 0.0); gtk_object_set_data(GTK_OBJECT(vb), "offset", Offset_adj); GtkMenu *m = GTK_MENU(gtk_option_menu_get_menu(GTK_OPTION_MENU(mnu))); SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(m)), "stop")); gtk_adjustment_set_value(Offset_adj, stop->offset); /* Slider */ GtkWidget *slider = gtk_hscale_new(Offset_adj); gtk_scale_set_draw_value( GTK_SCALE(slider), FALSE ); gtk_widget_show(slider); gtk_box_pack_start(GTK_BOX(hb),slider, TRUE, TRUE, AUX_BETWEEN_BUTTON_GROUPS); gtk_object_set_data(GTK_OBJECT(vb), "offslide", slider); /* Spinbutton */ GtkWidget *sbtn = gtk_spin_button_new(GTK_ADJUSTMENT(Offset_adj), 0.01, 2); sp_dialog_defocus_on_enter(sbtn); gtk_widget_show(sbtn); gtk_box_pack_start(GTK_BOX(hb),sbtn, FALSE, TRUE, AUX_BETWEEN_BUTTON_GROUPS); gtk_object_set_data(GTK_OBJECT(vb), "offspn", sbtn); if (stop->offset>0 && stop->offset<1) { gtk_widget_set_sensitive(slider, TRUE); gtk_widget_set_sensitive(GTK_WIDGET(sbtn), TRUE); } else { gtk_widget_set_sensitive(slider, FALSE); gtk_widget_set_sensitive(GTK_WIDGET(sbtn), FALSE); } /* Signals */ gtk_signal_connect(GTK_OBJECT(Offset_adj), "value_changed", GTK_SIGNAL_FUNC(offadjustmentChanged), vb); // gtk_signal_connect(GTK_OBJECT(slider), "changed", GTK_SIGNAL_FUNC(offsliderChanged), vb); gtk_widget_show(hb); gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, PAD); // TRANSLATORS: "Stop" means: a "phase" of a gradient f = gtk_frame_new(_("Stop Color")); gtk_widget_show(f); gtk_box_pack_start(GTK_BOX(vb), f, TRUE, TRUE, PAD); csel = static_cast(sp_color_selector_new(SP_TYPE_COLOR_NOTEBOOK)); g_object_set_data(G_OBJECT(vb), "cselector", csel); gtk_widget_show(csel); gtk_container_add(GTK_CONTAINER(f), csel); g_signal_connect(G_OBJECT(csel), "dragged", G_CALLBACK(sp_gradient_vector_color_dragged), vb); g_signal_connect(G_OBJECT(csel), "changed", G_CALLBACK(sp_gradient_vector_color_changed), vb); gtk_widget_show(vb); sp_gradient_vector_widget_load_gradient(vb, gradient); if (select_stop) { select_stop_in_list(GTK_WIDGET(mnu), gradient, select_stop); } return vb; } GtkWidget * sp_gradient_vector_editor_new(SPGradient *gradient, SPStop *stop) { GtkWidget *wid; if (dlg == NULL) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); dlg = sp_window_new(_("Gradient editor"), TRUE); if (x == -1000 || y == -1000) { x = prefs->getInt(prefs_path + "x", -1000); y = prefs->getInt(prefs_path + "y", -1000); } if (w ==0 || h == 0) { w = prefs->getInt(prefs_path + "w", 0); h = prefs->getInt(prefs_path + "h", 0); } if (x<0) { x=0; } if (y<0) { y=0; } if (x != 0 || y != 0) { gtk_window_move(reinterpret_cast(dlg), x, y); } else { gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); } if (w && h) { gtk_window_resize(reinterpret_cast(dlg), w, h); } sp_transientize(dlg); wd.win = dlg; wd.stop = 0; g_signal_connect(G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_transientize_callback), &wd); gtk_signal_connect(GTK_OBJECT(dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg); gtk_signal_connect(GTK_OBJECT(dlg), "destroy", G_CALLBACK(sp_gradient_vector_dialog_destroy), dlg); gtk_signal_connect(GTK_OBJECT(dlg), "delete_event", G_CALLBACK(sp_gradient_vector_dialog_delete), dlg); g_signal_connect(G_OBJECT(INKSCAPE), "shut_down", G_CALLBACK(sp_gradient_vector_dialog_delete), dlg); g_signal_connect( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(sp_dialog_hide), dlg ); g_signal_connect( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(sp_dialog_unhide), dlg ); gtk_container_set_border_width(GTK_CONTAINER(dlg), PAD); wid = static_cast(sp_gradient_vector_widget_new(gradient, stop)); g_object_set_data(G_OBJECT(dlg), "gradient-vector-widget", wid); /* Connect signals */ gtk_widget_show(wid); gtk_container_add(GTK_CONTAINER(dlg), wid); } else { // FIXME: temp fix for 0.38 // Simply load_gradient into the editor does not work for multi-stop gradients, // as the stop list and other widgets are in a wrong state and crash readily. // Instead we just delete the window (by sending the delete signal) // and call sp_gradient_vector_editor_new again, so it creates the window anew. GdkEventAny event; GtkWidget *widget = static_cast(dlg); event.type = GDK_DELETE; event.window = widget->window; event.send_event = TRUE; g_object_ref(G_OBJECT(event.window)); gtk_main_do_event(reinterpret_cast(&event)); g_object_unref(G_OBJECT(event.window)); g_assert(dlg == NULL); sp_gradient_vector_editor_new(gradient, stop); } return dlg; } static void sp_gradient_vector_widget_load_gradient(GtkWidget *widget, SPGradient *gradient) { blocked = TRUE; SPGradient *old; old = (SPGradient*)g_object_get_data(G_OBJECT(widget), "gradient"); if (old != gradient) { sigc::connection *release_connection; sigc::connection *modified_connection; release_connection = (sigc::connection *)g_object_get_data(G_OBJECT(widget), "gradient_release_connection"); modified_connection = (sigc::connection *)g_object_get_data(G_OBJECT(widget), "gradient_modified_connection"); if (old) { g_assert( release_connection != NULL ); g_assert( modified_connection != NULL ); release_connection->disconnect(); modified_connection->disconnect(); sp_signal_disconnect_by_data(old, widget); } if (gradient) { if (!release_connection) { release_connection = new sigc::connection(); } if (!modified_connection) { modified_connection = new sigc::connection(); } *release_connection = gradient->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gradient_vector_gradient_release), widget)); *modified_connection = gradient->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_gradient_vector_gradient_modified), widget)); } else { if (release_connection) { delete release_connection; release_connection = NULL; } if (modified_connection) { delete modified_connection; modified_connection = NULL; } } g_object_set_data(G_OBJECT(widget), "gradient_release_connection", release_connection); g_object_set_data(G_OBJECT(widget), "gradient_modified_connection", modified_connection); } g_object_set_data(G_OBJECT(widget), "gradient", gradient); if (gradient) { gtk_widget_set_sensitive(widget, TRUE); gradient->ensureVector(); GtkOptionMenu *mnu = static_cast(g_object_get_data(G_OBJECT(widget), "stopmenu")); SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); // get the color selector SPColorSelector *csel = SP_COLOR_SELECTOR(g_object_get_data(G_OBJECT(widget), "cselector")); csel->base->setColorAlpha( stop->getEffectiveColor(), stop->opacity ); /* Fill preview */ GtkWidget *w = static_cast(g_object_get_data(G_OBJECT(widget), "preview")); sp_gradient_image_set_gradient(SP_GRADIENT_IMAGE(w), gradient); update_stop_list(GTK_WIDGET(mnu), gradient, NULL); // Once the user edits a gradient, it stops being auto-collectable if (gradient->getRepr()->attribute("inkscape:collect")) { SPDocument *document = gradient->document; bool saved = DocumentUndo::getUndoSensitive(document); DocumentUndo::setUndoSensitive(document, false); gradient->getRepr()->setAttribute("inkscape:collect", NULL); DocumentUndo::setUndoSensitive(document, saved); } } else { // no gradient, disable everything gtk_widget_set_sensitive(widget, FALSE); } blocked = FALSE; } static void sp_gradient_vector_dialog_destroy(GtkObject */*object*/, gpointer /*data*/) { sp_signal_disconnect_by_data(INKSCAPE, dlg); wd.win = dlg = NULL; wd.stop = 0; } static gboolean sp_gradient_vector_dialog_delete(GtkWidget */*widget*/, GdkEvent */*event*/, GtkWidget */*dialog*/) { gtk_window_get_position(GTK_WINDOW(dlg), &x, &y); gtk_window_get_size(GTK_WINDOW(dlg), &w, &h); if (x<0) { x=0; } if (y<0) { y=0; } Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setInt(prefs_path + "x", x); prefs->setInt(prefs_path + "y", y); prefs->setInt(prefs_path + "w", w); prefs->setInt(prefs_path + "h", h); return FALSE; // which means, go ahead and destroy it } /* Widget destroy handler */ static void sp_gradient_vector_widget_destroy(GtkObject *object, gpointer /*data*/) { SPObject *gradient = reinterpret_cast(g_object_get_data(G_OBJECT(object), "gradient")); sigc::connection *release_connection = (sigc::connection *)g_object_get_data(G_OBJECT(object), "gradient_release_connection"); sigc::connection *modified_connection = (sigc::connection *)g_object_get_data(G_OBJECT(object), "gradient_modified_connection"); if (gradient) { g_assert( release_connection != NULL ); g_assert( modified_connection != NULL ); release_connection->disconnect(); modified_connection->disconnect(); sp_signal_disconnect_by_data(gradient, object); } if (gradient && gradient->getRepr()) { sp_repr_remove_listener_by_data(gradient->getRepr(), object); } } static void sp_gradient_vector_gradient_release(SPObject */*object*/, GtkWidget *widget) { sp_gradient_vector_widget_load_gradient(widget, NULL); } static void sp_gradient_vector_gradient_modified(SPObject *object, guint /*flags*/, GtkWidget *widget) { SPGradient *gradient=SP_GRADIENT(object); if (!blocked) { blocked = TRUE; sp_gradient_vector_widget_load_gradient(widget, gradient); blocked = FALSE; } } static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GtkObject *object) { SPGradient *gradient, *ngr; if (blocked) { return; } gradient = static_cast(g_object_get_data(G_OBJECT(object), "gradient")); if (!gradient) { return; } blocked = TRUE; ngr = sp_gradient_ensure_vector_normalized(gradient); if (ngr != gradient) { /* Our master gradient has changed */ sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr); } ngr->ensureVector(); GtkOptionMenu *mnu = static_cast(g_object_get_data(G_OBJECT(object), "stopmenu")); SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); csel->base->getColorAlpha(stop->specified_color, stop->opacity); stop->currentColor = false; blocked = FALSE; } static void sp_gradient_vector_color_changed(SPColorSelector *csel, GtkObject *object) { if (blocked) { return; } SPGradient *gradient = static_cast(g_object_get_data(G_OBJECT(object), "gradient")); if (!gradient) { return; } blocked = TRUE; SPGradient *ngr = sp_gradient_ensure_vector_normalized(gradient); if (ngr != gradient) { /* Our master gradient has changed */ sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr); } ngr->ensureVector(); /* Set start parameters */ /* We rely on normalized vector, i.e. stops HAVE to exist */ g_return_if_fail(ngr->getFirstStop() != NULL); GtkOptionMenu *mnu = static_cast(g_object_get_data(G_OBJECT(object), "stopmenu")); SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); csel = static_cast(g_object_get_data(G_OBJECT(object), "cselector")); SPColor color; float alpha = 0; csel->base->getColorAlpha( color, alpha ); sp_repr_set_css_double(stop->getRepr(), "offset", stop->offset); Inkscape::CSSOStringStream os; os << "stop-color:" << color.toString() << ";stop-opacity:" << static_cast(alpha) <<";"; stop->getRepr()->setAttribute("style", os.str().c_str()); // g_snprintf(c, 256, "stop-color:#%06x;stop-opacity:%g;", rgb >> 8, static_cast(alpha)); //stop->getRepr()->setAttribute("style", c); DocumentUndo::done(ngr->document, SP_VERB_CONTEXT_GRADIENT, _("Change gradient stop color")); blocked = FALSE; SPColorPreview *cpv = static_cast(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "preview")); sp_color_preview_set_rgba32(cpv, sp_stop_get_rgba32(stop)); } /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :