// SPDX-License-Identifier: GPL-2.0-or-later /** * @file * LPE 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 v2+, read the file 'COPYING' for more information. */ #include "lpe-toolbar.h" #include #include #include "live_effects/lpe-line_segment.h" #include "ui/dialog/dialog-container.h" #include "ui/icon-names.h" #include "ui/tools/lpe-tool.h" #include "ui/widget/combo-tool-item.h" #include "ui/widget/unit-tracker.h" using Inkscape::UI::Widget::UnitTracker; using Inkscape::Util::Unit; using Inkscape::Util::Quantity; using Inkscape::DocumentUndo; using Inkscape::UI::Tools::ToolBase; using Inkscape::UI::Tools::LpeTool; namespace Inkscape { namespace UI { namespace Toolbar { LPEToolbar::LPEToolbar(SPDesktop *desktop) : Toolbar(desktop), _tracker(new UnitTracker(Util::UNIT_TYPE_LINEAR)), _freeze(false), _currentlpe(nullptr), _currentlpeitem(nullptr) { _tracker->setActiveUnit(_desktop->getNamedView()->display_units); auto unit = _tracker->getActiveUnit(); g_return_if_fail(unit != nullptr); auto prefs = Inkscape::Preferences::get(); prefs->setString("/tools/lpetool/unit", unit->abbr); /* Automatically create a list of LPEs that get added to the toolbar **/ { Gtk::RadioToolButton::Group mode_group; // The first toggle button represents the state that no subtool is active. auto inactive_mode_btn = Gtk::manage(new Gtk::RadioToolButton(mode_group, _("All inactive"))); inactive_mode_btn->set_tooltip_text(_("No geometric tool is active")); inactive_mode_btn->set_icon_name(INKSCAPE_ICON("draw-geometry-inactive")); _mode_buttons.push_back(inactive_mode_btn); Inkscape::LivePathEffect::EffectType type; for (int i = 1; i < num_subtools; ++i) { // i == 0 ia INVALIDE_LPE. type = lpesubtools[i].type; auto btn = Gtk::manage(new Gtk::RadioToolButton(mode_group, Inkscape::LivePathEffect::LPETypeConverter.get_label(type))); btn->set_tooltip_text(_(Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str())); btn->set_icon_name(lpesubtools[i].icon_name); _mode_buttons.push_back(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, &LPEToolbar::mode_changed), btn_idx++)); } int mode = prefs->getInt("/tools/lpetool/mode", 0); _mode_buttons[mode]->set_active(); } add(* Gtk::manage(new Gtk::SeparatorToolItem())); /* Show limiting bounding box */ { _show_bbox_item = add_toggle_button(_("Show limiting bounding box"), _("Show bounding box (used to cut infinite lines)")); _show_bbox_item->set_icon_name(INKSCAPE_ICON("show-bounding-box")); _show_bbox_item->signal_toggled().connect(sigc::mem_fun(*this, &LPEToolbar::toggle_show_bbox)); _show_bbox_item->set_active(prefs->getBool( "/tools/lpetool/show_bbox", true )); } /* Set limiting bounding box to bbox of current selection */ { // TODO: Shouldn't this just be a button (not toggle button)? _bbox_from_selection_item = add_toggle_button(_("Get limiting bounding box from selection"), _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection")); _bbox_from_selection_item->set_icon_name(INKSCAPE_ICON("draw-geometry-set-bounding-box")); _bbox_from_selection_item->signal_toggled().connect(sigc::mem_fun(*this, &LPEToolbar::toggle_set_bbox)); _bbox_from_selection_item->set_active(false); } add(* Gtk::manage(new Gtk::SeparatorToolItem())); /* Combo box to choose line segment type */ { UI::Widget::ComboToolItemColumns columns; Glib::RefPtr store = Gtk::ListStore::create(columns); std::vector line_segment_dropdown_items_list = { _("Closed"), _("Open start"), _("Open end"), _("Open both") }; for (auto item: line_segment_dropdown_items_list) { Gtk::TreeModel::Row row = *(store->append()); row[columns.col_label ] = item; row[columns.col_sensitive] = true; } _line_segment_combo = Gtk::manage(UI::Widget::ComboToolItem::create(_("Line Type"), _("Choose a line segment type"), "Not Used", store)); _line_segment_combo->use_group_label(false); _line_segment_combo->set_active(0); _line_segment_combo->signal_changed().connect(sigc::mem_fun(*this, &LPEToolbar::change_line_segment_type)); add(*_line_segment_combo); } add(* Gtk::manage(new Gtk::SeparatorToolItem())); /* Display measuring info for selected items */ { _measuring_item = add_toggle_button(_("Display measuring info"), _("Display measuring info for selected items")); _measuring_item->set_icon_name(INKSCAPE_ICON("draw-geometry-show-measuring-info")); _measuring_item->signal_toggled().connect(sigc::mem_fun(*this, &LPEToolbar::toggle_show_measuring_info)); _measuring_item->set_active( prefs->getBool( "/tools/lpetool/show_measuring_info", true ) ); } // Add the units menu { _units_item = _tracker->create_tool_item(_("Units"), ("") ); add(*_units_item); _units_item->signal_changed_after().connect(sigc::mem_fun(*this, &LPEToolbar::unit_changed)); _units_item->set_sensitive( prefs->getBool("/tools/lpetool/show_measuring_info", true)); } add(* Gtk::manage(new Gtk::SeparatorToolItem())); /* Open LPE dialog (to adapt parameters numerically) */ { // TODO: Shouldn't this be a regular Gtk::ToolButton (not toggle)? _open_lpe_dialog_item = add_toggle_button(_("Open LPE dialog"), _("Open LPE dialog (to adapt parameters numerically)")); _open_lpe_dialog_item->set_icon_name(INKSCAPE_ICON("dialog-geometry")); _open_lpe_dialog_item->signal_toggled().connect(sigc::mem_fun(*this, &LPEToolbar::open_lpe_dialog)); _open_lpe_dialog_item->set_active(false); } desktop->connectEventContextChanged(sigc::mem_fun(*this, &LPEToolbar::watch_ec)); show_all(); } void LPEToolbar::set_mode(int mode) { _mode_buttons[mode]->set_active(); } GtkWidget * LPEToolbar::create(SPDesktop *desktop) { auto toolbar = new LPEToolbar(desktop); return GTK_WIDGET(toolbar->gobj()); } // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed) void LPEToolbar::mode_changed(int mode) { using namespace Inkscape::LivePathEffect; ToolBase *ec = _desktop->event_context; if (!SP_IS_LPETOOL_CONTEXT(ec)) { return; } // only take action if run by the attr_changed listener if (!_freeze) { // in turn, prevent listener from responding _freeze = true; EffectType type = lpesubtools[mode].type; LpeTool *lc = SP_LPETOOL_CONTEXT(_desktop->event_context); bool success = lpetool_try_construction(lc, type); if (success) { // since the construction was already performed, we set the state back to inactive _mode_buttons[0]->set_active(); mode = 0; } else { // switch to the chosen subtool SP_LPETOOL_CONTEXT(_desktop->event_context)->mode = type; } if (DocumentUndo::getUndoSensitive(_desktop->getDocument())) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setInt( "/tools/lpetool/mode", mode ); } _freeze = false; } } void LPEToolbar::toggle_show_bbox() { auto prefs = Inkscape::Preferences::get(); bool show = _show_bbox_item->get_active(); prefs->setBool("/tools/lpetool/show_bbox", show); LpeTool *lc = dynamic_cast(_desktop->event_context); if (lc) { lpetool_context_reset_limiting_bbox(lc); } } void LPEToolbar::toggle_set_bbox() { auto selection = _desktop->getSelection(); auto bbox = selection->visualBounds(); if (bbox) { Geom::Point A(bbox->min()); Geom::Point B(bbox->max()); A *= _desktop->doc2dt(); B *= _desktop->doc2dt(); // TODO: should we provide a way to store points in prefs? auto prefs = Inkscape::Preferences::get(); prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]); prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]); prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]); prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]); lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(_desktop->event_context)); } _bbox_from_selection_item->set_active(false); } void LPEToolbar::change_line_segment_type(int mode) { using namespace Inkscape::LivePathEffect; // quit if run by the attr_changed listener if (_freeze) { return; } // in turn, prevent listener from responding _freeze = true; auto line_seg = dynamic_cast(_currentlpe); if (_currentlpeitem && line_seg) { line_seg->end_type.param_set_value(static_cast(mode)); sp_lpe_item_update_patheffect(_currentlpeitem, true, true); } _freeze = false; } void LPEToolbar::toggle_show_measuring_info() { LpeTool *lc = dynamic_cast(_desktop->event_context); if (!lc) { return; } bool show = _measuring_item->get_active(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setBool("/tools/lpetool/show_measuring_info", show); lpetool_show_measuring_info(lc, show); _units_item->set_sensitive( show ); } void LPEToolbar::unit_changed(int /* NotUsed */) { Unit const *unit = _tracker->getActiveUnit(); g_return_if_fail(unit != nullptr); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setString("/tools/lpetool/unit", unit->abbr); if (SP_IS_LPETOOL_CONTEXT(_desktop->event_context)) { LpeTool *lc = SP_LPETOOL_CONTEXT(_desktop->event_context); lpetool_delete_measuring_items(lc); lpetool_create_measuring_items(lc); } } void LPEToolbar::open_lpe_dialog() { if (dynamic_cast(_desktop->event_context)) { _desktop->getContainer()->new_dialog("LivePathEffect"); } else { std::cerr << "LPEToolbar::open_lpe_dialog: LPEToolbar active but current tool is not LPE tool!" << std::endl; } _open_lpe_dialog_item->set_active(false); } void LPEToolbar::watch_ec(SPDesktop* desktop, Inkscape::UI::Tools::ToolBase* ec) { if (SP_IS_LPETOOL_CONTEXT(ec)) { // Watch selection c_selection_modified = desktop->getSelection()->connectModified(sigc::mem_fun(*this, &LPEToolbar::sel_modified)); c_selection_changed = desktop->getSelection()->connectChanged(sigc::mem_fun(*this, &LPEToolbar::sel_changed)); sel_changed(desktop->getSelection()); } else { if (c_selection_modified) c_selection_modified.disconnect(); if (c_selection_changed) c_selection_changed.disconnect(); } } void LPEToolbar::sel_modified(Inkscape::Selection *selection, guint /*flags*/) { ToolBase *ec = selection->desktop()->event_context; if (SP_IS_LPETOOL_CONTEXT(ec)) { lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec)); } } void LPEToolbar::sel_changed(Inkscape::Selection *selection) { using namespace Inkscape::LivePathEffect; ToolBase *ec = selection->desktop()->event_context; if (!SP_IS_LPETOOL_CONTEXT(ec)) { return; } LpeTool *lc = SP_LPETOOL_CONTEXT(ec); lpetool_delete_measuring_items(lc); lpetool_create_measuring_items(lc, selection); // activate line segment combo box if a single item with LPELineSegment is selected SPItem *item = selection->singleItem(); if (item && is(item) && lpetool_item_has_construction(lc, item)) { auto lpeitem = cast(item); Effect* lpe = lpeitem->getCurrentLPE(); if (lpe && lpe->effectType() == LINE_SEGMENT) { LPELineSegment *lpels = static_cast(lpe); _currentlpe = lpe; _currentlpeitem = lpeitem; _line_segment_combo->set_sensitive(true); _line_segment_combo->set_active( lpels->end_type.get_value() ); } else { _currentlpe = nullptr; _currentlpeitem = nullptr; _line_segment_combo->set_sensitive(false); } } else { _currentlpe = nullptr; _currentlpeitem = nullptr; _line_segment_combo->set_sensitive(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 :