diff options
Diffstat (limited to 'src/ui/toolbar/arc-toolbar.cpp')
-rw-r--r-- | src/ui/toolbar/arc-toolbar.cpp | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/src/ui/toolbar/arc-toolbar.cpp b/src/ui/toolbar/arc-toolbar.cpp new file mode 100644 index 0000000..b3493e8 --- /dev/null +++ b/src/ui/toolbar/arc-toolbar.cpp @@ -0,0 +1,559 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Arc aux toolbar + */ +/* Authors: + * MenTaLguY <mental@rydia.net> + * Lauris Kaplinski <lauris@kaplinski.com> + * bulia byak <buliabyak@users.sf.net> + * Frank Felfe <innerspace@iname.com> + * John Cliff <simarilius@yahoo.com> + * David Turner <novalis@gnu.org> + * Josh Andler <scislac@scislac.com> + * Jon A. Cruz <jon@joncruz.org> + * Maximilian Albert <maximilian.albert@gmail.com> + * Tavmjong Bah <tavmjong@free.fr> + * Abhishek Sharma + * Kris De Gussem <Kris.DeGussem@gmail.com> + * + * Copyright (C) 2004 David Turner + * Copyright (C) 2003 MenTaLguY + * Copyright (C) 1999-2011 authors + * Copyright (C) 2001-2002 Ximian, Inc. + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "arc-toolbar.h" + +#include <glibmm/i18n.h> + +#include <gtkmm/radiotoolbutton.h> +#include <gtkmm/separatortoolitem.h> + +#include "desktop.h" +#include "document-undo.h" +#include "mod360.h" +#include "selection.h" + +#include "object/sp-ellipse.h" +#include "object/sp-namedview.h" + +#include "ui/icon-names.h" +#include "ui/tools/arc-tool.h" +#include "ui/widget/canvas.h" +#include "ui/widget/combo-tool-item.h" +#include "ui/widget/label-tool-item.h" +#include "ui/widget/spinbutton.h" +#include "ui/widget/spin-button-tool-item.h" +#include "ui/widget/unit-tracker.h" + +#include "widgets/widget-sizes.h" + +#include "xml/node-event-vector.h" + +using Inkscape::UI::Widget::UnitTracker; +using Inkscape::DocumentUndo; +using Inkscape::Util::Quantity; +using Inkscape::Util::unit_table; + + +static Inkscape::XML::NodeEventVector arc_tb_repr_events = { + nullptr, /* child_added */ + nullptr, /* child_removed */ + Inkscape::UI::Toolbar::ArcToolbar::event_attr_changed, + nullptr, /* content_changed */ + nullptr /* order_changed */ +}; + +namespace Inkscape { +namespace UI { +namespace Toolbar { +ArcToolbar::ArcToolbar(SPDesktop *desktop) : + Toolbar(desktop), + _tracker(new UnitTracker(Inkscape::Util::UNIT_TYPE_LINEAR)), + _freeze(false), + _repr(nullptr) +{ + auto init_units = desktop->getNamedView()->display_units; + _tracker->setActiveUnit(init_units); + auto prefs = Inkscape::Preferences::get(); + + { + _mode_item = Gtk::manage(new UI::Widget::LabelToolItem(_("<b>New:</b>"))); + _mode_item->set_use_markup(true); + add(*_mode_item); + } + + /* Radius X */ + { + std::vector<double> values = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500}; + auto rx_val = prefs->getDouble("/tools/shapes/arc/rx", 0); + rx_val = Quantity::convert(rx_val, "px", init_units); + + _rx_adj = Gtk::Adjustment::create(rx_val, 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP); + _rx_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("arc-rx", _("Rx:"), _rx_adj)); + _rx_item->set_tooltip_text(_("Horizontal radius of the circle, ellipse, or arc")); + _rx_item->set_custom_numeric_menu_data(values); + _tracker->addAdjustment(_rx_adj->gobj()); + _rx_item->get_spin_button()->addUnitTracker(_tracker); + _rx_item->set_focus_widget(desktop->canvas); + _rx_adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &ArcToolbar::value_changed), + _rx_adj, "rx")); + _rx_item->set_sensitive(false); + add(*_rx_item); + } + + /* Radius Y */ + { + std::vector<double> values = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500}; + auto ry_val = prefs->getDouble("/tools/shapes/arc/ry", 0); + ry_val = Quantity::convert(ry_val, "px", init_units); + + _ry_adj = Gtk::Adjustment::create(ry_val, 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP); + _ry_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("arc-ry", _("Ry:"), _ry_adj)); + _ry_item->set_tooltip_text(_("Vertical radius of the circle, ellipse, or arc")); + _ry_item->set_custom_numeric_menu_data(values); + _tracker->addAdjustment(_ry_adj->gobj()); + _ry_item->get_spin_button()->addUnitTracker(_tracker); + _ry_item->set_focus_widget(desktop->canvas); + _ry_adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &ArcToolbar::value_changed), + _ry_adj, "ry")); + _ry_item->set_sensitive(false); + add(*_ry_item); + } + + // add the units menu + { + auto unit_menu = _tracker->create_tool_item(_("Units"), ("") ); + add(*unit_menu); + } + + add(* Gtk::manage(new Gtk::SeparatorToolItem())); + + /* Start */ + { + auto start_val = prefs->getDouble("/tools/shapes/arc/start", 0.0); + _start_adj = Gtk::Adjustment::create(start_val, -360.0, 360.0, 1.0, 10.0); + auto eact = Gtk::manage(new UI::Widget::SpinButtonToolItem("arc-start", _("Start:"), _start_adj)); + eact->set_tooltip_text(_("The angle (in degrees) from the horizontal to the arc's start point")); + eact->set_focus_widget(desktop->canvas); + add(*eact); + } + + /* End */ + { + auto end_val = prefs->getDouble("/tools/shapes/arc/end", 0.0); + _end_adj = Gtk::Adjustment::create(end_val, -360.0, 360.0, 1.0, 10.0); + auto eact = Gtk::manage(new UI::Widget::SpinButtonToolItem("arc-end", _("End:"), _end_adj)); + eact->set_tooltip_text(_("The angle (in degrees) from the horizontal to the arc's end point")); + eact->set_focus_widget(desktop->canvas); + add(*eact); + } + _start_adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &ArcToolbar::startend_value_changed), + _start_adj, "start", _end_adj)); + _end_adj->signal_value_changed().connect( sigc::bind(sigc::mem_fun(*this, &ArcToolbar::startend_value_changed), + _end_adj, "end", _start_adj)); + + add(* Gtk::manage(new Gtk::SeparatorToolItem())); + + /* Arc: Slice, Arc, Chord */ + { + Gtk::RadioToolButton::Group type_group; + + auto slice_btn = Gtk::manage(new Gtk::RadioToolButton(_("Slice"))); + slice_btn->set_tooltip_text(_("Switch to slice (closed shape with two radii)")); + slice_btn->set_icon_name(INKSCAPE_ICON("draw-ellipse-segment")); + _type_buttons.push_back(slice_btn); + + auto arc_btn = Gtk::manage(new Gtk::RadioToolButton(_("Arc (Open)"))); + arc_btn->set_tooltip_text(_("Switch to arc (unclosed shape)")); + arc_btn->set_icon_name(INKSCAPE_ICON("draw-ellipse-arc")); + _type_buttons.push_back(arc_btn); + + auto chord_btn = Gtk::manage(new Gtk::RadioToolButton(_("Chord"))); + chord_btn->set_tooltip_text(_("Switch to chord (closed shape)")); + chord_btn->set_icon_name(INKSCAPE_ICON("draw-ellipse-chord")); + _type_buttons.push_back(chord_btn); + + slice_btn->set_group(type_group); + arc_btn->set_group(type_group); + chord_btn->set_group(type_group); + + gint type = prefs->getInt("/tools/shapes/arc/arc_type", 0); + _type_buttons[type]->set_active(); + + int btn_index = 0; + for (auto btn : _type_buttons) + { + btn->set_sensitive(); + btn->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ArcToolbar::type_changed), btn_index++)); + add(*btn); + } + } + + add(* Gtk::manage(new Gtk::SeparatorToolItem())); + + /* Make Whole */ + { + _make_whole = Gtk::manage(new Gtk::ToolButton(_("Make whole"))); + _make_whole->set_tooltip_text(_("Make the shape a whole ellipse, not arc or segment")); + _make_whole->set_icon_name(INKSCAPE_ICON("draw-ellipse-whole")); + _make_whole->signal_clicked().connect(sigc::mem_fun(*this, &ArcToolbar::defaults)); + add(*_make_whole); + _make_whole->set_sensitive(true); + } + + _single = true; + // sensitivize make whole and open checkbox + { + sensitivize( _start_adj->get_value(), _end_adj->get_value() ); + } + + desktop->connectEventContextChanged(sigc::mem_fun(*this, &ArcToolbar::check_ec)); + + show_all(); +} + +ArcToolbar::~ArcToolbar() +{ + if(_repr) { + _repr->removeListenerByData(this); + GC::release(_repr); + _repr = nullptr; + } +} + +GtkWidget * +ArcToolbar::create(SPDesktop *desktop) +{ + auto toolbar = new ArcToolbar(desktop); + return GTK_WIDGET(toolbar->gobj()); +} + +void +ArcToolbar::value_changed(Glib::RefPtr<Gtk::Adjustment>& adj, + gchar const *value_name) +{ + // Per SVG spec "a [radius] value of zero disables rendering of the element". + // However our implementation does not allow a setting of zero in the UI (not even in the XML editor) + // and ugly things happen if it's forced here, so better leave the properties untouched. + if (!adj->get_value()) { + return; + } + + Unit const *unit = _tracker->getActiveUnit(); + g_return_if_fail(unit != nullptr); + + SPDocument* document = _desktop->getDocument(); + + if (DocumentUndo::getUndoSensitive(document)) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, + Quantity::convert(adj->get_value(), unit, "px")); + } + + // quit if run by the attr_changed listener + if (_freeze || _tracker->isUpdating()) { + return; + } + + // in turn, prevent listener from responding + _freeze = 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_GENERICELLIPSE(item)) { + + SPGenericEllipse *ge = SP_GENERICELLIPSE(item); + + if (!strcmp(value_name, "rx")) { + ge->setVisibleRx(Quantity::convert(adj->get_value(), unit, "px")); + } else { + ge->setVisibleRy(Quantity::convert(adj->get_value(), unit, "px")); + } + + ge->normalize(); + ge->updateRepr(); + ge->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + + modmade = true; + } + } + + if (modmade) { + DocumentUndo::done(_desktop->getDocument(), _("Ellipse: Change radius"), INKSCAPE_ICON("draw-ellipse")); + } + + _freeze = false; +} + +void +ArcToolbar::startend_value_changed(Glib::RefPtr<Gtk::Adjustment>& adj, + gchar const *value_name, + Glib::RefPtr<Gtk::Adjustment>& other_adj) +{ + if (DocumentUndo::getUndoSensitive(_desktop->getDocument())) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->get_value()); + } + + // quit if run by the attr_changed listener + if (_freeze) { + return; + } + + // in turn, prevent listener from responding + _freeze = true; + + gchar* namespaced_name = g_strconcat("sodipodi:", value_name, nullptr); + + bool modmade = false; + auto itemlist= _desktop->getSelection()->items(); + for(auto i=itemlist.begin();i!=itemlist.end();++i){ + SPItem *item = *i; + if (SP_IS_GENERICELLIPSE(item)) { + + SPGenericEllipse *ge = SP_GENERICELLIPSE(item); + + if (!strcmp(value_name, "start")) { + ge->start = (adj->get_value() * M_PI)/ 180; + } else { + ge->end = (adj->get_value() * M_PI)/ 180; + } + + ge->normalize(); + ge->updateRepr(); + ge->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + + modmade = true; + } + } + + g_free(namespaced_name); + + sensitivize( adj->get_value(), other_adj->get_value() ); + + if (modmade) { + DocumentUndo::maybeDone(_desktop->getDocument(), value_name, _("Arc: Change start/end"), INKSCAPE_ICON("draw-ellipse")); + } + + _freeze = false; +} + +void +ArcToolbar::type_changed( int type ) +{ + if (DocumentUndo::getUndoSensitive(_desktop->getDocument())) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setInt("/tools/shapes/arc/arc_type", type); + } + + // quit if run by the attr_changed listener + if (_freeze) { + return; + } + + // in turn, prevent listener from responding + _freeze = true; + + Glib::ustring arc_type = "slice"; + bool open = false; + switch (type) { + case 0: + arc_type = "slice"; + open = false; + break; + case 1: + arc_type = "arc"; + open = true; + break; + case 2: + arc_type = "chord"; + open = true; // For backward compat, not truly open but chord most like arc. + break; + default: + std::cerr << "sp_arctb_type_changed: bad arc type: " << type << std::endl; + } + + bool modmade = false; + auto itemlist= _desktop->getSelection()->items(); + for(auto i=itemlist.begin();i!=itemlist.end();++i){ + SPItem *item = *i; + if (SP_IS_GENERICELLIPSE(item)) { + Inkscape::XML::Node *repr = item->getRepr(); + repr->setAttribute("sodipodi:open", (open?"true":nullptr) ); + repr->setAttribute("sodipodi:arc-type", arc_type); + item->updateRepr(); + modmade = true; + } + } + + if (modmade) { + DocumentUndo::done(_desktop->getDocument(), _("Arc: Changed arc type"), INKSCAPE_ICON("draw-ellipse")); + } + + _freeze = false; +} + +void +ArcToolbar::defaults() +{ + _start_adj->set_value(0.0); + _end_adj->set_value(0.0); + + if(_desktop->canvas) _desktop->canvas->grab_focus(); +} + +void +ArcToolbar::sensitivize( double v1, double v2 ) +{ + if (v1 == 0 && v2 == 0) { + if (_single) { // only for a single selected ellipse (for now) + for (auto btn : _type_buttons) btn->set_sensitive(false); + _make_whole->set_sensitive(false); + } + } else { + for (auto btn : _type_buttons) btn->set_sensitive(true); + _make_whole->set_sensitive(true); + } +} + +void +ArcToolbar::check_ec(SPDesktop* desktop, Inkscape::UI::Tools::ToolBase* ec) +{ + if (SP_IS_ARC_CONTEXT(ec)) { + _changed = _desktop->getSelection()->connectChanged(sigc::mem_fun(*this, &ArcToolbar::selection_changed)); + selection_changed(desktop->getSelection()); + } else { + if (_changed) { + _changed.disconnect(); + if(_repr) { + _repr->removeListenerByData(this); + Inkscape::GC::release(_repr); + _repr = nullptr; + } + } + } +} + +void +ArcToolbar::selection_changed(Inkscape::Selection *selection) +{ + int n_selected = 0; + Inkscape::XML::Node *repr = nullptr; + + if ( _repr ) { + _item = nullptr; + _repr->removeListenerByData(this); + GC::release(_repr); + _repr = nullptr; + } + + SPItem *item = nullptr; + + for(auto i : selection->items()){ + if (SP_IS_GENERICELLIPSE(i)) { + n_selected++; + item = i; + repr = item->getRepr(); + } + } + + _single = false; + if (n_selected == 0) { + _mode_item->set_markup(_("<b>New:</b>")); + } else if (n_selected == 1) { + _single = true; + _mode_item->set_markup(_("<b>Change:</b>")); + _rx_item->set_sensitive(true); + _ry_item->set_sensitive(true); + + if (repr) { + _repr = repr; + _item = item; + Inkscape::GC::anchor(_repr); + _repr->addListener(&arc_tb_repr_events, this); + _repr->synthesizeEvents(&arc_tb_repr_events, this); + } + } else { + // FIXME: implement averaging of all parameters for multiple selected + //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>")); + _mode_item->set_markup(_("<b>Change:</b>")); + sensitivize( 1, 0 ); + } +} + +void +ArcToolbar::event_attr_changed(Inkscape::XML::Node *repr, gchar const * /*name*/, + gchar const * /*old_value*/, gchar const * /*new_value*/, + bool /*is_interactive*/, gpointer data) +{ + auto toolbar = reinterpret_cast<ArcToolbar *>(data); + + // quit if run by the _changed callbacks + if (toolbar->_freeze) { + return; + } + + // in turn, prevent callbacks from responding + toolbar->_freeze = true; + + if (toolbar->_item && SP_IS_GENERICELLIPSE(toolbar->_item)) { + SPGenericEllipse *ge = SP_GENERICELLIPSE(toolbar->_item); + + Unit const *unit = toolbar->_tracker->getActiveUnit(); + g_return_if_fail(unit != nullptr); + + gdouble rx = ge->getVisibleRx(); + gdouble ry = ge->getVisibleRy(); + toolbar->_rx_adj->set_value(Quantity::convert(rx, "px", unit)); + toolbar->_ry_adj->set_value(Quantity::convert(ry, "px", unit)); + } + + gdouble start = repr->getAttributeDouble("sodipodi:start", 0.0);; + gdouble end = repr->getAttributeDouble("sodipodi:end", 0.0); + + toolbar->_start_adj->set_value(mod360((start * 180)/M_PI)); + toolbar->_end_adj->set_value(mod360((end * 180)/M_PI)); + + toolbar->sensitivize(toolbar->_start_adj->get_value(), toolbar->_end_adj->get_value()); + + char const *arctypestr = nullptr; + arctypestr = repr->attribute("sodipodi:arc-type"); + if (!arctypestr) { // For old files. + char const *openstr = nullptr; + openstr = repr->attribute("sodipodi:open"); + arctypestr = (openstr ? "arc" : "slice"); + } + + if (!strcmp(arctypestr,"slice")) { + toolbar->_type_buttons[0]->set_active(); + } else if (!strcmp(arctypestr,"arc")) { + toolbar->_type_buttons[1]->set_active(); + } else { + toolbar->_type_buttons[2]->set_active(); + } + + toolbar->_freeze = false; +} + +} +} +} + + +/* + 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 : |