/** * @file * Star aux toolbar */ /* Authors: * MenTaLguY * Lauris Kaplinski * bulia byak * Frank Felfe * John Cliff * David Turner * Josh Andler * Jon A. Cruz * Maximilian Albert * Tavmjong Bah * Abhishek Sharma * Kris De Gussem * * Copyright (C) 2004 David Turner * Copyright (C) 2003 MenTaLguY * Copyright (C) 1999-2011 authors * Copyright (C) 2001-2002 Ximian, Inc. * * Released under GNU GPL, read the file 'COPYING' for more information */ #ifdef HAVE_CONFIG_H #include #endif #include #include "star-toolbar.h" #include "desktop.h" #include "document-undo.h" #include "widgets/ege-adjustment-action.h" #include "widgets/ege-output-action.h" #include "widgets/ege-select-one-action.h" #include "ink-action.h" #include "ink-radio-action.h" #include "selection.h" #include "sp-star.h" #include "toolbox.h" #include "ui/icon-names.h" #include "ui/tools/star-tool.h" #include "ui/uxmanager.h" #include "verbs.h" #include "xml/node-event-vector.h" using Inkscape::UI::UXManager; using Inkscape::DocumentUndo; using Inkscape::UI::ToolboxFactory; using Inkscape::UI::PrefPusher; //######################## //## Star ## //######################## static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge ) { SPDesktop *desktop = static_cast(g_object_get_data( dataKludge, "desktop" )); if (DocumentUndo::getUndoSensitive(desktop->getDocument())) { // do not remember prefs if this call is initiated by an undo change, because undoing object // creation sets bogus values to its attributes before it is deleted Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setInt("/tools/shapes/star/magnitude", (gint)gtk_adjustment_get_value(adj)); } // quit if run by the attr_changed listener if (g_object_get_data( dataKludge, "freeze" )) { return; } // in turn, prevent listener from responding g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) ); bool modmade = false; Inkscape::Selection *selection = desktop->getSelection(); auto itemlist= selection->items(); for(auto i=itemlist.begin();i!=itemlist.end();++i){ SPItem *item = *i; if (SP_IS_STAR(item)) { Inkscape::XML::Node *repr = item->getRepr(); sp_repr_set_int(repr,"sodipodi:sides", (gint)gtk_adjustment_get_value(adj)); double arg1 = 0.5; sp_repr_get_double(repr, "sodipodi:arg1", &arg1); sp_repr_set_svg_double(repr, "sodipodi:arg2", (arg1 + M_PI / (gint)gtk_adjustment_get_value(adj))); item->updateRepr(); modmade = true; } } if (modmade) { DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_STAR, _("Star: Change number of corners")); } g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) ); } static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge ) { SPDesktop *desktop = static_cast(g_object_get_data( dataKludge, "desktop" )); if (DocumentUndo::getUndoSensitive(desktop->getDocument())) { if (!IS_NAN(gtk_adjustment_get_value(adj))) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setDouble("/tools/shapes/star/proportion", gtk_adjustment_get_value(adj)); } } // quit if run by the attr_changed listener if (g_object_get_data( dataKludge, "freeze" )) { return; } // in turn, prevent listener from responding g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) ); bool modmade = false; Inkscape::Selection *selection = desktop->getSelection(); auto itemlist= selection->items(); for(auto i=itemlist.begin();i!=itemlist.end();++i){ SPItem *item = *i; if (SP_IS_STAR(item)) { Inkscape::XML::Node *repr = item->getRepr(); gdouble r1 = 1.0; gdouble r2 = 1.0; sp_repr_get_double(repr, "sodipodi:r1", &r1); sp_repr_get_double(repr, "sodipodi:r2", &r2); if (r2 < r1) { sp_repr_set_svg_double(repr, "sodipodi:r2", r1*gtk_adjustment_get_value(adj)); } else { sp_repr_set_svg_double(repr, "sodipodi:r1", r2*gtk_adjustment_get_value(adj)); } item->updateRepr(); modmade = true; } } if (modmade) { DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_STAR, _("Star: Change spoke ratio")); } g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) ); } static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge ) { SPDesktop *desktop = static_cast(g_object_get_data( dataKludge, "desktop" )); bool flat = ege_select_one_action_get_active( act ) == 0; if (DocumentUndo::getUndoSensitive(desktop->getDocument())) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setBool( "/tools/shapes/star/isflatsided", flat); } // quit if run by the attr_changed listener if (g_object_get_data( dataKludge, "freeze" )) { return; } // in turn, prevent listener from responding g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) ); Inkscape::Selection *selection = desktop->getSelection(); GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) ); bool modmade = false; if ( prop_action ) { gtk_action_set_visible( prop_action, !flat ); } auto itemlist= selection->items(); for(auto i=itemlist.begin();i!=itemlist.end();++i){ SPItem *item = *i; if (SP_IS_STAR(item)) { Inkscape::XML::Node *repr = item->getRepr(); repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" ); item->updateRepr(); modmade = true; } } if (modmade) { DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_STAR, flat ? _("Make polygon") : _("Make star")); } g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) ); } static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge ) { SPDesktop *desktop = static_cast(g_object_get_data( dataKludge, "desktop" )); if (DocumentUndo::getUndoSensitive(desktop->getDocument())) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setDouble("/tools/shapes/star/rounded", (gdouble) gtk_adjustment_get_value(adj)); } // quit if run by the attr_changed listener if (g_object_get_data( dataKludge, "freeze" )) { return; } // in turn, prevent listener from responding g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) ); bool modmade = false; Inkscape::Selection *selection = desktop->getSelection(); auto itemlist= selection->items(); for(auto i=itemlist.begin();i!=itemlist.end();++i){ SPItem *item = *i; if (SP_IS_STAR(item)) { Inkscape::XML::Node *repr = item->getRepr(); sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) gtk_adjustment_get_value(adj)); item->updateRepr(); modmade = true; } } if (modmade) { DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_STAR, _("Star: Change rounding")); } g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) ); } static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge ) { SPDesktop *desktop = static_cast(g_object_get_data( dataKludge, "desktop" )); if (DocumentUndo::getUndoSensitive(desktop->getDocument())) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setDouble("/tools/shapes/star/randomized", (gdouble) gtk_adjustment_get_value(adj)); } // quit if run by the attr_changed listener if (g_object_get_data( dataKludge, "freeze" )) { return; } // in turn, prevent listener from responding g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) ); bool modmade = false; Inkscape::Selection *selection = desktop->getSelection(); auto itemlist= selection->items(); for(auto i=itemlist.begin();i!=itemlist.end();++i){ SPItem *item = *i; if (SP_IS_STAR(item)) { Inkscape::XML::Node *repr = item->getRepr(); sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) gtk_adjustment_get_value(adj)); item->updateRepr(); modmade = true; } } if (modmade) { DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_STAR, _("Star: Change randomization")); } g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) ); } static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name, gchar const * /*old_value*/, gchar const * /*new_value*/, bool /*is_interactive*/, gpointer data) { GtkWidget *tbl = GTK_WIDGET(data); // quit if run by the _changed callbacks if (g_object_get_data(G_OBJECT(tbl), "freeze")) { return; } // in turn, prevent callbacks from responding g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE)); GtkAdjustment *adj = 0; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true); if (!strcmp(name, "inkscape:randomized")) { adj = GTK_ADJUSTMENT( g_object_get_data(G_OBJECT(tbl), "randomized") ); double randomized = 0.0; sp_repr_get_double(repr, "inkscape:randomized", &randomized); gtk_adjustment_set_value(adj, randomized); } else if (!strcmp(name, "inkscape:rounded")) { adj = GTK_ADJUSTMENT( g_object_get_data(G_OBJECT(tbl), "rounded") ); double rounded = 0.0; sp_repr_get_double(repr, "inkscape:rounded", &rounded); gtk_adjustment_set_value(adj, rounded); } else if (!strcmp(name, "inkscape:flatsided")) { GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") ); char const *flatsides = repr->attribute("inkscape:flatsided"); EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) ); if ( flatsides && !strcmp(flatsides,"false") ) { ege_select_one_action_set_active( flat_action, 1 ); gtk_action_set_visible( prop_action, TRUE ); } else { ege_select_one_action_set_active( flat_action, 0 ); gtk_action_set_visible( prop_action, FALSE ); } } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) { adj = GTK_ADJUSTMENT(g_object_get_data(G_OBJECT(tbl), "proportion")); gdouble r1 = 1.0; gdouble r2 = 1.0; sp_repr_get_double(repr, "sodipodi:r1", &r1); sp_repr_get_double(repr, "sodipodi:r2", &r2); if (r2 < r1) { gtk_adjustment_set_value(adj, r2/r1); } else { gtk_adjustment_set_value(adj, r1/r2); } } else if (!strcmp(name, "sodipodi:sides")) { adj = GTK_ADJUSTMENT(g_object_get_data(G_OBJECT(tbl), "magnitude")); int sides = 0; sp_repr_get_int(repr, "sodipodi:sides", &sides); gtk_adjustment_set_value(adj, sides); } g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE)); } static Inkscape::XML::NodeEventVector star_tb_repr_events = { NULL, /* child_added */ NULL, /* child_removed */ star_tb_event_attr_changed, NULL, /* content_changed */ NULL /* order_changed */ }; /** * \param selection Should not be NULL. */ static void sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl) { int n_selected = 0; Inkscape::XML::Node *repr = NULL; purge_repr_listener( tbl, tbl ); auto itemlist= selection->items(); for(auto i=itemlist.begin();i!=itemlist.end();++i){ SPItem *item = *i; if (SP_IS_STAR(item)) { n_selected++; repr = item->getRepr(); } } EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) ); if (n_selected == 0) { g_object_set( G_OBJECT(act), "label", _("New:"), NULL ); } else if (n_selected == 1) { g_object_set( G_OBJECT(act), "label", _("Change:"), NULL ); if (repr) { g_object_set_data( tbl, "repr", repr ); Inkscape::GC::anchor(repr); sp_repr_add_listener(repr, &star_tb_repr_events, tbl); sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl); } } else { // FIXME: implement averaging of all parameters for multiple selected stars //gtk_label_set_markup(GTK_LABEL(l), _("Average:")); //gtk_label_set_markup(GTK_LABEL(l), _("Change:")); } } static void sp_stb_defaults( GtkWidget * /*widget*/, GObject *dataKludge ) { // FIXME: in this and all other _default functions, set some flag telling the value_changed // callbacks to lump all the changes for all selected objects in one undo step GtkAdjustment *adj = 0; // fixme: make settable in prefs! gint mag = 5; gdouble prop = 0.5; gboolean flat = FALSE; gdouble randomized = 0; gdouble rounded = 0; EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) ); ege_select_one_action_set_active( flat_action, flat ? 0 : 1 ); GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) ); gtk_action_set_visible( sb2, !flat ); adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) ); gtk_adjustment_set_value(adj, mag); gtk_adjustment_value_changed(adj); adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) ); gtk_adjustment_set_value(adj, prop); gtk_adjustment_value_changed(adj); adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) ); gtk_adjustment_set_value(adj, rounded); gtk_adjustment_value_changed(adj); adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) ); gtk_adjustment_set_value(adj, randomized); gtk_adjustment_value_changed(adj); } static void star_toolbox_watch_ec(SPDesktop* dt, Inkscape::UI::Tools::ToolBase* ec, GObject* holder); void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) { GtkIconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1); { EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("New:"), "", 0 ); ege_output_action_set_use_markup( act, TRUE ); gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); g_object_set_data( holder, "mode_action", act ); } { EgeAdjustmentAction* eact = 0; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true); /* Flatsided checkbox */ { GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING ); GtkTreeIter iter; gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, 0, _("Polygon"), 1, _("Regular polygon (with one handle) instead of a star"), 2, INKSCAPE_ICON("draw-polygon"), -1 ); gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, 0, _("Star"), 1, _("Star instead of a regular polygon (with one handle)"), 2, INKSCAPE_ICON("draw-star"), -1 ); EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) ); gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); g_object_set_data( holder, "flat_action", act ); ege_select_one_action_set_appearance( act, "full" ); ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE ); g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL ); ege_select_one_action_set_icon_column( act, 2 ); ege_select_one_action_set_icon_size( act, secondarySize ); ege_select_one_action_set_tooltip_column( act, 1 ); ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 ); g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder); } /* Magnitude */ { gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0}; gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20}; eact = create_adjustment_action( "MagnitudeAction", _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"), "/tools/shapes/star/magnitude", 3, GTK_WIDGET(desktop->canvas), holder, FALSE, NULL, 3, 1024, 1, 5, labels, values, G_N_ELEMENTS(labels), sp_stb_magnitude_value_changed, NULL /*unit tracker*/, 1.0, 0 ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); } /* Spoke ratio */ { gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")}; gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1}; eact = create_adjustment_action( "SpokeAction", _("Spoke ratio"), _("Spoke ratio:"), // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle. // Base radius is the same for the closest handle. _("Base radius to tip radius ratio"), "/tools/shapes/star/proportion", 0.5, GTK_WIDGET(desktop->canvas), holder, FALSE, NULL, 0.01, 1.0, 0.01, 0.1, labels, values, G_N_ELEMENTS(labels), sp_stb_proportion_value_changed ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); g_object_set_data( holder, "prop_action", eact ); } if ( !isFlatSided ) { gtk_action_set_visible( GTK_ACTION(eact), TRUE ); } else { gtk_action_set_visible( GTK_ACTION(eact), FALSE ); } /* Roundedness */ { gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")}; gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10}; eact = create_adjustment_action( "RoundednessAction", _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"), "/tools/shapes/star/rounded", 0.0, GTK_WIDGET(desktop->canvas), holder, FALSE, NULL, -10.0, 10.0, 0.01, 0.1, labels, values, G_N_ELEMENTS(labels), sp_stb_rounded_value_changed ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); } /* Randomization */ { gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")}; gdouble values[] = {0, 0.01, 0.1, 0.5, 10}; eact = create_adjustment_action( "RandomizationAction", _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"), "/tools/shapes/star/randomized", 0.0, GTK_WIDGET(desktop->canvas), holder, FALSE, NULL, -10.0, 10.0, 0.001, 0.01, labels, values, G_N_ELEMENTS(labels), sp_stb_randomized_value_changed, NULL /*unit tracker*/, 0.1, 3 ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); } } { /* Reset */ { InkAction* inky = ink_action_new( "StarResetAction", _("Defaults"), _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"), INKSCAPE_ICON("edit-clear"), GTK_ICON_SIZE_SMALL_TOOLBAR); g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_stb_defaults), holder ); gtk_action_group_add_action( mainActions, GTK_ACTION(inky) ); gtk_action_set_sensitive( GTK_ACTION(inky), TRUE ); } } desktop->connectEventContextChanged(sigc::bind(sigc::ptr_fun(star_toolbox_watch_ec), holder)); g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder); } static void star_toolbox_watch_ec(SPDesktop* desktop, Inkscape::UI::Tools::ToolBase* ec, GObject* holder) { static sigc::connection changed; if (dynamic_cast(ec) != NULL) { changed = desktop->getSelection()->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), holder)); sp_star_toolbox_selection_changed(desktop->getSelection(), holder); } else { if (changed) changed.disconnect(); } } /* 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 :