Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
layer-properties.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/* Author:
7 * Bryce W. Harrington <bryce@bryceharrington.org>
8 * Andrius R. <knutux@gmail.com>
9 * Abhishek Sharma
10 *
11 * Copyright (C) 2004 Bryce Harrington
12 * Copyright (C) 2006 Andrius R.
13 *
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
17#include <glibmm/i18n.h>
18#include <glibmm/main.h>
19#include <gtkmm/eventcontroller.h>
20#include <gtkmm/eventcontrollerkey.h>
21#include <gtkmm/gestureclick.h>
22
23#include "layer-properties.h"
24#include "desktop.h"
25#include "document.h"
26#include "document-undo.h"
27#include "layer-manager.h"
28#include "message-stack.h"
29#include "object/sp-root.h"
30#include "preferences.h"
31#include "selection.h"
32#include "ui/controller.h"
33#include "ui/icon-names.h"
34#include "ui/tools/tool-base.h"
36
37namespace Inkscape::UI::Dialog {
38
40 : _type{type}
41 , _mainbox(Gtk::Orientation::VERTICAL)
42 , _close_button(_("_Cancel"), true)
43{
44 set_name("LayerPropertiesDialog");
45
46 set_child(_mainbox);
47 _mainbox.set_margin(2);
48 _mainbox.set_spacing(4);
49
50 _layout_table.set_row_spacing(8);
51 _layout_table.set_column_spacing(4);
52
53 // Layer name widgets
54 _layer_name_entry.set_activates_default(true);
55 _layer_name_label.set_label(_("Layer name:"));
56 _layer_name_label.set_halign(Gtk::Align::START);
57 _layer_name_label.set_valign(Gtk::Align::CENTER);
58
59 _layout_table.attach(_layer_name_label, 0, 0, 1, 1);
60
61 _layer_name_entry.set_halign(Gtk::Align::FILL);
62 _layer_name_entry.set_valign(Gtk::Align::FILL);
63 _layer_name_entry.set_hexpand();
64 _layout_table.attach(_layer_name_entry, 1, 0, 1, 1);
65
66 _layout_table.set_expand();
67 _mainbox.append(_layout_table);
68
69 // Buttons
70 _close_button.set_receives_default(true);
71
72 _apply_button.set_use_underline(true);
73 _apply_button.set_receives_default();
74
75 _close_button.signal_clicked().connect([this] { destroy(); });
76 _apply_button.signal_clicked().connect([this] { _apply(); });
77
78 _mainbox.append(_buttonbox);
79 _buttonbox.set_halign(Gtk::Align::END);
80 _buttonbox.set_homogeneous();
81 _buttonbox.set_spacing(4);
82
85
86 set_default_widget(_apply_button);
87}
88
94
97{
98 auto dialog = Gtk::manage(new LayerPropertiesDialog(type));
99
100 dialog->_setDesktop(desktop);
101 dialog->_setLayer(layer);
102
103 dialog->_setup();
104
105 dialog->set_modal(true);
106 desktop->setWindowTransient(*dialog);
107 dialog->property_destroy_with_parent() = true;
108
109 dialog->present();
110}
111
114{
115 switch (_type) {
117 _doCreate();
118 break;
119
121 _doMove();
122 break;
123
125 _doRename();
126 break;
127
129 default:
130 break;
131 }
132}
133
136{
138
139 if (_position_visible) {
140 int index = 0;
141 if (_layer_position_radio[1].get_active()) {
142 position = LPOS_CHILD;
143 index = 1;
144 } else if (_layer_position_radio[2].get_active()) {
145 position = LPOS_BELOW;
146 index = 2;
147 }
148 Preferences::get()->setInt("/dialogs/layerProp/addLayerPosition", index);
149 }
150
151 Glib::ustring name(_layer_name_entry.get_text());
152 if (name.empty()) {
153 return;
154 }
155
156 auto root = _desktop->getDocument()->getRoot();
157 SPObject *new_layer = Inkscape::create_layer(root, _layer, position);
158
159 if (!name.empty()) {
160 _desktop->layerManager().renameLayer(new_layer, name.c_str(), true);
161 }
164 DocumentUndo::done(_desktop->getDocument(), _("Add layer"), INKSCAPE_ICON("layer-new"));
165 _desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("New layer created."));
166}
167
170{
171 if (auto moveto = _selectedLayer()) {
172 _desktop->getSelection()->toLayer(moveto);
173 DocumentUndo::done(_desktop->getDocument(), _("Move selection to layer"), INKSCAPE_ICON("selection-move-to-layer"));
174 }
175}
176
179{
180 Glib::ustring name(_layer_name_entry.get_text());
181 if (name.empty()) {
182 return;
183 }
185
186 SPGroup *activeLayer = layman.currentLayer();
187 if (activeLayer && !activeLayer->isHighlightSet()) {
188 activeLayer->setHighlight(activeLayer->highlight_color());
189 }
190
191 layman.renameLayer(layman.currentLayer(), name.c_str(), false);
192
193 DocumentUndo::done(_desktop->getDocument(), _("Rename layer"), INKSCAPE_ICON("layer-rename"));
194 // TRANSLATORS: This means "The layer has been renamed"
195 _desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Renamed layer"));
196}
197
200{
201 g_assert(_desktop);
203
204 switch (_type) {
206 set_title(_("Add Layer"));
207 Glib::ustring new_name = layman.getNextLayerName(nullptr, layman.currentLayer()->label());
208 _layer_name_entry.set_text(new_name);
209 _apply_button.set_label(_("_Add"));
211 break;
212 }
213
215 set_title(_("Move to Layer"));
216 _layer_name_entry.set_text(_("Layer"));
217 _apply_button.set_label(_("_Move"));
218 _apply_button.set_sensitive(layman.getLayerCount());
220 break;
221 }
222
224 set_title(_("Rename Layer"));
225 gchar const *name = layman.currentLayer()->label();
226 _layer_name_entry.set_text(name ? name : _("Layer"));
227 _apply_button.set_label(_("_Rename"));
228 break;
229 }
230
232 default:
233 break;
234 }
235}
236
239{
240 if (!_layer || _desktop->getDocument()->getRoot() == _layer) {
241 // no layers yet, so option above/below/sublayer is not applicable
242 return;
243 }
244
245 _position_visible = true;
246
247 _layer_position_label.set_label(_("Position:"));
248 _layer_position_label.set_halign(Gtk::Align::START);
249 _layer_position_label.set_valign(Gtk::Align::START);
250 _layout_table.attach(_layer_position_label, 0, 1, 1, 1);
251
252 int position = Preferences::get()->getIntLimited("/dialogs/layerProp/addLayerPosition", 0, 0, 2);
253
256 _layer_position_radio[0].set_label(_("Above current"));
257 _layer_position_radio[1].set_label(_("As sublayer of current"));
258 _layer_position_radio[1].get_style_context()->add_class("indent");
259 _layer_position_radio[2].set_label(_("Below current"));
260 _layer_position_radio[0].set_active(position == LPOS_ABOVE);
261 _layer_position_radio[1].set_active(position == LPOS_CHILD);
262 _layer_position_radio[2].set_active(position == LPOS_BELOW);
263
264 auto &vbox = *Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 3);
265 vbox.append(_layer_position_radio[0]);
266 vbox.append(_layer_position_radio[1]);
267 vbox.append(_layer_position_radio[2]);
268
269 _layout_table.attach(vbox, 1, 1, 1, 1);
270}
271
274{
275 _store = Gtk::TreeStore::create(_model);
276 _tree.set_model(_store);
277 _tree.set_headers_visible(false);
278
279 auto const eyeRenderer = Gtk::make_managed<UI::Widget::ImageToggler>(INKSCAPE_ICON("object-visible"),
280 INKSCAPE_ICON("object-hidden"));
281 int visibleColNum = _tree.append_column("vis", *eyeRenderer) - 1;
282 Gtk::TreeViewColumn *col = _tree.get_column(visibleColNum);
283 if (col) {
284 col->add_attribute(eyeRenderer->property_active(), _model.visible);
285 }
286
287 auto const renderer = Gtk::make_managed<UI::Widget::ImageToggler>(INKSCAPE_ICON("object-locked"),
288 INKSCAPE_ICON("object-unlocked"));
289 int lockedColNum = _tree.append_column("lock", *renderer) - 1;
290 col = _tree.get_column(lockedColNum);
291 if (col) {
292 col->add_attribute(renderer->property_active(), _model.locked);
293 }
294
295 auto const _text_renderer = Gtk::make_managed<Gtk::CellRendererText>();
296 int nameColNum = _tree.append_column("Name", *_text_renderer) - 1;
297 Gtk::TreeView::Column *_name_column = _tree.get_column(nameColNum);
298 _name_column->add_attribute(_text_renderer->property_text(), _model.label);
299
300 _tree.set_expander_column(*_tree.get_column(nameColNum));
301
302 auto const key = Gtk::EventControllerKey::create();
303 key->signal_key_pressed().connect([this, &key = *key](auto &&...args) { return on_key_pressed(key, args...); }, true);
304 _tree.add_controller(key);
305
306 auto const click = Gtk::GestureClick::create();
307 click->set_button(1); // left
308 click->signal_pressed().connect(Controller::use_state([this](auto &, auto &&...args) { return on_click_pressed(args...); }, *click));
309 _tree.add_controller(click);
310
311 _scroller.set_child(_tree);
312 _scroller.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
313 _scroller.set_has_frame(true);
314 _scroller.set_size_request(220, 180);
315
316 SPDocument* document = _desktop->doc();
317 SPRoot* root = document->getRoot();
318 if (root) {
320 _store->clear();
321 _addLayer(root, nullptr, target, 0);
322 }
323
326
327 _scroller.set_halign(Gtk::Align::FILL);
328 _scroller.set_valign(Gtk::Align::FILL);
329 _scroller.set_hexpand();
330 _scroller.set_vexpand();
331 _scroller.set_propagate_natural_width(true);
332 _scroller.set_propagate_natural_height(true);
333 _layout_table.attach(_scroller, 0, 1, 2, 1);
334}
335
337void LayerPropertiesDialog::_addLayer(SPObject* layer, Gtk::TreeModel::Row* parentRow, SPObject* target,
338 int level)
339{
340 constexpr int max_nest_depth = 20;
341 if (!_desktop || !layer || level >= max_nest_depth) {
342 g_warn_message("Inkscape", __FILE__, __LINE__, __func__, "Maximum layer nesting reached.");
343 return;
344 }
346 unsigned int counter = layman.childCount(layer);
347 for (unsigned int i = 0; i < counter; i++) {
349 if (!child) {
350 continue;
351 }
352#if DUMP_LAYERS
353 g_message(" %3d layer:%p {%s} [%s]", level, child, child->id, child->label() );
354#endif // DUMP_LAYERS
355
356 Gtk::TreeModel::iterator iter = parentRow ? _store->prepend(parentRow->children()) : _store->prepend();
357 Gtk::TreeModel::Row row = *iter;
358 row[_model.object] = child;
359 row[_model.label] = child->label() ? child->label() : child->getId();
360 row[_model.visible] = is<SPItem>(child) ? !cast_unsafe<SPItem>(child)->isHidden() : false;
361 row[_model.locked] = is<SPItem>(child) ? cast_unsafe<SPItem>(child)->isLocked() : false;
362
363 if (target && child == target) {
364 _tree.expand_to_path(_store->get_path(iter));
365 Glib::RefPtr<Gtk::TreeSelection> select = _tree.get_selection();
366 select->select(iter);
367 }
368
369 _addLayer(child, &row, target, level + 1);
370 }
371}
372
374{
375 if (auto iter = _tree.get_selection()->get_selected()) {
376 return (*iter)[_model.object];
377 }
378
379 return nullptr;
380}
381
382bool LayerPropertiesDialog::on_key_pressed(Gtk::EventControllerKey const &controller,
383 unsigned keyval, unsigned keycode, Gdk::ModifierType state)
384{
385 auto const latin_keyval = Inkscape::UI::Tools::get_latin_keyval(controller, keyval, keycode, state);
386 switch (latin_keyval) {
387 case GDK_KEY_Return:
388 case GDK_KEY_KP_Enter: {
389 _apply();
390 return true;
391 }
392 }
393 return false;
394}
395
396Gtk::EventSequenceState LayerPropertiesDialog::on_click_pressed(int n_press, double /*x*/, double /*y*/)
397{
398 if (n_press == 2) {
399 _apply();
400 return Gtk::EventSequenceState::CLAIMED;
401 }
402 return Gtk::EventSequenceState::NONE;
403}
404
407void LayerPropertiesDialog::_prepareLabelRenderer(Gtk::TreeModel::const_iterator const &iter)
408{
409 _label_renderer.property_markup() = (*iter)[_dropdown_columns.name];
410}
411
413{
414 if (layer == _layer) {
415 return;
416 }
417
418 if (_layer) {
419 sp_object_unref(_layer, nullptr);
420 }
421
422 _layer = layer;
423
424 if (_layer) {
425 sp_object_ref(_layer, nullptr);
426 }
427}
428
429} // namespace Inkscape::UI::Dialog
430
431/*
432 Local Variables:
433 mode:c++
434 c-file-style:"stroustrup"
435 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
436 indent-tabs-mode:nil
437 fill-column:99
438 End:
439*/
440// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
SPObject * nthChildOf(SPObject *obj, unsigned n) const
unsigned childCount(SPObject *obj) const
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
void renameLayer(SPObject *obj, char const *label, bool uniquify)
void setCurrentLayer(SPObject *object, bool clear=false)
Sets the current layer of the desktop.
SPGroup * currentLayer() const
Returns current top layer.
unsigned getLayerCount() const
Glib::ustring getNextLayerName(SPObject *obj, char const *label)
MessageId flash(MessageType type, char const *message)
Temporarily pushes a message onto the stack.
void clear()
Unselects all selected objects.
void toLayer(SPObject *layer)
Move selection to group moveto, after the last child of moveto (if it has any children).
static Preferences * get()
Access the singleton Preferences object.
void setInt(Glib::ustring const &pref_path, int value)
Set an integer value.
int getIntLimited(Glib::ustring const &pref_path, int def=0, int min=INT_MIN, int max=INT_MAX)
Retrieve a limited integer.
Gtk::EventSequenceState on_click_pressed(int n_press, double x, double y)
void _doCreate()
Creates a new layer based on the input entered in the dialog window.
void _doRename()
Renames a layer based on the user input in the dialog window.
static void _showDialog(LayerPropertiesDialogType type, SPDesktop *desktop, SPObject *layer)
Static member function which displays a modal dialog of the given type.
LayerPropertiesDialog(LayerPropertiesDialogType type)
void _setup_layers_controls()
Sets up the tree view of current layers.
bool on_key_pressed(Gtk::EventControllerKey const &controller, unsigned keyval, unsigned keycode, Gdk::ModifierType state)
void _setup_position_controls()
Sets up the combo box for choosing the relative position of the new layer.
void _setup()
Sets up the dialog depending on its type.
void _doMove()
Moves selection to the chosen layer.
void _apply()
Performs an action depending on the type of the dialog.
void _addLayer(SPObject *layer, Gtk::TreeModel::Row *parentRow, SPObject *target, int level)
Inserts the new layer into the document.
void _prepareLabelRenderer(Gtk::TreeModel::const_iterator const &iter)
Formats the label for a given layer row.
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::MessageStack * messageStack() const
Definition desktop.h:160
Inkscape::Selection * getSelection() const
Definition desktop.h:188
SPDocument * doc() const
Definition desktop.h:159
void setWindowTransient(Gtk::Window &window, int transient_policy=1)
Definition desktop.cpp:970
Inkscape::LayerManager & layerManager()
Definition desktop.h:287
Typed SVG document implementation.
Definition document.h:101
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:200
Inkscape::Colors::Color highlight_color() const override
Generate a highlight colour if one isn't set and return it.
void setHighlight(Inkscape::Colors::Color color)
Definition sp-item.cpp:275
bool isHighlightSet() const
Definition sp-item.cpp:280
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
char const * label() const
Gets the author-visible label property for the object or a default if no label is defined.
<svg> element
Definition sp-root.h:33
RootCluster root
Utilities to more easily use Gtk::EventController & subclasses like Gesture.
Editable view implementation.
TODO: insert short description here.
Macro for icon names used in Inkscape.
Dialog for renaming layers.
Raw stack of active status messages.
Definition desktop.h:50
auto use_state(Slot &&slot)
Definition controller.h:43
Dialog code.
Definition desktop.h:117
unsigned get_latin_keyval(GtkEventControllerKey const *const controller, unsigned const keyval, unsigned const keycode, GdkModifierType const state, unsigned *consumed_modifiers)
Return the keyval corresponding to the event controller key in Latin group.
SPObject * create_layer(SPObject *root, SPObject *layer, LayerRelativePosition position)
Creates a new layer.
@ NORMAL_MESSAGE
Definition message.h:26
static cairo_user_data_key_t key
static gint counter
Definition box3d.cpp:39
Singleton class to access the preferences file in a convenient way.
Ocnode * child[8]
Definition quantize.cpp:33
SPObject * sp_object_unref(SPObject *object, SPObject *owner)
Decrease reference count of object, with possible debugging and finalization.
SPObject * sp_object_ref(SPObject *object, SPObject *owner)
Increase reference count of object, with possible debugging.
SPRoot: SVG <svg> implementation.
int index
SPDesktop * desktop
Glib::ustring name
Definition toolbars.cpp:55