diff options
Diffstat (limited to 'src/ui/widget/selected-style.cpp')
-rw-r--r-- | src/ui/widget/selected-style.cpp | 1435 |
1 files changed, 1435 insertions, 0 deletions
diff --git a/src/ui/widget/selected-style.cpp b/src/ui/widget/selected-style.cpp new file mode 100644 index 0000000..433112c --- /dev/null +++ b/src/ui/widget/selected-style.cpp @@ -0,0 +1,1435 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Author: + * buliabyak@gmail.com + * Abhishek Sharma + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) 2005 author + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "selected-style.h" + +#include <vector> + +#include <gtkmm/separatormenuitem.h> + + +#include "desktop-style.h" +#include "document-undo.h" +#include "gradient-chemistry.h" +#include "message-context.h" +#include "selection.h" + +#include "include/gtkmm_version.h" + +#include "object/sp-hatch.h" +#include "object/sp-linear-gradient.h" +#include "object/sp-mesh-gradient.h" +#include "object/sp-namedview.h" +#include "object/sp-pattern.h" +#include "object/sp-radial-gradient.h" +#include "style.h" + +#include "svg/css-ostringstream.h" +#include "svg/svg-color.h" + +#include "ui/cursor-utils.h" +#include "ui/dialog/dialog-container.h" +#include "ui/dialog/dialog-base.h" +#include "ui/dialog/fill-and-stroke.h" +#include "ui/icon-names.h" +#include "ui/tools/tool-base.h" +#include "ui/widget/canvas.h" // Forced redraws. +#include "ui/widget/color-preview.h" +#include "ui/widget/gradient-image.h" + +#include "widgets/ege-paint-def.h" +#include "widgets/spw-utilities.h" + +using Inkscape::Util::unit_table; + +static gdouble const _sw_presets[] = { 32 , 16 , 10 , 8 , 6 , 4 , 3 , 2 , 1.5 , 1 , 0.75 , 0.5 , 0.25 , 0.1 }; +static gchar const *const _sw_presets_str[] = {"32", "16", "10", "8", "6", "4", "3", "2", "1.5", "1", "0.75", "0.5", "0.25", "0.1"}; + +static void +ss_selection_changed (Inkscape::Selection *, gpointer data) +{ + Inkscape::UI::Widget::SelectedStyle *ss = (Inkscape::UI::Widget::SelectedStyle *) data; + ss->update(); +} + +static void +ss_selection_modified( Inkscape::Selection *selection, guint flags, gpointer data ) +{ + // Don't update the style when dragging or doing non-style related changes + if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG)) { + ss_selection_changed (selection, data); + } +} + +static void +ss_subselection_changed( gpointer /*dragger*/, gpointer data ) +{ + ss_selection_changed (nullptr, data); +} + +namespace { + +void clearTooltip( Gtk::Widget &widget ) +{ + widget.set_tooltip_text(""); + widget.set_has_tooltip(false); +} + +} // namespace + +namespace Inkscape { +namespace UI { +namespace Widget { + + +struct DropTracker { + SelectedStyle* parent; + int item; +}; + +/* Drag and Drop */ +enum ui_drop_target_info { + APP_OSWB_COLOR +}; + +//TODO: warning: deprecated conversion from string constant to ‘gchar*’ +// +//Turn out to be warnings that we should probably leave in place. The +// pointers/types used need to be read-only. So until we correct the using +// code, those warnings are actually desired. They say "Hey! Fix this". We +// definitely don't want to hide/ignore them. --JonCruz +static const GtkTargetEntry ui_drop_target_entries [] = { + {g_strdup("application/x-oswb-color"), 0, APP_OSWB_COLOR} +}; + +static guint nui_drop_target_entries = G_N_ELEMENTS(ui_drop_target_entries); + +/* convenience function */ +static Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop); + +SelectedStyle::SelectedStyle(bool /*layout*/) + : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) + , current_stroke_width(0) + , _sw_unit(nullptr) + , _desktop(nullptr) + , _table() + , _fill_label(_("Fill:")) + , _stroke_label(_("Stroke:")) + , _opacity_label(_("O:")) + , _fill_place(this, SS_FILL) + , _stroke_place(this, SS_STROKE) + , _fill_flag_place() + , _stroke_flag_place() + , _opacity_place() + , _opacity_adjustment(Gtk::Adjustment::create(100, 0.0, 100, 1.0, 10.0)) + , _opacity_sb(0.02, 0) + , _fill(Gtk::ORIENTATION_HORIZONTAL, 1) + , _stroke(Gtk::ORIENTATION_HORIZONTAL) + , _stroke_width_place(this) + , _stroke_width("") + , _fill_empty_space("") + , _opacity_blocked(false) +{ + set_name("SelectedStyle"); + _drop[0] = _drop[1] = nullptr; + _dropEnabled[0] = _dropEnabled[1] = false; + + _fill_label.set_halign(Gtk::ALIGN_END); + _fill_label.set_valign(Gtk::ALIGN_CENTER); + _fill_label.set_margin_top(0); + _fill_label.set_margin_bottom(0); + _stroke_label.set_halign(Gtk::ALIGN_END); + _stroke_label.set_valign(Gtk::ALIGN_CENTER); + _stroke_label.set_margin_top(0); + _stroke_label.set_margin_bottom(0); + _opacity_label.set_halign(Gtk::ALIGN_START); + _opacity_label.set_valign(Gtk::ALIGN_CENTER); + _opacity_label.set_margin_top(0); + _opacity_label.set_margin_bottom(0); + _stroke_width.set_name("monoStrokeWidth"); + _fill_empty_space.set_name("fillEmptySpace"); + + _fill_label.set_margin_start(0); + _fill_label.set_margin_end(0); + _stroke_label.set_margin_start(0); + _stroke_label.set_margin_end(0); + _opacity_label.set_margin_start(0); + _opacity_label.set_margin_end(0); + + _table.set_column_spacing(2); + _table.set_row_spacing(0); + + for (int i = SS_FILL; i <= SS_STROKE; i++) { + + _na[i].set_markup (_("N/A")); + _na[i].show_all(); + __na[i] = (_("Nothing selected")); + + if (i == SS_FILL) { + _none[i].set_markup (C_("Fill", "<i>None</i>")); + } else { + _none[i].set_markup (C_("Stroke", "<i>None</i>")); + } + _none[i].show_all(); + __none[i] = (i == SS_FILL)? (C_("Fill and stroke", "No fill, middle-click for black fill")) : (C_("Fill and stroke", "No stroke, middle-click for black stroke")); + + _pattern[i].set_markup (_("Pattern")); + _pattern[i].show_all(); + __pattern[i] = (i == SS_FILL)? (_("Pattern (fill)")) : (_("Pattern (stroke)")); + + _hatch[i].set_markup(_("Hatch")); + _hatch[i].show_all(); + __hatch[i] = (i == SS_FILL) ? (_("Hatch (fill)")) : (_("Hatch (stroke)")); + + _lgradient[i].set_markup (_("<b>L</b>")); + _lgradient[i].show_all(); + __lgradient[i] = (i == SS_FILL)? (_("Linear gradient (fill)")) : (_("Linear gradient (stroke)")); + + _gradient_preview_l[i] = Gtk::manage(new GradientImage(nullptr)); + _gradient_box_l[i].set_orientation(Gtk::ORIENTATION_HORIZONTAL); + _gradient_box_l[i].pack_start(_lgradient[i]); + _gradient_box_l[i].pack_start(*_gradient_preview_l[i]); + _gradient_box_l[i].show_all(); + + _rgradient[i].set_markup (_("<b>R</b>")); + _rgradient[i].show_all(); + __rgradient[i] = (i == SS_FILL)? (_("Radial gradient (fill)")) : (_("Radial gradient (stroke)")); + + _gradient_preview_r[i] = Gtk::manage(new GradientImage(nullptr)); + _gradient_box_r[i].set_orientation(Gtk::ORIENTATION_HORIZONTAL); + _gradient_box_r[i].pack_start(_rgradient[i]); + _gradient_box_r[i].pack_start(*_gradient_preview_r[i]); + _gradient_box_r[i].show_all(); + +#ifdef WITH_MESH + _mgradient[i].set_markup (_("<b>M</b>")); + _mgradient[i].show_all(); + __mgradient[i] = (i == SS_FILL)? (_("Mesh gradient (fill)")) : (_("Mesh gradient (stroke)")); + + _gradient_preview_m[i] = Gtk::manage(new GradientImage(nullptr)); + _gradient_box_m[i].set_orientation(Gtk::ORIENTATION_HORIZONTAL); + _gradient_box_m[i].pack_start(_mgradient[i]); + _gradient_box_m[i].pack_start(*_gradient_preview_m[i]); + _gradient_box_m[i].show_all(); +#endif + + _many[i].set_markup (_("≠")); + _many[i].show_all(); + __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes")); + + _unset[i].set_markup (_("<b>Unset</b>")); + _unset[i].show_all(); + __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke")); + + _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0); + __color[i] = (i == SS_FILL)? (_("Flat color (fill)")) : (_("Flat color (stroke)")); + + // TRANSLATORS: A means "Averaged" + _averaged[i].set_markup (_("<b>a</b>")); + _averaged[i].show_all(); + __averaged[i] = (i == SS_FILL)? (_("Fill is averaged over selected objects")) : (_("Stroke is averaged over selected objects")); + + // TRANSLATORS: M means "Multiple" + _multiple[i].set_markup (_("<b>m</b>")); + _multiple[i].show_all(); + __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke")); + + _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), Gtk::ALIGN_START))); + _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this, + (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit )); + + _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), Gtk::ALIGN_START))); + _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this, + (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused )); + + _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), Gtk::ALIGN_START))); + _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this, + (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected )); + + _popup_invert[i].add(*(new Gtk::Label(_("Invert"), Gtk::ALIGN_START))); + _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this, + (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert )); + + _popup_white[i].add(*(new Gtk::Label(_("White"), Gtk::ALIGN_START))); + _popup_white[i].signal_activate().connect(sigc::mem_fun(*this, + (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white )); + + _popup_black[i].add(*(new Gtk::Label(_("Black"), Gtk::ALIGN_START))); + _popup_black[i].signal_activate().connect(sigc::mem_fun(*this, + (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black )); + + _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), Gtk::ALIGN_START))); + _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this, + (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy )); + + _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), Gtk::ALIGN_START))); + _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this, + (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste )); + + _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), Gtk::ALIGN_START))); + _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this, + &SelectedStyle::on_fillstroke_swap)); + + _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), Gtk::ALIGN_START))); + _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this, + (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque )); + + //TRANSLATORS COMMENT: unset is a verb here + _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), Gtk::ALIGN_START))); + _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this, + (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset )); + + _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), Gtk::ALIGN_START))); + _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this, + (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove )); + + _popup[i].attach(_popup_edit[i], 0,1, 0,1); + _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 1,2); + _popup[i].attach(_popup_lastused[i], 0,1, 2,3); + _popup[i].attach(_popup_lastselected[i], 0,1, 3,4); + _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 4,5); + _popup[i].attach(_popup_invert[i], 0,1, 5,6); + _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 6,7); + _popup[i].attach(_popup_white[i], 0,1, 7,8); + _popup[i].attach(_popup_black[i], 0,1, 8,9); + _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 9,10); + _popup[i].attach(_popup_copy[i], 0,1, 10,11); + _popup_copy[i].set_sensitive(false); + _popup[i].attach(_popup_paste[i], 0,1, 11,12); + _popup[i].attach(_popup_swap[i], 0,1, 12,13); + _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14); + _popup[i].attach(_popup_opaque[i], 0,1, 14,15); + _popup[i].attach(_popup_unset[i], 0,1, 15,16); + _popup[i].attach(_popup_remove[i], 0,1, 16,17); + _popup[i].show_all(); + + _mode[i] = SS_NA; + } + + { + int row = 0; + + Inkscape::Util::UnitTable::UnitMap m = unit_table.units(Inkscape::Util::UNIT_TYPE_LINEAR); + Inkscape::Util::UnitTable::UnitMap::iterator iter = m.begin(); + while(iter != m.end()) { + Gtk::RadioMenuItem *mi = Gtk::manage(new Gtk::RadioMenuItem(_sw_group)); + mi->add(*(new Gtk::Label(iter->first, Gtk::ALIGN_START))); + _unit_mis.push_back(mi); + Inkscape::Util::Unit const *u = unit_table.getUnit(iter->first); + mi->signal_activate().connect(sigc::bind<Inkscape::Util::Unit const *>(sigc::mem_fun(*this, &SelectedStyle::on_popup_units), u)); + _popup_sw.attach(*mi, 0,1, row, row+1); + row++; + ++iter; + } + + _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, row, row+1); + row++; + + for (guint i = 0; i < G_N_ELEMENTS(_sw_presets_str); ++i) { + Gtk::MenuItem *mi = Gtk::manage(new Gtk::MenuItem()); + mi->add(*(new Gtk::Label(_sw_presets_str[i], Gtk::ALIGN_START))); + mi->signal_activate().connect(sigc::bind<int>(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i)); + _popup_sw.attach(*mi, 0,1, row, row+1); + row++; + } + + _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, row, row+1); + row++; + + _popup_sw_remove.add(*(new Gtk::Label(_("Remove"), Gtk::ALIGN_START))); + _popup_sw_remove.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove)); + _popup_sw.attach(_popup_sw_remove, 0,1, row, row+1); + row++; + + _popup_sw.show_all(); + } + // fill row + _fill_flag_place.set_size_request(SELECTED_STYLE_FLAG_WIDTH , -1); + + _fill_place.add(_na[SS_FILL]); + _fill_place.set_tooltip_text(__na[SS_FILL]); + _fill.set_size_request(SELECTED_STYLE_PLACE_WIDTH, -1); + _fill.pack_start(_fill_place, Gtk::PACK_EXPAND_WIDGET); + + _fill_empty_space.set_size_request(SELECTED_STYLE_STROKE_WIDTH); + + // stroke row + _stroke_flag_place.set_size_request(SELECTED_STYLE_FLAG_WIDTH, -1); + + _stroke_place.add(_na[SS_STROKE]); + _stroke_place.set_tooltip_text(__na[SS_STROKE]); + _stroke.set_size_request(SELECTED_STYLE_PLACE_WIDTH, -1); + _stroke.pack_start(_stroke_place, Gtk::PACK_EXPAND_WIDGET); + + _stroke_width_place.add(_stroke_width); + _stroke_width_place.set_size_request(SELECTED_STYLE_STROKE_WIDTH); + + // opacity selector + _opacity_place.add(_opacity_label); + + _opacity_sb.set_adjustment(_opacity_adjustment); + _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1); + _opacity_sb.set_sensitive (false); + + // arrange in table + _table.attach(_fill_label, 0, 0, 1, 1); + _table.attach(_stroke_label, 0, 1, 1, 1); + + _table.attach(_fill_flag_place, 1, 0, 1, 1); + _table.attach(_stroke_flag_place, 1, 1, 1, 1); + + _table.attach(_fill, 2, 0, 1, 1); + _table.attach(_stroke, 2, 1, 1, 1); + + _table.attach(_fill_empty_space, 3, 0, 1, 1); + _table.attach(_stroke_width_place, 3, 1, 1, 1); + + _table.attach(_opacity_place, 4, 0, 1, 2); + _table.attach(_opacity_sb, 5, 0, 1, 2); + + pack_start(_table, true, true, 2); + + set_size_request (SELECTED_STYLE_WIDTH, -1); + + _drop[SS_FILL] = new DropTracker(); + ((DropTracker*)_drop[SS_FILL])->parent = this; + ((DropTracker*)_drop[SS_FILL])->item = SS_FILL; + + _drop[SS_STROKE] = new DropTracker(); + ((DropTracker*)_drop[SS_STROKE])->parent = this; + ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE; + + g_signal_connect(_stroke_place.gobj(), + "drag_data_received", + G_CALLBACK(dragDataReceived), + _drop[SS_STROKE]); + + g_signal_connect(_fill_place.gobj(), + "drag_data_received", + G_CALLBACK(dragDataReceived), + _drop[SS_FILL]); + + _fill_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click)); + _stroke_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click)); + _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click)); + _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click)); + _stroke_width_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click)); + _opacity_sb.signal_populate_popup().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_menu)); + _opacity_sb.signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed)); +} + +SelectedStyle::~SelectedStyle() +{ + selection_changed_connection->disconnect(); + delete selection_changed_connection; + selection_modified_connection->disconnect(); + delete selection_modified_connection; + subselection_changed_connection->disconnect(); + delete subselection_changed_connection; + _unit_mis.clear(); + + _fill_place.remove(); + _stroke_place.remove(); + + for (int i = SS_FILL; i <= SS_STROKE; i++) { + delete _color_preview[i]; + } + + delete (DropTracker*)_drop[SS_FILL]; + delete (DropTracker*)_drop[SS_STROKE]; +} + +void +SelectedStyle::setDesktop(SPDesktop *desktop) +{ + _desktop = desktop; + + Inkscape::Selection *selection = desktop->getSelection(); + + selection_changed_connection = new sigc::connection (selection->connectChanged( + sigc::bind ( + sigc::ptr_fun(&ss_selection_changed), + this ) + )); + selection_modified_connection = new sigc::connection (selection->connectModified( + sigc::bind ( + sigc::ptr_fun(&ss_selection_modified), + this ) + )); + subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged( + sigc::bind ( + sigc::ptr_fun(&ss_subselection_changed), + this ) + )); + + _sw_unit = desktop->getNamedView()->display_units; + + // Set the doc default unit active in the units list + for ( auto mi:_unit_mis ) { + if (mi && mi->get_label() == _sw_unit->abbr) { + mi->set_active(); + break; + } + } +} + +void SelectedStyle::dragDataReceived( GtkWidget */*widget*/, + GdkDragContext */*drag_context*/, + gint /*x*/, gint /*y*/, + GtkSelectionData *data, + guint /*info*/, + guint /*event_time*/, + gpointer user_data ) +{ + DropTracker* tracker = (DropTracker*)user_data; + + // copied from drag-and-drop.cpp, case APP_OSWB_COLOR + bool worked = false; + Glib::ustring colorspec; + if (gtk_selection_data_get_format(data) == 8) { + ege::PaintDef color; + worked = color.fromMIMEData("application/x-oswb-color", + reinterpret_cast<char const *>(gtk_selection_data_get_data(data)), + gtk_selection_data_get_length(data), + gtk_selection_data_get_format(data)); + if (worked) { + if (color.getType() == ege::PaintDef::CLEAR) { + colorspec = ""; // TODO check if this is sufficient + } else if (color.getType() == ege::PaintDef::NONE) { + colorspec = "none"; + } else { + unsigned int r = color.getR(); + unsigned int g = color.getG(); + unsigned int b = color.getB(); + + gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b); + colorspec = tmp; + g_free(tmp); + } + } + } + if (worked) { + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property(css, (tracker->item == SS_FILL) ? "fill":"stroke", colorspec.c_str()); + + sp_desktop_set_style(tracker->parent->_desktop, css); + sp_repr_css_attr_unref(css); + DocumentUndo::done(tracker->parent->_desktop->getDocument(), _("Drop color"), ""); + } +} + +void SelectedStyle::on_fill_remove() { + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_set_property (css, "fill", "none"); + sp_desktop_set_style (_desktop, css, true, true); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Remove fill"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_stroke_remove() { + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_set_property (css, "stroke", "none"); + sp_desktop_set_style (_desktop, css, true, true); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Remove stroke"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_fill_unset() { + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_unset_property (css, "fill"); + sp_desktop_set_style (_desktop, css, true, true); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Unset fill"), INKSCAPE_ICON("dialog-fill-and-stroke")); + +} + +void SelectedStyle::on_stroke_unset() { + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_unset_property (css, "stroke"); + sp_repr_css_unset_property (css, "stroke-opacity"); + sp_repr_css_unset_property (css, "stroke-width"); + sp_repr_css_unset_property (css, "stroke-miterlimit"); + sp_repr_css_unset_property (css, "stroke-linejoin"); + sp_repr_css_unset_property (css, "stroke-linecap"); + sp_repr_css_unset_property (css, "stroke-dashoffset"); + sp_repr_css_unset_property (css, "stroke-dasharray"); + sp_desktop_set_style (_desktop, css, true, true); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Unset stroke"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_fill_opaque() { + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_set_property (css, "fill-opacity", "1"); + sp_desktop_set_style (_desktop, css, true); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Make fill opaque"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_stroke_opaque() { + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_set_property (css, "stroke-opacity", "1"); + sp_desktop_set_style (_desktop, css, true); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Make fill opaque"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_fill_lastused() { + SPCSSAttr *css = sp_repr_css_attr_new (); + guint32 color = sp_desktop_get_color(_desktop, true); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), color); + sp_repr_css_set_property (css, "fill", c); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Apply last set color to fill"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_stroke_lastused() { + SPCSSAttr *css = sp_repr_css_attr_new (); + guint32 color = sp_desktop_get_color(_desktop, false); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), color); + sp_repr_css_set_property (css, "stroke", c); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Apply last set color to stroke"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_fill_lastselected() { + SPCSSAttr *css = sp_repr_css_attr_new (); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), _lastselected[SS_FILL]); + sp_repr_css_set_property (css, "fill", c); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Apply last selected color to fill"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_stroke_lastselected() { + SPCSSAttr *css = sp_repr_css_attr_new (); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), _lastselected[SS_STROKE]); + sp_repr_css_set_property (css, "stroke", c); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Apply last selected color to stroke"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_fill_invert() { + SPCSSAttr *css = sp_repr_css_attr_new (); + guint32 color = _thisselected[SS_FILL]; + gchar c[64]; + if (_mode[SS_FILL] == SS_LGRADIENT || _mode[SS_FILL] == SS_RGRADIENT) { + sp_gradient_invert_selected_gradients(_desktop, Inkscape::FOR_FILL); + return; + + } + + if (_mode[SS_FILL] != SS_COLOR) return; + sp_svg_write_color (c, sizeof(c), + SP_RGBA32_U_COMPOSE( + (255 - SP_RGBA32_R_U(color)), + (255 - SP_RGBA32_G_U(color)), + (255 - SP_RGBA32_B_U(color)), + SP_RGBA32_A_U(color) + ) + ); + sp_repr_css_set_property (css, "fill", c); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Invert fill"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_stroke_invert() { + SPCSSAttr *css = sp_repr_css_attr_new (); + guint32 color = _thisselected[SS_STROKE]; + gchar c[64]; + if (_mode[SS_STROKE] == SS_LGRADIENT || _mode[SS_STROKE] == SS_RGRADIENT) { + sp_gradient_invert_selected_gradients(_desktop, Inkscape::FOR_STROKE); + return; + } + if (_mode[SS_STROKE] != SS_COLOR) return; + sp_svg_write_color (c, sizeof(c), + SP_RGBA32_U_COMPOSE( + (255 - SP_RGBA32_R_U(color)), + (255 - SP_RGBA32_G_U(color)), + (255 - SP_RGBA32_B_U(color)), + SP_RGBA32_A_U(color) + ) + ); + sp_repr_css_set_property (css, "stroke", c); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Invert stroke"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_fill_white() { + SPCSSAttr *css = sp_repr_css_attr_new (); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), 0xffffffff); + sp_repr_css_set_property (css, "fill", c); + sp_repr_css_set_property (css, "fill-opacity", "1"); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("White fill"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_stroke_white() { + SPCSSAttr *css = sp_repr_css_attr_new (); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), 0xffffffff); + sp_repr_css_set_property (css, "stroke", c); + sp_repr_css_set_property (css, "stroke-opacity", "1"); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("White stroke"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_fill_black() { + SPCSSAttr *css = sp_repr_css_attr_new (); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), 0x000000ff); + sp_repr_css_set_property (css, "fill", c); + sp_repr_css_set_property (css, "fill-opacity", "1.0"); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Black fill"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_stroke_black() { + SPCSSAttr *css = sp_repr_css_attr_new (); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), 0x000000ff); + sp_repr_css_set_property (css, "stroke", c); + sp_repr_css_set_property (css, "stroke-opacity", "1.0"); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Black stroke"), INKSCAPE_ICON("dialog-fill-and-stroke")); +} + +void SelectedStyle::on_fill_copy() { + if (_mode[SS_FILL] == SS_COLOR) { + gchar c[64]; + sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]); + Glib::ustring text; + text += c; + if (!text.empty()) { + Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get(); + refClipboard->set_text(text); + } + } +} + +void SelectedStyle::on_stroke_copy() { + if (_mode[SS_STROKE] == SS_COLOR) { + gchar c[64]; + sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]); + Glib::ustring text; + text += c; + if (!text.empty()) { + Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get(); + refClipboard->set_text(text); + } + } +} + +void SelectedStyle::on_fill_paste() { + Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get(); + Glib::ustring const text = refClipboard->wait_for_text(); + + if (!text.empty()) { + guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity + if (color == 0x000000ff) // failed to parse color string + return; + + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_set_property (css, "fill", text.c_str()); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Paste fill"), INKSCAPE_ICON("dialog-fill-and-stroke")); + } +} + +void SelectedStyle::on_stroke_paste() { + Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get(); + Glib::ustring const text = refClipboard->wait_for_text(); + + if (!text.empty()) { + guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity + if (color == 0x000000ff) // failed to parse color string + return; + + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_set_property (css, "stroke", text.c_str()); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Paste stroke"), INKSCAPE_ICON("dialog-fill-and-stroke")); + } +} + +void SelectedStyle::on_fillstroke_swap() { + _desktop->getSelection()->swapFillStroke(); +} + +void SelectedStyle::on_fill_edit() { + if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop)) + fs->showPageFill(); +} + +void SelectedStyle::on_stroke_edit() { + if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop)) + fs->showPageStrokePaint(); +} + +bool +SelectedStyle::on_fill_click(GdkEventButton *event) +{ + if (event->button == 1) { // click, open fill&stroke + + if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop)) + fs->showPageFill(); + + } else if (event->button == 3) { // right-click, popup menu + _popup[SS_FILL].popup_at_pointer(reinterpret_cast<GdkEvent *>(event)); + } else if (event->button == 2) { // middle click, toggle none/lastcolor + if (_mode[SS_FILL] == SS_NONE) { + on_fill_lastused(); + } else { + on_fill_remove(); + } + } + return true; +} + +bool +SelectedStyle::on_stroke_click(GdkEventButton *event) +{ + if (event->button == 1) { // click, open fill&stroke + if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop)) + fs->showPageStrokePaint(); + } else if (event->button == 3) { // right-click, popup menu + _popup[SS_STROKE].popup_at_pointer(reinterpret_cast<GdkEvent *>(event)); + } else if (event->button == 2) { // middle click, toggle none/lastcolor + if (_mode[SS_STROKE] == SS_NONE) { + on_stroke_lastused(); + } else { + on_stroke_remove(); + } + } + return true; +} + +bool +SelectedStyle::on_sw_click(GdkEventButton *event) +{ + if (event->button == 1) { // click, open fill&stroke + if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop)) + fs->showPageStrokeStyle(); + } else if (event->button == 3) { // right-click, popup menu + _popup_sw.popup_at_pointer(reinterpret_cast<GdkEvent *>(event)); + } else if (event->button == 2) { // middle click, toggle none/lastwidth? + // + } + return true; +} + +bool +SelectedStyle::on_opacity_click(GdkEventButton *event) +{ + if (event->button == 2) { // middle click + const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1"); + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_set_property (css, "opacity", opacity); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Change opacity"), INKSCAPE_ICON("dialog-fill-and-stroke")); + return true; + } + + return false; +} + +void SelectedStyle::on_popup_units(Inkscape::Util::Unit const *unit) { + _sw_unit = unit; + update(); +} + +void SelectedStyle::on_popup_preset(int i) { + SPCSSAttr *css = sp_repr_css_attr_new (); + gdouble w; + if (_sw_unit) { + w = Inkscape::Util::Quantity::convert(_sw_presets[i], _sw_unit, "px"); + } else { + w = _sw_presets[i]; + } + Inkscape::CSSOStringStream os; + os << w; + sp_repr_css_set_property (css, "stroke-width", os.str().c_str()); + // FIXME: update dash patterns! + sp_desktop_set_style (_desktop, css, true); + sp_repr_css_attr_unref (css); + DocumentUndo::done(_desktop->getDocument(), _("Change stroke width"), INKSCAPE_ICON("swatches")); +} + +void +SelectedStyle::update() +{ + if (_desktop == nullptr) + return; + + // create temporary style + SPStyle query(_desktop->getDocument()); + + for (int i = SS_FILL; i <= SS_STROKE; i++) { + Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place; + Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place; + + place->remove(); + flag_place->remove(); + + clearTooltip(*place); + clearTooltip(*flag_place); + + _mode[i] = SS_NA; + _paintserver_id[i].clear(); + + _popup_copy[i].set_sensitive(false); + + // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection + int result = sp_desktop_query_style (_desktop, &query, + (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE); + switch (result) { + case QUERY_STYLE_NOTHING: + place->add(_na[i]); + place->set_tooltip_text(__na[i]); + _mode[i] = SS_NA; + if ( _dropEnabled[i] ) { + gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) ); + _dropEnabled[i] = false; + } + break; + case QUERY_STYLE_SINGLE: + case QUERY_STYLE_MULTIPLE_AVERAGED: + case QUERY_STYLE_MULTIPLE_SAME: + if ( !_dropEnabled[i] ) { + gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()), + GTK_DEST_DEFAULT_ALL, + ui_drop_target_entries, + nui_drop_target_entries, + GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) ); + _dropEnabled[i] = true; + } + SPIPaint *paint; + if (i == SS_FILL) { + paint = &(query.fill); + } else { + paint = &(query.stroke); + } + if (paint->set && paint->isPaintserver()) { + SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (&query) : SP_STYLE_STROKE_SERVER (&query); + if ( server ) { + Inkscape::XML::Node *srepr = server->getRepr(); + _paintserver_id[i] += "url(#"; + _paintserver_id[i] += srepr->attribute("id"); + _paintserver_id[i] += ")"; + + if (SP_IS_LINEARGRADIENT(server)) { + SPGradient *vector = SP_GRADIENT(server)->getVector(); + _gradient_preview_l[i]->set_gradient(vector); + place->add(_gradient_box_l[i]); + place->set_tooltip_text(__lgradient[i]); + _mode[i] = SS_LGRADIENT; + } else if (SP_IS_RADIALGRADIENT(server)) { + SPGradient *vector = SP_GRADIENT(server)->getVector(); + _gradient_preview_r[i]->set_gradient(vector); + place->add(_gradient_box_r[i]); + place->set_tooltip_text(__rgradient[i]); + _mode[i] = SS_RGRADIENT; +#ifdef WITH_MESH + } else if (SP_IS_MESHGRADIENT(server)) { + SPGradient *array = SP_GRADIENT(server)->getArray(); + _gradient_preview_m[i]->set_gradient(array); + place->add(_gradient_box_m[i]); + place->set_tooltip_text(__mgradient[i]); + _mode[i] = SS_MGRADIENT; +#endif + } else if (SP_IS_PATTERN(server)) { + place->add(_pattern[i]); + place->set_tooltip_text(__pattern[i]); + _mode[i] = SS_PATTERN; + } else if (SP_IS_HATCH(server)) { + place->add(_hatch[i]); + place->set_tooltip_text(__hatch[i]); + _mode[i] = SS_HATCH; + } + } else { + g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__); + } + } else if (paint->set && paint->isColor()) { + guint32 color = paint->value.color.toRGBA32( + SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query.fill_opacity.value : query.stroke_opacity.value)); + _lastselected[i] = _thisselected[i]; + _thisselected[i] = color; // include opacity + ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color); + _color_preview[i]->show_all(); + place->add(*_color_preview[i]); + gchar c_string[64]; + g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color)); + place->set_tooltip_text(__color[i] + ": " + c_string + _(", drag to adjust, middle-click to remove")); + _mode[i] = SS_COLOR; + _popup_copy[i].set_sensitive(true); + + } else if (paint->set && paint->isNone()) { + place->add(_none[i]); + place->set_tooltip_text(__none[i]); + _mode[i] = SS_NONE; + } else if (!paint->set) { + place->add(_unset[i]); + place->set_tooltip_text(__unset[i]); + _mode[i] = SS_UNSET; + } + if (result == QUERY_STYLE_MULTIPLE_AVERAGED) { + flag_place->add(_averaged[i]); + flag_place->set_tooltip_text(__averaged[i]); + } else if (result == QUERY_STYLE_MULTIPLE_SAME) { + flag_place->add(_multiple[i]); + flag_place->set_tooltip_text(__multiple[i]); + } + break; + case QUERY_STYLE_MULTIPLE_DIFFERENT: + place->add(_many[i]); + place->set_tooltip_text(__many[i]); + _mode[i] = SS_MANY; + break; + default: + break; + } + } + +// Now query opacity + clearTooltip(_opacity_place); + clearTooltip(_opacity_sb); + + int result = sp_desktop_query_style (_desktop, &query, QUERY_STYLE_PROPERTY_MASTEROPACITY); + + switch (result) { + case QUERY_STYLE_NOTHING: + _opacity_place.set_tooltip_text(_("Nothing selected")); + _opacity_sb.set_tooltip_text(_("Nothing selected")); + _opacity_sb.set_sensitive(false); + break; + case QUERY_STYLE_SINGLE: + case QUERY_STYLE_MULTIPLE_AVERAGED: + case QUERY_STYLE_MULTIPLE_SAME: + _opacity_place.set_tooltip_text(_("Opacity (%)")); + _opacity_sb.set_tooltip_text(_("Opacity (%)")); + if (_opacity_blocked) break; + _opacity_blocked = true; + _opacity_sb.set_sensitive(true); + _opacity_adjustment->set_value(SP_SCALE24_TO_FLOAT(query.opacity.value) * 100); + _opacity_blocked = false; + break; + } + +// Now query stroke_width + int result_sw = sp_desktop_query_style (_desktop, &query, QUERY_STYLE_PROPERTY_STROKEWIDTH); + switch (result_sw) { + case QUERY_STYLE_NOTHING: + _stroke_width.set_markup(""); + current_stroke_width = 0; + break; + case QUERY_STYLE_SINGLE: + case QUERY_STYLE_MULTIPLE_AVERAGED: + case QUERY_STYLE_MULTIPLE_SAME: + { + if (query.stroke_extensions.hairline) { + _stroke_width.set_markup(_("Hairline")); + auto str = Glib::ustring::compose(_("Stroke width: %1"), _("Hairline")); + _stroke_width_place.set_tooltip_text(str); + } else { + double w; + if (_sw_unit) { + w = Inkscape::Util::Quantity::convert(query.stroke_width.computed, "px", _sw_unit); + } else { + w = query.stroke_width.computed; + } + current_stroke_width = w; + + { + gchar *str = g_strdup_printf(" %#.3g", w); + if (str[strlen(str) - 1] == ',' || str[strlen(str) - 1] == '.') { + str[strlen(str)-1] = '\0'; + } + _stroke_width.set_markup(str); + g_free (str); + } + { + gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"), + w, + _sw_unit? _sw_unit->abbr.c_str() : "px", + (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)? + _(" (averaged)") : ""); + _stroke_width_place.set_tooltip_text(str); + g_free (str); + } + } + break; + } + default: + break; + } +} + +void SelectedStyle::opacity_0() {_opacity_sb.set_value(0);} +void SelectedStyle::opacity_025() {_opacity_sb.set_value(25);} +void SelectedStyle::opacity_05() {_opacity_sb.set_value(50);} +void SelectedStyle::opacity_075() {_opacity_sb.set_value(75);} +void SelectedStyle::opacity_1() {_opacity_sb.set_value(100);} + +void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) { + + std::vector<Gtk::Widget *> children = menu->get_children(); + for (auto iter : children) { + menu->remove(*iter); + } + + { + Gtk::MenuItem *item = new Gtk::MenuItem; + item->add(*(new Gtk::Label(_("0 (transparent)"), Gtk::ALIGN_START, Gtk::ALIGN_START))); + item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 )); + menu->add(*item); + } + { + Gtk::MenuItem *item = new Gtk::MenuItem; + item->add(*(new Gtk::Label("25%", Gtk::ALIGN_START, Gtk::ALIGN_START))); + item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 )); + menu->add(*item); + } + { + Gtk::MenuItem *item = new Gtk::MenuItem; + item->add(*(new Gtk::Label("50%", Gtk::ALIGN_START, Gtk::ALIGN_START))); + item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 )); + menu->add(*item); + } + { + Gtk::MenuItem *item = new Gtk::MenuItem; + item->add(*(new Gtk::Label("75%", Gtk::ALIGN_START, Gtk::ALIGN_START))); + item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 )); + menu->add(*item); + } + { + Gtk::MenuItem *item = new Gtk::MenuItem; + item->add(*(new Gtk::Label(_("100% (opaque)"), Gtk::ALIGN_START, Gtk::ALIGN_START))); + item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 )); + menu->add(*item); + } + + menu->show_all(); +} + +void SelectedStyle::on_opacity_changed () +{ + g_return_if_fail(_desktop); // TODO this shouldn't happen! + if (_opacity_blocked) + return; + _opacity_blocked = true; + SPCSSAttr *css = sp_repr_css_attr_new (); + Inkscape::CSSOStringStream os; + os << CLAMP ((_opacity_adjustment->get_value() / 100), 0.0, 1.0); + sp_repr_css_set_property (css, "opacity", os.str().c_str()); + sp_desktop_set_style (_desktop, css); + sp_repr_css_attr_unref (css); + DocumentUndo::maybeDone(_desktop->getDocument(), "fillstroke:opacity", _("Change opacity"), INKSCAPE_ICON("dialog-fill-and-stroke")); + _opacity_blocked = false; +} + +/* ============================================= RotateableSwatch */ + +RotateableSwatch::RotateableSwatch(SelectedStyle *parent, guint mode) + : fillstroke(mode) + , parent(parent) +{ +} + +RotateableSwatch::~RotateableSwatch() = default; + +double +RotateableSwatch::color_adjust(float *hsla, double by, guint32 cc, guint modifier) +{ + SPColor::rgb_to_hsl_floatv (hsla, SP_RGBA32_R_F(cc), SP_RGBA32_G_F(cc), SP_RGBA32_B_F(cc)); + hsla[3] = SP_RGBA32_A_F(cc); + double diff = 0; + if (modifier == 2) { // saturation + double old = hsla[1]; + if (by > 0) { + hsla[1] += by * (1 - hsla[1]); + } else { + hsla[1] += by * (hsla[1]); + } + diff = hsla[1] - old; + } else if (modifier == 1) { // lightness + double old = hsla[2]; + if (by > 0) { + hsla[2] += by * (1 - hsla[2]); + } else { + hsla[2] += by * (hsla[2]); + } + diff = hsla[2] - old; + } else if (modifier == 3) { // alpha + double old = hsla[3]; + hsla[3] += by/2; + if (hsla[3] < 0) { + hsla[3] = 0; + } else if (hsla[3] > 1) { + hsla[3] = 1; + } + diff = hsla[3] - old; + } else { // hue + double old = hsla[0]; + hsla[0] += by/2; + while (hsla[0] < 0) + hsla[0] += 1; + while (hsla[0] > 1) + hsla[0] -= 1; + diff = hsla[0] - old; + } + + float rgb[3]; + SPColor::hsl_to_rgb_floatv (rgb, hsla[0], hsla[1], hsla[2]); + + gchar c[64]; + sp_svg_write_color (c, sizeof(c), + SP_RGBA32_U_COMPOSE( + (SP_COLOR_F_TO_U(rgb[0])), + (SP_COLOR_F_TO_U(rgb[1])), + (SP_COLOR_F_TO_U(rgb[2])), + 0xff + ) + ); + + SPCSSAttr *css = sp_repr_css_attr_new (); + + if (modifier == 3) { // alpha + Inkscape::CSSOStringStream osalpha; + osalpha << hsla[3]; + sp_repr_css_set_property(css, (fillstroke == SS_FILL) ? "fill-opacity" : "stroke-opacity", osalpha.str().c_str()); + } else { + sp_repr_css_set_property (css, (fillstroke == SS_FILL) ? "fill" : "stroke", c); + } + sp_desktop_set_style (parent->getDesktop(), css); + sp_repr_css_attr_unref (css); + return diff; +} + +void +RotateableSwatch::do_motion(double by, guint modifier) { + if (parent->_mode[fillstroke] != SS_COLOR) + return; + + if (!scrolling && !cr_set) { + + std::string cursor_filename = "adjust_hue.svg"; + if (modifier == 2) { + cursor_filename = "adjust_saturation.svg"; + } else if (modifier == 1) { + cursor_filename = "adjust_lightness.svg"; + } else if (modifier == 3) { + cursor_filename = "adjust_alpha.svg"; + } + + auto window = get_window(); + auto cursor = load_svg_cursor(get_display(), window, cursor_filename); + get_window()->set_cursor(cursor); + } + + guint32 cc; + if (!startcolor_set) { + cc = startcolor = parent->_thisselected[fillstroke]; + startcolor_set = true; + } else { + cc = startcolor; + } + + float hsla[4]; + double diff = 0; + + diff = color_adjust(hsla, by, cc, modifier); + + if (modifier == 3) { // alpha + DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust alpha")), INKSCAPE_ICON("dialog-fill-and-stroke")); + double ch = hsla[3]; + parent->getDesktop()->event_context->message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>alpha</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Ctrl</b> to adjust lightness, with <b>Shift</b> to adjust saturation, without modifiers to adjust hue"), ch - diff, ch, diff); + + } else if (modifier == 2) { // saturation + DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust saturation")), INKSCAPE_ICON("dialog-fill-and-stroke")); + double ch = hsla[1]; + parent->getDesktop()->event_context->message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>saturation</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Ctrl</b> to adjust lightness, with <b>Alt</b> to adjust alpha, without modifiers to adjust hue"), ch - diff, ch, diff); + + } else if (modifier == 1) { // lightness + DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust lightness")), INKSCAPE_ICON("dialog-fill-and-stroke")); + double ch = hsla[2]; + parent->getDesktop()->event_context->message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>lightness</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Shift</b> to adjust saturation, with <b>Alt</b> to adjust alpha, without modifiers to adjust hue"), ch - diff, ch, diff); + + } else { // hue + DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust hue")), INKSCAPE_ICON("dialog-fill-and-stroke")); + double ch = hsla[0]; + parent->getDesktop()->event_context->message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>hue</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Shift</b> to adjust saturation, with <b>Alt</b> to adjust alpha, with <b>Ctrl</b> to adjust lightness"), ch - diff, ch, diff); + } +} + + +void +RotateableSwatch::do_scroll(double by, guint modifier) { + do_motion(by/30.0, modifier); + do_release(by/30.0, modifier); +} + +void +RotateableSwatch::do_release(double by, guint modifier) { + if (parent->_mode[fillstroke] != SS_COLOR) + return; + + float hsla[4]; + color_adjust(hsla, by, startcolor, modifier); + + if (cr_set) { + get_window()->set_cursor(); // Use parent window cursor. + cr_set = false; + } + + if (modifier == 3) { // alpha + DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust alpha"), INKSCAPE_ICON("dialog-fill-and-stroke")); + + } else if (modifier == 2) { // saturation + DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust saturation"), INKSCAPE_ICON("dialog-fill-and-stroke")); + + } else if (modifier == 1) { // lightness + DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust lightness"), INKSCAPE_ICON("dialog-fill-and-stroke")); + + } else { // hue + DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust hue"), INKSCAPE_ICON("dialog-fill-and-stroke")); + } + + if (!strcmp(undokey, "ssrot1")) { + undokey = "ssrot2"; + } else { + undokey = "ssrot1"; + } + + parent->getDesktop()->event_context->message_context->clear(); + startcolor_set = false; +} + +/* ============================================= RotateableStrokeWidth */ + +RotateableStrokeWidth::RotateableStrokeWidth(SelectedStyle *parent) : + parent(parent), + startvalue(0), + startvalue_set(false), + undokey("swrot1") +{ +} + +RotateableStrokeWidth::~RotateableStrokeWidth() = default; + +double +RotateableStrokeWidth::value_adjust(double current, double by, guint /*modifier*/, bool final) +{ + double newval; + // by is -1..1 + double max_f = 50; // maximum width is (current * max_f), minimum - zero + newval = current * (std::exp(std::log(max_f-1) * (by+1)) - 1) / (max_f-2); + + SPCSSAttr *css = sp_repr_css_attr_new (); + if (final && newval < 1e-6) { + // if dragged into zero and this is the final adjust on mouse release, delete stroke; + // if it's not final, leave it a chance to increase again (which is not possible with "none") + sp_repr_css_set_property (css, "stroke", "none"); + } else { + newval = Inkscape::Util::Quantity::convert(newval, parent->_sw_unit, "px"); + Inkscape::CSSOStringStream os; + os << newval; + sp_repr_css_set_property (css, "stroke-width", os.str().c_str()); + } + + sp_desktop_set_style (parent->getDesktop(), css); + sp_repr_css_attr_unref (css); + return newval - current; +} + +void +RotateableStrokeWidth::do_motion(double by, guint modifier) { + + // if this is the first motion after a mouse grab, remember the current width + if (!startvalue_set) { + startvalue = parent->current_stroke_width; + // if it's 0, adjusting (which uses multiplication) will not be able to change it, so we + // cheat and provide a non-zero value + if (startvalue == 0) + startvalue = 1; + startvalue_set = true; + } + + if (modifier == 3) { // Alt, do nothing + } else { + double diff = value_adjust(startvalue, by, modifier, false); + DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust stroke width")), INKSCAPE_ICON("dialog-fill-and-stroke")); + parent->getDesktop()->event_context->message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>stroke width</b>: was %.3g, now <b>%.3g</b> (diff %.3g)"), startvalue, startvalue + diff, diff); + } +} + +void +RotateableStrokeWidth::do_release(double by, guint modifier) { + + if (modifier == 3) { // do nothing + + } else { + value_adjust(startvalue, by, modifier, true); + startvalue_set = false; + DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust stroke width")), INKSCAPE_ICON("dialog-fill-and-stroke")); + } + + if (!strcmp(undokey, "swrot1")) { + undokey = "swrot2"; + } else { + undokey = "swrot1"; + } + parent->getDesktop()->event_context->message_context->clear(); +} + +void +RotateableStrokeWidth::do_scroll(double by, guint modifier) { + do_motion(by/10.0, modifier); + startvalue_set = false; +} + +Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop) +{ + desktop->getContainer()->new_dialog("FillStroke"); + return dynamic_cast<Dialog::FillAndStroke *>(desktop->getContainer()->get_dialog("FillStroke")); +} + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +/* + 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 : |