Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
gradient-toolbar.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors:
7 * bulia byak <bulia@dr.com>
8 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
9 * Abhishek Sharma
10 * Vaibhav Malik <vaibhavmalik2018@gmail.com>
11 *
12 * Copyright (C) 2007 Johan Engelen
13 * Copyright (C) 2005 authors
14 *
15 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
16 */
17
18#include "gradient-toolbar.h"
19
20#include <glibmm/i18n.h>
21#include <gtkmm/button.h>
22#include <gtkmm/comboboxtext.h>
23#include <gtkmm/togglebutton.h>
24#include <map>
25
26#include "desktop.h"
27#include "document-undo.h"
28#include "document.h"
29#include "gradient-chemistry.h"
30#include "gradient-drag.h"
31#include "object/sp-defs.h"
34#include "object/sp-stop.h"
35#include "selection.h"
36#include "style.h"
37#include "ui/builder-utils.h"
38#include "ui/icon-names.h"
40#include "ui/util.h"
45
48
49namespace Inkscape::UI::Toolbar {
50namespace {
51
52void gr_apply_gradient_to_item(SPItem *item, SPGradient *gr, SPGradientType initialType, PaintTarget initialMode, PaintTarget mode)
53{
54 auto style = item->style;
55 bool is_fill = mode == Inkscape::FOR_FILL;
56 if (style
57 && (is_fill ? style->fill.isPaintserver() : style->stroke.isPaintserver())
58 && (is_fill ? is<SPGradient>(style->getFillPaintServer()) : is<SPGradient>(style->getStrokePaintServer())))
59 {
60 auto server = is_fill ? style->getFillPaintServer() : style->getStrokePaintServer();
61 if (is<SPLinearGradient>(server)) {
63 } else if ( is<SPRadialGradient>(server) ) {
65 }
66 } else if (initialMode == mode) {
67 sp_item_set_gradient(item, gr, initialType, mode);
68 }
69}
70
77void gr_apply_gradient(Selection *selection, GrDrag *drag, SPGradient *gr)
78{
79 auto prefs = Preferences::get();
80 auto initialType = static_cast<SPGradientType>(prefs->getInt("/tools/gradient/newgradient", SP_GRADIENT_TYPE_LINEAR));
81 auto initialMode = (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
82
83 // GRADIENTFIXME: make this work for multiple selected draggers.
84
85 // First try selected dragger
86 if (drag && !drag->selected.empty()) {
87 auto dragger = *drag->selected.begin();
88 for(auto draggable : dragger->draggables) { //for all draggables of dragger
89 gr_apply_gradient_to_item(draggable->item, gr, initialType, initialMode, draggable->fill_or_stroke);
90 }
91 return;
92 }
93
94 // If no drag or no dragger selected, act on selection
95 for (auto item : selection->items()) {
96 gr_apply_gradient_to_item(item, gr, initialType, initialMode, initialMode);
97 }
98}
99
100int gr_vector_list(Glib::RefPtr<Gtk::ListStore> store, SPDesktop *desktop,
101 bool selection_empty, SPGradient *gr_selected, bool gr_multi)
102{
103 int selected = -1;
104
105 // Get list of gradients in document.
106 SPDocument *document = desktop->getDocument();
107 std::vector<SPObject *> gradients = document->getResourceList( "gradient" );
108 std::map<Glib::ustring, SPGradient *> labels_gradients; // ordered map, so we sort by label.
109 for (auto gradient : gradients) {
110 auto grad = cast<SPGradient>(gradient);
111 if ( grad->hasStops() && !grad->isSolid() ) {
112 labels_gradients.emplace(gr_prepare_label(gradient), grad);
113 }
114 }
115
116 store->clear();
118 Gtk::TreeModel::Row row;
119
120 if (labels_gradients.empty()) {
121 // The document has no gradients
122 row = *(store->append());
123 row[columns.col_label ] = _("No gradient");
124 row[columns.col_tooltip ] = "";
125 row[columns.col_icon ] = "NotUsed";
126 row[columns.col_data ] = nullptr;
127 row[columns.col_sensitive] = true;
128 return selected;
129 }
130
131 if (selection_empty) {
132 // Document has gradients, but nothing is currently selected.
133 row = *(store->append());
134 row[columns.col_label ] = _("Nothing selected");
135 row[columns.col_tooltip ] = "";
136 row[columns.col_icon ] = "NotUsed";
137 row[columns.col_data ] = nullptr;
138 row[columns.col_sensitive] = true;
139 return selected;
140 }
141
142 // Document has gradients and a selection.
143
144 if (gr_selected == nullptr) {
145 row = *(store->append());
146 row[columns.col_label ] = _("No gradient");
147 row[columns.col_tooltip ] = "";
148 row[columns.col_icon ] = "NotUsed";
149 row[columns.col_data ] = nullptr;
150 row[columns.col_sensitive] = true;
151 }
152
153 if (gr_multi) {
154 row = *(store->append());
155 row[columns.col_label ] = _("Multiple gradients");
156 row[columns.col_tooltip ] = "";
157 row[columns.col_icon ] = "NotUsed";
158 row[columns.col_data ] = nullptr;
159 row[columns.col_sensitive] = true;
160 }
161
162 int idx = 0;
163 for (auto const &[label, gradient] : labels_gradients) {
164 Glib::RefPtr<Gdk::Pixbuf> pixbuf = sp_gradient_to_pixbuf_ref(gradient, 64, 16);
165
166 row = *(store->append());
167 row[columns.col_label ] = label;
168 row[columns.col_tooltip ] = "";
169 row[columns.col_icon ] = "NotUsed";
170 row[columns.col_pixbuf ] = pixbuf;
171 row[columns.col_data ] = gradient;
172 row[columns.col_sensitive] = true;
173
174 if (gradient == gr_selected) {
175 selected = idx;
176 }
177 idx ++;
178 }
179
180 if (gr_multi) {
181 selected = 0; // This will show "Multiple Gradients"
182 }
183
184 return selected;
185}
186
187/*
188 * Get the list of gradients of the selected desktop item
189 * These are the gradients containing the repeat settings, not the underlying "getVector" href linked gradient.
190 */
191void gr_get_dt_selected_gradient(Inkscape::Selection *selection, std::vector<SPGradient *> &gr_selected)
192{
193 SPGradient *gradient = nullptr;
194
195 auto itemlist= selection->items();
196 for(auto i=itemlist.begin();i!=itemlist.end();++i){
197 SPItem *item = *i;// get the items gradient, not the getVector() version
198 SPStyle *style = item->style;
199 SPPaintServer *server = nullptr;
200
201 if (style && (style->fill.isPaintserver())) {
202 server = item->style->getFillPaintServer();
203 }
204 if (style && (style->stroke.isPaintserver())) {
205 server = item->style->getStrokePaintServer();
206 }
207
208 if ( is<SPGradient>(server) ) {
209 gradient = cast<SPGradient>(server);
210 }
211 if (gradient && gradient->isSolid()) {
212 gradient = nullptr;
213 }
214
215 if (gradient) {
216 gr_selected.push_back(gradient);
217 }
218 }
219}
220
221/*
222 * Get the current selection and dragger status from the desktop
223 */
224void gr_read_selection( Inkscape::Selection *selection,
225 GrDrag *drag,
226 SPGradient *&gr_selected,
227 bool &gr_multi,
228 SPGradientSpread &spr_selected,
229 bool &spr_multi )
230{
231 if (drag && !drag->selected.empty()) {
232 // GRADIENTFIXME: make this work for more than one selected dragger?
233 GrDragger *dragger = *(drag->selected.begin());
234 for(auto draggable : dragger->draggables) { //for all draggables of dragger
235 SPGradient *gradient = sp_item_gradient_get_vector(draggable->item, draggable->fill_or_stroke);
236 SPGradientSpread spread = sp_item_gradient_get_spread(draggable->item, draggable->fill_or_stroke);
237
238 if (gradient && gradient->isSolid()) {
239 gradient = nullptr;
240 }
241
242 if (gradient && (gradient != gr_selected)) {
243 if (gr_selected) {
244 gr_multi = true;
245 } else {
246 gr_selected = gradient;
247 }
248 }
249 if (spread != spr_selected) {
250 if (spr_selected != SP_GRADIENT_SPREAD_UNDEFINED) {
251 spr_multi = true;
252 } else {
253 spr_selected = spread;
254 }
255 }
256 }
257 return;
258 }
259
260 // If no selected dragger, read desktop selection
261 auto itemlist= selection->items();
262 for(auto i=itemlist.begin();i!=itemlist.end();++i){
263 SPItem *item = *i;
264 SPStyle *style = item->style;
265
266 if (style && (style->fill.isPaintserver())) {
268 if ( is<SPGradient>(server) ) {
269 auto gradient = cast<SPGradient>(server)->getVector();
270 SPGradientSpread spread = cast<SPGradient>(server)->fetchSpread();
271
272 if (gradient && gradient->isSolid()) {
273 gradient = nullptr;
274 }
275
276 if (gradient && (gradient != gr_selected)) {
277 if (gr_selected) {
278 gr_multi = true;
279 } else {
280 gr_selected = gradient;
281 }
282 }
283 if (spread != spr_selected) {
284 if (spr_selected != SP_GRADIENT_SPREAD_UNDEFINED) {
285 spr_multi = true;
286 } else {
287 spr_selected = spread;
288 }
289 }
290 }
291 }
292 if (style && (style->stroke.isPaintserver())) {
294 if ( is<SPGradient>(server) ) {
295 auto gradient = cast<SPGradient>(server)->getVector();
296 SPGradientSpread spread = cast<SPGradient>(server)->fetchSpread();
297
298 if (gradient && gradient->isSolid()) {
299 gradient = nullptr;
300 }
301
302 if (gradient && (gradient != gr_selected)) {
303 if (gr_selected) {
304 gr_multi = true;
305 } else {
306 gr_selected = gradient;
307 }
308 }
309 if (spread != spr_selected) {
310 if (spr_selected != SP_GRADIENT_SPREAD_UNDEFINED) {
311 spr_multi = true;
312 } else {
313 spr_selected = spread;
314 }
315 }
316 }
317 }
318 }
319}
320
321} // namespace
322
326
327GradientToolbar::GradientToolbar(Glib::RefPtr<Gtk::Builder> const &builder)
328 : Toolbar{get_widget<Gtk::Box>(builder, "gradient-toolbar")}
329 , _linked_btn{get_widget<Gtk::ToggleButton>(builder, "_linked_btn")}
330 , _stops_reverse_btn{get_widget<Gtk::Button>(builder, "_stops_reverse_btn")}
331 , _offset_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_offset_item")}
332 , _stops_add_btn{get_widget<Gtk::Button>(builder, "_stops_add_btn")}
333 , _stops_delete_btn{get_widget<Gtk::Button>(builder, "_stops_delete_btn")}
334{
335 auto prefs = Preferences::get();
336
337 // Setup the spin buttons.
338 setup_derived_spin_button(_offset_item, "stopoffset", 0);
339
340 // Values auto-calculated.
342
343 // Configure mode buttons
344 int btn_index = 0;
345 for_each_child(get_widget<Gtk::Box>(builder, "new_type_buttons_box"), [&](Gtk::Widget &item){
346 auto &btn = dynamic_cast<Gtk::ToggleButton &>(item);
347 _new_type_buttons.push_back(&btn);
348 btn.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &GradientToolbar::new_type_changed), btn_index++));
350 });
351
352 int mode = prefs->getInt("/tools/gradient/newgradient", SP_GRADIENT_TYPE_LINEAR);
353 _new_type_buttons[mode == SP_GRADIENT_TYPE_LINEAR ? 0 : 1]->set_active(); // linear == 1, radial == 2
354
355 btn_index = 0;
356 for_each_child(get_widget<Gtk::Box>(builder, "new_fillstroke_buttons_box"), [&](Gtk::Widget &item){
357 auto &btn = dynamic_cast<Gtk::ToggleButton &>(item);
358 _new_fillstroke_buttons.push_back(&btn);
359 btn.signal_clicked().connect(
360 sigc::bind(sigc::mem_fun(*this, &GradientToolbar::new_fillstroke_changed), btn_index++));
362 });
363
364 auto fsmode = prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0 ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
365 _new_fillstroke_buttons[fsmode == Inkscape::FOR_FILL ? 0 : 1]->set_active();
366
367 /* Gradient Select list */
368 {
370 auto store = Gtk::ListStore::create(columns);
371 Gtk::TreeModel::Row row;
372 row = *(store->append());
373 row[columns.col_label ] = _("No gradient");
374 row[columns.col_tooltip ] = "";
375 row[columns.col_icon ] = "NotUsed";
376 row[columns.col_sensitive] = true;
377
378 _select_cb = UI::Widget::ComboToolItem::create(_("Select"), // Label
379 "", // Tooltip
380 "Not Used", // Icon
381 store ); // Tree store
382 _select_cb->use_icon( false );
383 _select_cb->use_pixbuf( true );
386 _select_cb->set_sensitive( false );
387
388 get_widget<Gtk::Box>(builder, "select_box").append(*_select_cb);
389 _select_cb->signal_changed().connect(sigc::mem_fun(*this, &GradientToolbar::gradient_changed));
390 }
391
392 // Configure the linked button.
393 _linked_btn.signal_toggled().connect(sigc::mem_fun(*this, &GradientToolbar::linked_changed));
394
395 bool linkedmode = prefs->getBool("/options/forkgradientvectors/value", true);
396 _linked_btn.set_active(!linkedmode);
397
398 // Configure the reverse button.
399 _stops_reverse_btn.signal_clicked().connect(sigc::mem_fun(*this, &GradientToolbar::reverse));
400 _stops_reverse_btn.set_sensitive(false);
401
402 // Gradient Spread type (how a gradient is drawn outside its nominal area)
403 {
405 Glib::RefPtr<Gtk::ListStore> store = Gtk::ListStore::create(columns);
406
407 std::vector<gchar*> spread_dropdown_items_list = {
408 const_cast<gchar *>(C_("Gradient repeat type", "None")),
409 _("Reflected"),
410 _("Direct")
411 };
412
413 for (auto item: spread_dropdown_items_list) {
414 Gtk::TreeModel::Row row = *(store->append());
415 row[columns.col_label ] = item;
416 row[columns.col_sensitive] = true;
417 }
418
419 _spread_cb = Gtk::manage(UI::Widget::ComboToolItem::create(_("Repeat"),
420 // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/pservers.html#LinearGradientSpreadMethodAttribute
421 _("Whether to fill with flat color beyond the ends of the gradient vector "
422 "(spreadMethod=\"pad\"), or repeat the gradient in the same direction "
423 "(spreadMethod=\"repeat\"), or repeat the gradient in alternating opposite "
424 "directions (spreadMethod=\"reflect\")"),
425 "Not Used", store));
427
429 _spread_cb->set_sensitive(false);
430
431 _spread_cb->signal_changed().connect(sigc::mem_fun(*this, &GradientToolbar::spread_changed));
432 get_widget<Gtk::Box>(builder, "spread_box").append(*_spread_cb);
433 }
434
435 // Gradient Stop list
436 {
438
439 auto store = Gtk::ListStore::create(columns);
440
441 Gtk::TreeModel::Row row;
442
443 row = *(store->append());
444 row[columns.col_label ] = _("No stops");
445 row[columns.col_tooltip ] = "";
446 row[columns.col_icon ] = "NotUsed";
447 row[columns.col_sensitive] = true;
448
449 _stop_cb =
450 UI::Widget::ComboToolItem::create(_("Stops" ), // Label
451 "", // Tooltip
452 "Not Used", // Icon
453 store ); // Tree store
454
455 _stop_cb->use_icon( false );
456 _stop_cb->use_pixbuf( true );
457 _stop_cb->use_group_label( true );
458 _stop_cb->set_active( 0 );
459 _stop_cb->set_sensitive( false );
460
461 get_widget<Gtk::Box>(builder, "stop_box").append(*_stop_cb);
462 _stop_cb->signal_changed().connect(sigc::mem_fun(*this, &GradientToolbar::stop_changed));
463 }
464
465 // Configure the stops add button.
466 _stops_add_btn.signal_clicked().connect(sigc::mem_fun(*this, &GradientToolbar::add_stop));
467 _stops_add_btn.set_sensitive(false);
468
469 // Configure the stops add button.
470 _stops_delete_btn.signal_clicked().connect(sigc::mem_fun(*this, &GradientToolbar::remove_stop));
471 _stops_delete_btn.set_sensitive(false);
472
474}
475
477
479{
480 if (_desktop) {
481 _connection_changed.disconnect();
482 _connection_modified.disconnect();
484 _connection_defs_release.disconnect();
485 _connection_defs_modified.disconnect();
486 }
487
489
490 if (_desktop) {
491 auto sel = desktop->getSelection();
492 auto document = desktop->getDocument();
493
494 // connect to selection modified and changed signals
495 _connection_changed = sel->connectChanged([this] (auto) { _update(); });
496 _connection_modified = sel->connectModified([this] (auto, auto) { _update(); });
498 _update();
499
500 // connect to release and modified signals of the defs (i.e. when someone changes gradient)
501 _connection_defs_release = document->getDefs()->connectRelease([this] (auto) { _update(); });
502 _connection_defs_modified = document->getDefs()->connectModified([this] (auto, auto) { _update(); });
503 }
504}
505
506void GradientToolbar::setup_derived_spin_button(UI::Widget::SpinButton &btn, Glib::ustring const &name, double default_value)
507{
508 auto const prefs = Preferences::get();
509 auto const path = "/tools/gradient/" + name;
510 auto const val = prefs->getDouble(path, default_value);
511
512 auto adj = btn.get_adjustment();
513 adj->set_value(val);
514
515 adj->signal_value_changed().connect(sigc::mem_fun(*this, &GradientToolbar::stop_offset_adjustment_changed));
516
517 btn.set_sensitive(false);
518 btn.setDefocusTarget(this);
519}
520
522{
523 Preferences::get()->setInt("/tools/gradient/newgradient",
525}
526
528{
529 Preferences::get()->setInt("/tools/gradient/newfillorstroke", mode == 0 ? 1 : 0);
530}
531
532/*
533 * User selected a gradient from the combobox
534 */
536{
537 if (_blocker.pending()) {
538 return;
539 }
540
541 if (active < 0) {
542 return;
543 }
544
545 auto gr = get_selected_gradient();
546 if (!gr) {
547 return;
548 }
549
550 auto guard = _blocker.block();
551
553
554 auto selection = _desktop->getSelection();
555 auto ev = _desktop->getTool();
556
557 gr_apply_gradient(selection, ev ? ev->get_drag() : nullptr, gr);
558
559 DocumentUndo::done(_desktop->getDocument(), _("Assign gradient to object"), INKSCAPE_ICON("color-gradient"));
560}
561
566{
567 int active = _select_cb->get_active();
568
569 auto store = _select_cb->get_store();
570 auto row = store->children()[active];
572
573 void* pointer = row[columns.col_data];
574 SPGradient *gr = static_cast<SPGradient *>(pointer);
575
576 return gr;
577}
578
583{
584 if (_blocker.pending()) {
585 return;
586 }
587
588 auto guard = _blocker.block();
589
590 auto selection = _desktop->getSelection();
591 std::vector<SPGradient *> gradientList;
592 gr_get_dt_selected_gradient(selection, gradientList);
593
594 auto spread = static_cast<SPGradientSpread>(active);
595
596 if (!gradientList.empty()) {
597 for (auto item : gradientList) {
598 item->setSpread(spread);
599 item->updateRepr();
600 }
601 DocumentUndo::done(_desktop->getDocument(), _("Set gradient repeat"), INKSCAPE_ICON("color-gradient"));
602 }
603}
604
609{
610 if (_blocker.pending()) {
611 return;
612 }
613
614 auto guard = _blocker.block();
615
617}
618
620{
621 if (!_blocker.pending()) {
622 std::cerr << "select_dragger_by_stop: should be blocked!" << std::endl;
623 }
624
625 if (!ev || !gradient) {
626 return;
627 }
628
629 auto drag = ev->get_drag();
630 if (!drag) {
631 return;
632 }
633
634 drag->selectByStop(get_selected_stop(), false, true);
635
637}
638
643{
644 int active = _stop_cb->get_active();
645
646 auto store = _stop_cb->get_store();
647 auto row = store->children()[active];
649 void* pointer = row[columns.col_data];
650 return static_cast<SPStop *>(pointer);
651}
652
659{
660 if (!_blocker.pending()) {
661 std::cerr << "gr_stop_set_offset: should be blocked!" << std::endl;
662 }
663
664 auto stop = get_selected_stop();
665 if (!stop) {
666 // std::cerr << "gr_stop_set_offset: no stop!" << std::endl;
667 return;
668 }
669
670 SPStop *prev = nullptr;
671 prev = stop->getPrevStop();
672 auto adj = _offset_item.get_adjustment();
673 adj->set_lower(prev != nullptr ? prev->offset : 0);
674
675 SPStop *next = nullptr;
676 next = stop->getNextStop();
677 adj->set_lower(next != nullptr ? next->offset : 1.0);
678 adj->set_value(stop->offset);
679 _offset_item.set_sensitive(true);
680}
681
686{
687 if (_blocker.pending()) {
688 return;
689 }
690
691 auto guard = _blocker.block();
692
693 auto stop = get_selected_stop();
694 if (!stop) {
695 return;
696 }
697
698 stop->offset = _offset_item.get_adjustment()->get_value();
699 _offset_adj_changed = true; // checked to stop changing the selected stop after the update of the offset
700 stop->getRepr()->setAttributeCssDouble("offset", stop->offset);
701
702 DocumentUndo::maybeDone(stop->document, "gradient:stop:offset", _("Change gradient stop offset"), INKSCAPE_ICON("color-gradient"));
703}
704
709{
710 if (!_desktop) {
711 return;
712 }
713
714 auto selection = _desktop->getSelection();
715 if (!selection) {
716 return;
717 }
718
719 if (auto gt = dynamic_cast<Tools::GradientTool*>(_desktop->getTool())) {
720 gt->add_stops_between_selected_stops();
721 }
722}
723
728{
729 if (!_desktop) {
730 return;
731 }
732
733 auto selection = _desktop->getSelection(); // take from desktop, not from args
734 if (!selection) {
735 return;
736 }
737
738 auto ev = _desktop->getTool();
739 if (!ev) {
740 return;
741 }
742
743 auto drag = ev->get_drag();
744
745 if (drag) {
746 drag->deleteSelected();
747 }
748}
749
757
762{
763 bool active = _linked_btn.get_active();
764 if (active) {
765 _linked_btn.set_image_from_icon_name(INKSCAPE_ICON("object-locked"));
766 } else {
767 _linked_btn.set_image_from_icon_name(INKSCAPE_ICON("object-unlocked"));
768 }
769
770 Preferences::get()->setBool("/options/forkgradientvectors/value", !active);
771}
772
777{
778 if (_blocker.pending()) {
779 return;
780 }
781
782 if (!_desktop) {
783 return;
784 }
785
786 if (_offset_adj_changed) { // stops change of selection when offset update event is triggered
787 _offset_adj_changed = false;
788 return;
789 }
790
791 auto guard = _blocker.block();
792
793 auto selection = _desktop->getSelection();
794 if (selection) {
795
796 ToolBase *ev = _desktop->getTool();
797 GrDrag *drag = nullptr;
798 if (ev) {
799 drag = ev->get_drag();
800 }
801
802 SPGradient *gr_selected = nullptr;
804 bool gr_multi = false;
805 bool spr_multi = false;
806
807 gr_read_selection(selection, drag, gr_selected, gr_multi, spr_selected, spr_multi);
808
809 // Gradient selection menu
810 auto store = _select_cb->get_store();
811 int gradient = gr_vector_list (store, _desktop, selection->isEmpty(), gr_selected, gr_multi);
812
813 if (gradient < 0) {
814 // No selection or no gradients
816 _select_cb->set_sensitive(false);
817 } else {
818 // Single gradient or multiple gradients
819 _select_cb->set_active(gradient);
820 _select_cb->set_sensitive(true);
821 }
822
823 // Spread menu
824 _spread_cb->set_sensitive(gr_selected );
825 _spread_cb->set_active(gr_selected ? (int)spr_selected : 0);
826
827 _stops_add_btn.set_sensitive((gr_selected && !gr_multi && drag && !drag->selected.empty()));
828 _stops_delete_btn.set_sensitive((gr_selected && !gr_multi && drag && !drag->selected.empty()));
829 _stops_reverse_btn.set_sensitive((gr_selected != nullptr));
830
831 _stop_cb->set_sensitive(gr_selected && !gr_multi);
832 _offset_item.set_sensitive(!gr_multi);
833
834 update_stop_list(gr_selected, nullptr, gr_multi);
835 select_stop_by_draggers(gr_selected, ev);
836 }
837}
838
842int GradientToolbar::update_stop_list(SPGradient *gradient, SPStop *new_stop, bool gr_multi)
843{
844 if (!_blocker.pending()) {
845 std::cerr << "update_stop_list should be blocked!" << std::endl;
846 }
847
848 int selected = -1;
849
850 auto store = _stop_cb->get_store();
851 if (!store) {
852 return selected;
853 }
854
855 store->clear();
857 Gtk::TreeModel::Row row;
858
859 if (gr_multi) {
860 row = *(store->append());
861 row[columns.col_label ] = _("Multiple gradients");
862 row[columns.col_tooltip ] = "";
863 row[columns.col_icon ] = "NotUsed";
864 row[columns.col_data ] = nullptr;
865 row[columns.col_sensitive] = true;
866 selected = 0;
867 return selected;
868 }
869
870 if (!gradient) {
871 // No valid gradient
872 row = *(store->append());
873 row[columns.col_label ] = _("No gradient");
874 row[columns.col_tooltip ] = "";
875 row[columns.col_icon ] = "NotUsed";
876 row[columns.col_data ] = nullptr;
877 row[columns.col_sensitive] = true;
878 } else if (!gradient->hasStops()) {
879 // Has gradient but it has no stops
880 row = *(store->append());
881 row[columns.col_label ] = _("No stops in gradient");
882 row[columns.col_tooltip ] = "";
883 row[columns.col_icon ] = "NotUsed";
884 row[columns.col_data ] = nullptr;
885 row[columns.col_sensitive] = true;
886 } else {
887 // Gradient has stops
888 for (auto& ochild: gradient->children) {
889 if (is<SPStop>(&ochild)) {
890
891 auto stop = cast<SPStop>(&ochild);
892 Glib::RefPtr<Gdk::Pixbuf> pixbuf = sp_gradstop_to_pixbuf_ref(stop, 32, 16);
893
894 Inkscape::XML::Node *repr = reinterpret_cast<SPItem *>(&ochild)->getRepr();
895 Glib::ustring label = gr_ellipsize_text(repr->attribute("id"), 25);
896
897 row = *(store->append());
898 row[columns.col_label ] = label;
899 row[columns.col_tooltip ] = "";
900 row[columns.col_icon ] = "NotUsed";
901 row[columns.col_pixbuf ] = pixbuf;
902 row[columns.col_data ] = stop;
903 row[columns.col_sensitive] = true;
904 }
905 }
906 }
907
908 if (new_stop) {
909 selected = select_stop_in_list (gradient, new_stop);
910 }
911
912 return selected;
913}
914
919{
920 int i = 0;
921 for (auto& ochild: gradient->children) {
922 if (is<SPStop>(&ochild)) {
923 if (&ochild == new_stop) {
924 return i;
925 }
926 i++;
927 }
928 }
929 return -1;
930}
931
936{
937 if (!_blocker.pending()) {
938 std::cerr << "select_stop_by_draggers should be blocked!" << std::endl;
939 }
940
941 if (!ev || !gradient)
942 return;
943
944 SPGradient *vector = gradient->getVector();
945 if (!vector)
946 return;
947
948 GrDrag *drag = ev->get_drag();
949
950 if (!drag || drag->selected.empty()) {
953 return;
954 }
955
956 gint n = 0;
957 SPStop *stop = nullptr;
958 int selected = -1;
959
960 // For all selected draggers
961 for(auto dragger : drag->selected) {
962
963 // For all draggables of dragger
964 for(auto draggable : dragger->draggables) {
965
966 if (draggable->point_type != POINT_RG_FOCUS) {
967 n++;
968 if (n > 1) break;
969 }
970
971 stop = vector->getFirstStop();
972
973 switch (draggable->point_type) {
974 case POINT_LG_MID:
975 case POINT_RG_MID1:
976 case POINT_RG_MID2:
977 stop = sp_get_stop_i(vector, draggable->point_i);
978 break;
979 case POINT_LG_END:
980 case POINT_RG_R1:
981 case POINT_RG_R2:
982 stop = sp_last_stop(vector);
983 break;
984 default:
985 break;
986 }
987 }
988 if (n > 1) break;
989 }
990
991 if (n > 1) {
992 // Multiple stops selected
993 _offset_item.set_sensitive(false);
994
995 // Stop list always updated first... reinsert "Multiple stops" as first entry.
997 auto store = _stop_cb->get_store();
998
999 auto row = *(store->prepend());
1000 row[columns.col_label ] = _("Multiple stops");
1001 row[columns.col_tooltip ] = "";
1002 row[columns.col_icon ] = "NotUsed";
1003 row[columns.col_sensitive] = true;
1004 selected = 0;
1005
1006 } else {
1007 selected = select_stop_in_list(gradient, stop);
1008 }
1009
1010 if (selected < 0) {
1011 _stop_cb->set_active (0);
1012 _stop_cb->set_sensitive (false);
1013 } else {
1014 _stop_cb->set_active (selected);
1015 _stop_cb->set_sensitive (true);
1017 }
1018}
1019
1020} // namespace Inkscape::UI::Toolbar
1021
1022/*
1023 Local Variables:
1024 mode:c++
1025 c-file-style:"stroustrup"
1026 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1027 indent-tabs-mode:nil
1028 fill-column:99
1029 End:
1030*/
1031// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Gtk builder utilities.
Fragment store
Definition canvas.cpp:155
bool is(S const *s)
Equivalent to the boolean value of dynamic_cast<T const*>(...).
Definition cast.h:37
This is the root class of the gradient dragging machinery.
void selectByStop(SPStop *stop, bool add_to_selection=true, bool override=true)
Select draggers by stop.
std::set< GrDragger * > selected
void deleteSelected(bool just_one=false)
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
static void maybeDone(SPDocument *document, const gchar *keyconst, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:255
bool isEmpty()
Returns true if no items are selected.
static Preferences * get()
Access the singleton Preferences object.
void setInt(Glib::ustring const &pref_path, int value)
Set an integer value.
void setBool(Glib::ustring const &pref_path, bool value)
Set a Boolean value.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
void stop_set_offset()
Change desktop dragger selection to this stop.
void spread_changed(int active)
User selected a spread method from the combobox.
std::vector< Gtk::ToggleButton * > _new_fillstroke_buttons
void setDesktop(SPDesktop *desktop) override
UI::Widget::ComboToolItem * _spread_cb
int update_stop_list(SPGradient *gradient, SPStop *new_stop, bool gr_multi)
Construct stop list.
void remove_stop()
Remove stop from vector.
void select_dragger_by_stop(SPGradient *gradient, Tools::ToolBase *ev)
SPStop * get_selected_stop()
Get stop selected by menu.
UI::Widget::ComboToolItem * _select_cb
void stop_offset_adjustment_changed()
User changed the offset.
void select_stop_by_draggers(SPGradient *gradient, UI::Tools::ToolBase *ev)
Set stop in menu to match stops selected by draggers.
UI::Widget::ComboToolItem * _stop_cb
int select_stop_in_list(SPGradient *gradient, SPStop *new_stop)
Find position of new_stop in menu.
SPGradient * get_selected_gradient()
Return gradient selected in menu.
void linked_changed()
Lock or unlock links.
std::vector< Gtk::ToggleButton * > _new_type_buttons
void stop_changed(int active)
User selected a stop from the combobox.
void _update()
Core function, setup all the widgets whenever something changes on the desktop.
void setup_derived_spin_button(UI::Widget::SpinButton &btn, Glib::ustring const &name, double default_value)
Base class for all tool toolbars.
Definition toolbar.h:72
virtual void setDesktop(SPDesktop *desktop)
Definition toolbar.h:76
Base class for Event processors.
Definition tool-base.h:107
Gtk::TreeModelColumn< Glib::RefPtr< Gdk::Pixbuf > > col_pixbuf
Gtk::TreeModelColumn< void * > col_data
Gtk::TreeModelColumn< Glib::ustring > col_icon
Gtk::TreeModelColumn< Glib::ustring > col_tooltip
Gtk::TreeModelColumn< bool > col_sensitive
Gtk::TreeModelColumn< Glib::ustring > col_label
void use_group_label(bool use_group_label)
Glib::RefPtr< Gtk::ListStore > const & get_store()
static ComboToolItem * create(const Glib::ustring &label, const Glib::ustring &tooltip, const Glib::ustring &stock_id, Glib::RefPtr< Gtk::ListStore > store, bool has_entry=false)
sigc::signal< void(int)> signal_changed()
SpinButton widget, that allows entry of simple math expressions (also units, when linked with UnitMen...
Definition spinbutton.h:52
void setDefocusTarget(decltype(_defocus_target) target)
Definition spinbutton.h:127
void set_custom_numeric_menu_data(NumericMenuData &&custom_menu_data)
Interface for refcounted XML nodes.
Definition node.h:80
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
scoped_block block()
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
sigc::connection connect_gradient_stop_selected(sigc::slot< void(SPStop *)> const &slot)
Definition desktop.cpp:1349
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
Typed SVG document implementation.
Definition document.h:101
std::vector< SPObject * > const getResourceList(char const *key)
sigc::connection connectModified(ModifiedSignal::slot_type slot)
SPDefs * getDefs()
Return the main defs object for the document.
Definition document.cpp:246
Gradient.
Definition sp-gradient.h:86
SPStop * getFirstStop()
SPGradientSpread fetchSpread() const
Returns the effective spread of given gradient (climbing up the refs chain if needed).
bool hasStops() const
SPGradient * getVector(bool force_private=false)
Returns private vector of given gradient (the gradient at the end of the href chain which has stops),...
bool isSolid() const
Base class for visual SVG elements.
Definition sp-item.h:109
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
sigc::connection connectRelease(sigc::slot< void(SPObject *)> slot)
Connects to the release request signal.
Definition sp-object.h:237
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
sigc::connection connectModified(sigc::slot< void(SPObject *, unsigned int)> slot)
Connects to the modification notification signal.
Definition sp-object.h:705
ChildrenList children
Definition sp-object.h:907
Gradient stop.
Definition sp-stop.h:31
float offset
Definition sp-stop.h:38
SPStop * getNextStop()
Virtual write: write object attributes to repr.
Definition sp-stop.cpp:98
SPStop * getPrevStop()
Definition sp-stop.cpp:110
An SVG style object.
Definition style.h:45
SPPaintServer * getFillPaintServer()
Definition style.h:339
T< SPAttr::FILL, SPIPaint > fill
fill
Definition style.h:240
T< SPAttr::STROKE, SPIPaint > stroke
stroke
Definition style.h:247
SPPaintServer * getStrokePaintServer()
Definition style.h:343
A combobox that can be displayed in a toolbar.
Colors::Color stroke
Editable view implementation.
TODO: insert short description here.
SPStop * sp_get_stop_i(SPGradient *gradient, guint stop_i)
void sp_gradient_reverse_selected_gradients(SPDesktop *desktop)
SPGradient * sp_item_gradient_get_vector(SPItem *item, Inkscape::PaintTarget fill_or_stroke)
SPStop * sp_last_stop(SPGradient *gradient)
SPGradient * sp_gradient_ensure_vector_normalized(SPGradient *gr)
Either normalizes given gradient to vector, or returns fresh normalized vector - in latter case,...
SPGradientSpread sp_item_gradient_get_spread(SPItem *item, Inkscape::PaintTarget fill_or_stroke)
SPGradient * sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, Inkscape::PaintTarget fill_or_stroke)
Sets item fill or stroke to the gradient of the specified type with given vector, creating new privat...
Glib::RefPtr< Gdk::Pixbuf > sp_gradstop_to_pixbuf_ref(SPStop *stop, int width, int height)
Glib::RefPtr< Gdk::Pixbuf > sp_gradient_to_pixbuf_ref(SPGradient *gr, int width, int height)
Gradient drawing and editing tool.
Glib::ustring gr_ellipsize_text(Glib::ustring const &src, size_t maxlen)
Glib::ustring gr_prepare_label(SPObject *obj)
Macro for icon names used in Inkscape.
SPItem * item
Glib::ustring label
Definition desktop.h:50
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
Gtk::Widget * for_each_child(Gtk::Widget &widget, Func &&func, bool const plus_self=false, bool const recurse=false, int const level=0)
Call Func with a reference to each child of parent, until it returns _break.
Definition util.h:103
W & get_derived_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id, Args &&... args)
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
Helper class to stream background task notifications as a series of messages.
int mode
GList * items
SPGradientSpread
@ SP_GRADIENT_SPREAD_UNDEFINED
@ POINT_LG_END
Definition sp-gradient.h:48
@ POINT_RG_R2
Definition sp-gradient.h:52
@ POINT_RG_MID2
Definition sp-gradient.h:55
@ POINT_RG_MID1
Definition sp-gradient.h:54
@ POINT_RG_R1
Definition sp-gradient.h:51
@ POINT_LG_MID
Definition sp-gradient.h:49
@ POINT_RG_FOCUS
Definition sp-gradient.h:53
SPGradientType
Definition sp-gradient.h:33
@ SP_GRADIENT_TYPE_LINEAR
Definition sp-gradient.h:35
@ SP_GRADIENT_TYPE_RADIAL
Definition sp-gradient.h:36
TODO: insert short description here.
TODO: insert short description here.
TODO: insert short description here.
This class holds together a visible on-canvas knot and a list of draggables that need to be moved whe...
std::vector< GrDraggable * > draggables
SPStyle - a style object for SPItem objects.
SPDesktop * desktop
Glib::ustring name
Definition toolbars.cpp:55
Glib::RefPtr< Gtk::Builder > builder