summaryrefslogtreecommitdiffstats
path: root/src/ui/toolbar/arc-toolbar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/toolbar/arc-toolbar.cpp')
-rw-r--r--src/ui/toolbar/arc-toolbar.cpp559
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 :