// SPDX-License-Identifier: GPL-2.0-or-later /** * @file * Spray 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 * Jabiertxo Arraiza * * Copyright (C) 2004 David Turner * Copyright (C) 2003 MenTaLguY * Copyright (C) 1999-2015 authors * Copyright (C) 2001-2002 Ximian, Inc. * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include "spray-toolbar.h" #include #include #include #include "desktop.h" #include "ui/icon-names.h" #include "ui/simple-pref-pusher.h" #include "ui/dialog/clonetiler.h" #include "ui/dialog/dialog-container.h" #include "ui/dialog/dialog-base.h" #include "ui/widget/canvas.h" #include "ui/widget/spin-button-tool-item.h" // Disabled in 0.91 because of Bug #1274831 (crash, spraying an object // with the mode: spray object in single path) // Please enable again when working on 1.0 #define ENABLE_SPRAY_MODE_SINGLE_PATH Inkscape::UI::Dialog::CloneTiler *get_clone_tiler_panel(SPDesktop *desktop) { Inkscape::UI::Dialog::DialogBase *dialog = desktop->getContainer()->get_dialog("CloneTiler"); if (!dialog) { desktop->getContainer()->new_dialog("CloneTiler"); return dynamic_cast( desktop->getContainer()->get_dialog("CloneTiler")); } return dynamic_cast(dialog); } namespace Inkscape { namespace UI { namespace Toolbar { SprayToolbar::SprayToolbar(SPDesktop *desktop) : Toolbar(desktop) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); /* Mode */ { add_label(_("Mode:")); Gtk::RadioToolButton::Group mode_group; auto copy_mode_btn = Gtk::manage(new Gtk::RadioToolButton(mode_group, _("Spray with copies"))); copy_mode_btn->set_tooltip_text(_("Spray copies of the initial selection")); copy_mode_btn->set_icon_name(INKSCAPE_ICON("spray-mode-copy")); _mode_buttons.push_back(copy_mode_btn); auto clone_mode_btn = Gtk::manage(new Gtk::RadioToolButton(mode_group, _("Spray with clones"))); clone_mode_btn->set_tooltip_text(_("Spray clones of the initial selection")); clone_mode_btn->set_icon_name(INKSCAPE_ICON("spray-mode-clone")); _mode_buttons.push_back(clone_mode_btn); #ifdef ENABLE_SPRAY_MODE_SINGLE_PATH auto union_mode_btn = Gtk::manage(new Gtk::RadioToolButton(mode_group, _("Spray single path"))); union_mode_btn->set_tooltip_text(_("Spray objects in a single path")); union_mode_btn->set_icon_name(INKSCAPE_ICON("spray-mode-union")); _mode_buttons.push_back(union_mode_btn); #endif auto eraser_mode_btn = Gtk::manage(new Gtk::RadioToolButton(mode_group, _("Delete sprayed items"))); eraser_mode_btn->set_tooltip_text(_("Delete sprayed items from selection")); eraser_mode_btn->set_icon_name(INKSCAPE_ICON("draw-eraser")); _mode_buttons.push_back(eraser_mode_btn); int btn_idx = 0; for (auto btn : _mode_buttons) { btn->set_sensitive(true); add(*btn); btn->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &SprayToolbar::mode_changed), btn_idx++)); } } add(* Gtk::manage(new Gtk::SeparatorToolItem())); { /* Width */ std::vector labels = {_("(narrow spray)"), "", "", "", _("(default)"), "", "", "", "", _("(broad spray)")}; std::vector values = { 1, 3, 5, 10, 15, 20, 30, 50, 75, 100}; auto width_val = prefs->getDouble("/tools/spray/width", 15); _width_adj = Gtk::Adjustment::create(width_val, 1, 100, 1.0, 10.0); auto width_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("spray-width", _("Width:"), _width_adj, 1, 0)); width_item->set_tooltip_text(_("The width of the spray area (relative to the visible canvas area)")); width_item->set_custom_numeric_menu_data(values, labels); width_item->set_focus_widget(desktop->canvas); _width_adj->signal_value_changed().connect(sigc::mem_fun(*this, &SprayToolbar::width_value_changed)); add(*width_item); width_item->set_sensitive(true); } /* Use Pressure Width button */ { auto pressure_item = add_toggle_button(_("Pressure"), _("Use the pressure of the input device to alter the width of spray area")); pressure_item->set_icon_name(INKSCAPE_ICON("draw-use-pressure")); _usepressurewidth_pusher.reset(new UI::SimplePrefPusher(pressure_item, "/tools/spray/usepressurewidth")); pressure_item->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &SprayToolbar::on_pref_toggled), pressure_item, "/tools/spray/usepressurewidth")); } { /* Population */ std::vector labels = {_("(low population)"), "", "", "", _("(default)"), "", _("(high population)")}; std::vector values = { 5, 20, 35, 50, 70, 85, 100}; auto population_val = prefs->getDouble("/tools/spray/population", 70); _population_adj = Gtk::Adjustment::create(population_val, 1, 100, 1.0, 10.0); _spray_population = Gtk::manage(new UI::Widget::SpinButtonToolItem("spray-population", _("Amount:"), _population_adj, 1, 0)); _spray_population->set_tooltip_text(_("Adjusts the number of items sprayed per click")); _spray_population->set_custom_numeric_menu_data(values, labels); _spray_population->set_focus_widget(desktop->canvas); _population_adj->signal_value_changed().connect(sigc::mem_fun(*this, &SprayToolbar::population_value_changed)); add(*_spray_population); _spray_population->set_sensitive(true); } /* Use Pressure Population button */ { auto pressure_population_item = add_toggle_button(_("Pressure"), _("Use the pressure of the input device to alter the amount of sprayed objects")); pressure_population_item->set_icon_name(INKSCAPE_ICON("draw-use-pressure")); _usepressurepopulation_pusher.reset(new UI::SimplePrefPusher(pressure_population_item, "/tools/spray/usepressurepopulation")); pressure_population_item->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &SprayToolbar::on_pref_toggled), pressure_population_item, "/tools/spray/usepressurepopulation")); } add(* Gtk::manage(new Gtk::SeparatorToolItem())); { /* Rotation */ std::vector labels = {_("(default)"), "", "", "", "", "", "", _("(high rotation variation)")}; std::vector values = { 0, 10, 25, 35, 50, 60, 80, 100}; auto rotation_val = prefs->getDouble("/tools/spray/rotation_variation", 0); _rotation_adj = Gtk::Adjustment::create(rotation_val, 0, 100, 1.0, 10.0); _spray_rotation = Gtk::manage(new UI::Widget::SpinButtonToolItem("spray-rotation", _("Rotation:"), _rotation_adj, 1, 0)); // xgettext:no-c-format _spray_rotation->set_tooltip_text(_("Variation of the rotation of the sprayed objects; 0% for the same rotation than the original object")); _spray_rotation->set_custom_numeric_menu_data(values, labels); _spray_rotation->set_focus_widget(desktop->canvas); _rotation_adj->signal_value_changed().connect(sigc::mem_fun(*this, &SprayToolbar::rotation_value_changed)); add(*_spray_rotation); _spray_rotation->set_sensitive(); } { /* Scale */ std::vector labels = {_("(default)"), "", "", "", "", "", "", _("(high scale variation)")}; std::vector values = { 0, 10, 25, 35, 50, 60, 80, 100}; auto scale_val = prefs->getDouble("/tools/spray/scale_variation", 0); _scale_adj = Gtk::Adjustment::create(scale_val, 0, 100, 1.0, 10.0); _spray_scale = Gtk::manage(new UI::Widget::SpinButtonToolItem("spray-scale", C_("Spray tool", "Scale:"), _scale_adj, 1, 0)); // xgettext:no-c-format _spray_scale->set_tooltip_text(_("Variation in the scale of the sprayed objects; 0% for the same scale than the original object")); _spray_scale->set_custom_numeric_menu_data(values, labels); _spray_scale->set_focus_widget(desktop->canvas); _scale_adj->signal_value_changed().connect(sigc::mem_fun(*this, &SprayToolbar::scale_value_changed)); add(*_spray_scale); _spray_scale->set_sensitive(true); } /* Use Pressure Scale button */ { _usepressurescale = add_toggle_button(_("Pressure"), _("Use the pressure of the input device to alter the scale of new items")); _usepressurescale->set_icon_name(INKSCAPE_ICON("draw-use-pressure")); _usepressurescale->set_active(prefs->getBool("/tools/spray/usepressurescale", false)); _usepressurescale->signal_toggled().connect(sigc::mem_fun(*this, &SprayToolbar::toggle_pressure_scale)); } add(* Gtk::manage(new Gtk::SeparatorToolItem())); { /* Standard_deviation */ std::vector labels = {_("(minimum scatter)"), "", "", "", "", "", _("(default)"), _("(maximum scatter)")}; std::vector values = { 1, 5, 10, 20, 30, 50, 70, 100}; auto sd_val = prefs->getDouble("/tools/spray/standard_deviation", 70); _sd_adj = Gtk::Adjustment::create(sd_val, 1, 100, 1.0, 10.0); auto sd_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("spray-standard-deviation", C_("Spray tool", "Scatter:"), _sd_adj, 1, 0)); sd_item->set_tooltip_text(_("Increase to scatter sprayed objects")); sd_item->set_custom_numeric_menu_data(values, labels); sd_item->set_focus_widget(desktop->canvas); _sd_adj->signal_value_changed().connect(sigc::mem_fun(*this, &SprayToolbar::standard_deviation_value_changed)); add(*sd_item); sd_item->set_sensitive(true); } { /* Mean */ std::vector labels = {_("(default)"), "", "", "", "", "", "", _("(maximum mean)")}; std::vector values = { 0, 5, 10, 20, 30, 50, 70, 100}; auto mean_val = prefs->getDouble("/tools/spray/mean", 0); _mean_adj = Gtk::Adjustment::create(mean_val, 0, 100, 1.0, 10.0); auto mean_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("spray-mean", _("Focus:"), _mean_adj, 1, 0)); mean_item->set_tooltip_text(_("0 to spray a spot; increase to enlarge the ring radius")); mean_item->set_custom_numeric_menu_data(values, labels); mean_item->set_focus_widget(desktop->canvas); _mean_adj->signal_value_changed().connect(sigc::mem_fun(*this, &SprayToolbar::mean_value_changed)); add(*mean_item); mean_item->set_sensitive(true); } add(* Gtk::manage(new Gtk::SeparatorToolItem())); /* Over No Transparent */ { _over_no_transparent = add_toggle_button(_("Apply over no transparent areas"), _("Apply over no transparent areas")); _over_no_transparent->set_icon_name(INKSCAPE_ICON("object-visible")); _over_no_transparent->set_active(prefs->getBool("/tools/spray/over_no_transparent", true)); _over_no_transparent->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &SprayToolbar::on_pref_toggled), _over_no_transparent, "/tools/spray/over_no_transparent")); } /* Over Transparent */ { _over_transparent = add_toggle_button(_("Apply over transparent areas"), _("Apply over transparent areas")); _over_transparent->set_icon_name(INKSCAPE_ICON("object-hidden")); _over_transparent->set_active(prefs->getBool("/tools/spray/over_transparent", true)); _over_transparent->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &SprayToolbar::on_pref_toggled), _over_transparent, "/tools/spray/over_transparent")); } /* Pick No Overlap */ { _pick_no_overlap = add_toggle_button(_("No overlap between colors"), _("No overlap between colors")); _pick_no_overlap->set_icon_name(INKSCAPE_ICON("symbol-bigger")); _pick_no_overlap->set_active(prefs->getBool("/tools/spray/pick_no_overlap", false)); _pick_no_overlap->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &SprayToolbar::on_pref_toggled), _pick_no_overlap, "/tools/spray/pick_no_overlap")); } /* Overlap */ { _no_overlap = add_toggle_button(_("Prevent overlapping objects"), _("Prevent overlapping objects")); _no_overlap->set_icon_name(INKSCAPE_ICON("distribute-randomize")); _no_overlap->set_active(prefs->getBool("/tools/spray/no_overlap", false)); _no_overlap->signal_toggled().connect(sigc::mem_fun(*this, &SprayToolbar::toggle_no_overlap)); } /* Offset */ { std::vector labels = {_("(minimum offset)"), "", "", "", _("(default)"), "", "", _("(maximum offset)")}; std::vector values = { 0, 25, 50, 75, 100, 150, 200, 1000}; auto offset_val = prefs->getDouble("/tools/spray/offset", 100); _offset_adj = Gtk::Adjustment::create(offset_val, 0, 1000, 1, 4); _offset = Gtk::manage(new UI::Widget::SpinButtonToolItem("spray-offset", _("Offset %:"), _offset_adj, 0, 0)); _offset->set_tooltip_text(_("Increase to segregate objects more (value in percent)")); _offset->set_custom_numeric_menu_data(values, labels); _offset->set_focus_widget(desktop->canvas); _offset_adj->signal_value_changed().connect(sigc::mem_fun(*this, &SprayToolbar::offset_value_changed)); add(*_offset); } add(* Gtk::manage(new Gtk::SeparatorToolItem())); /* Picker */ { _picker = add_toggle_button(_("Pick color from the drawing. You can use clonetiler trace dialog for advanced effects. In clone mode original fill or stroke colors must be unset."), _("Pick color from the drawing. You can use clonetiler trace dialog for advanced effects. In clone mode original fill or stroke colors must be unset.")); _picker->set_icon_name(INKSCAPE_ICON("color-picker")); _picker->set_active(prefs->getBool("/tools/spray/picker", false)); _picker->signal_toggled().connect(sigc::mem_fun(*this, &SprayToolbar::toggle_picker)); } /* Pick Fill */ { _pick_fill = add_toggle_button(_("Apply picked color to fill"), _("Apply picked color to fill")); _pick_fill->set_icon_name(INKSCAPE_ICON("paint-solid")); _pick_fill->set_active(prefs->getBool("/tools/spray/pick_fill", false)); _pick_fill->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &SprayToolbar::on_pref_toggled), _pick_fill, "/tools/spray/pick_fill")); } /* Pick Stroke */ { _pick_stroke = add_toggle_button(_("Apply picked color to stroke"), _("Apply picked color to stroke")); _pick_stroke->set_icon_name(INKSCAPE_ICON("no-marker")); _pick_stroke->set_active(prefs->getBool("/tools/spray/pick_stroke", false)); _pick_stroke->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &SprayToolbar::on_pref_toggled), _pick_stroke, "/tools/spray/pick_stroke")); } /* Inverse Value Size */ { _pick_inverse_value = add_toggle_button(_("Inverted pick value, retaining color in advanced trace mode"), _("Inverted pick value, retaining color in advanced trace mode")); _pick_inverse_value->set_icon_name(INKSCAPE_ICON("object-tweak-shrink")); _pick_inverse_value->set_active(prefs->getBool("/tools/spray/pick_inverse_value", false)); _pick_inverse_value->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &SprayToolbar::on_pref_toggled), _pick_inverse_value, "/tools/spray/pick_inverse_value")); } /* Pick from center */ { _pick_center = add_toggle_button(_("Pick from center instead of average area."), _("Pick from center instead of average area.")); _pick_center->set_icon_name(INKSCAPE_ICON("snap-bounding-box-center")); _pick_center->set_active(prefs->getBool("/tools/spray/pick_center", true)); _pick_center->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &SprayToolbar::on_pref_toggled), _pick_center, "/tools/spray/pick_center")); } gint mode = prefs->getInt("/tools/spray/mode", 1); _mode_buttons[mode]->set_active(); show_all(); init(); } GtkWidget * SprayToolbar::create(SPDesktop *desktop) { auto toolbar = new SprayToolbar(desktop); return GTK_WIDGET(toolbar->gobj()); } void SprayToolbar::width_value_changed() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setDouble( "/tools/spray/width", _width_adj->get_value()); } void SprayToolbar::mean_value_changed() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setDouble( "/tools/spray/mean", _mean_adj->get_value()); } void SprayToolbar::standard_deviation_value_changed() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setDouble( "/tools/spray/standard_deviation", _sd_adj->get_value()); } void SprayToolbar::mode_changed(int mode) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setInt("/tools/spray/mode", mode); init(); } void SprayToolbar::init(){ Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int mode = prefs->getInt("/tools/spray/mode", 0); bool show = true; if(mode == 3 || mode == 2){ show = false; } _no_overlap->set_visible(show); _over_no_transparent->set_visible(show); _over_transparent->set_visible(show); _pick_no_overlap->set_visible(show); _pick_stroke->set_visible(show); _pick_fill->set_visible(show); _pick_inverse_value->set_visible(show); _pick_center->set_visible(show); _picker->set_visible(show); _offset->set_visible(show); _pick_fill->set_visible(show); _pick_stroke->set_visible(show); _pick_inverse_value->set_visible(show); _pick_center->set_visible(show); if(mode == 2){ show = true; } _spray_rotation->set_visible(show); update_widgets(); } void SprayToolbar::population_value_changed() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setDouble( "/tools/spray/population", _population_adj->get_value()); } void SprayToolbar::rotation_value_changed() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setDouble( "/tools/spray/rotation_variation", _rotation_adj->get_value()); } void SprayToolbar::update_widgets() { _offset_adj->set_value(100.0); bool no_overlap_is_active = _no_overlap->get_active() && _no_overlap->get_visible(); _offset->set_visible(no_overlap_is_active); if (_usepressurescale->get_active()) { _scale_adj->set_value(0.0); _spray_scale->set_sensitive(false); } else { _spray_scale->set_sensitive(true); } bool picker_is_active = _picker->get_active() && _picker->get_visible(); _pick_fill->set_visible(picker_is_active); _pick_stroke->set_visible(picker_is_active); _pick_inverse_value->set_visible(picker_is_active); _pick_center->set_visible(picker_is_active); } void SprayToolbar::toggle_no_overlap() { auto prefs = Inkscape::Preferences::get(); bool active = _no_overlap->get_active(); prefs->setBool("/tools/spray/no_overlap", active); update_widgets(); } void SprayToolbar::scale_value_changed() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setDouble( "/tools/spray/scale_variation", _scale_adj->get_value()); } void SprayToolbar::offset_value_changed() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setDouble( "/tools/spray/offset", _offset_adj->get_value()); } void SprayToolbar::toggle_pressure_scale() { auto prefs = Inkscape::Preferences::get(); bool active = _usepressurescale->get_active(); prefs->setBool("/tools/spray/usepressurescale", active); if(active){ prefs->setDouble("/tools/spray/scale_variation", 0); } update_widgets(); } void SprayToolbar::toggle_picker() { auto prefs = Inkscape::Preferences::get(); bool active = _picker->get_active(); prefs->setBool("/tools/spray/picker", active); if(active){ prefs->setBool("/dialogs/clonetiler/dotrace", false); SPDesktop *dt = _desktop; if (Inkscape::UI::Dialog::CloneTiler *ct = get_clone_tiler_panel(dt)){ dt->getContainer()->new_dialog("CloneTiler"); ct->show_page_trace(); } } update_widgets(); } void SprayToolbar::on_pref_toggled(Gtk::ToggleToolButton *btn, const Glib::ustring& path) { auto prefs = Inkscape::Preferences::get(); bool active = btn->get_active(); prefs->setBool(path, active); } void SprayToolbar::set_mode(int mode) { _mode_buttons[mode]->set_active(); } } } } /* 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 :