summaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/layer-properties.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/dialog/layer-properties.cpp')
-rw-r--r--src/ui/dialog/layer-properties.cpp435
1 files changed, 435 insertions, 0 deletions
diff --git a/src/ui/dialog/layer-properties.cpp b/src/ui/dialog/layer-properties.cpp
new file mode 100644
index 0000000..1149f04
--- /dev/null
+++ b/src/ui/dialog/layer-properties.cpp
@@ -0,0 +1,435 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Dialog for renaming layers.
+ */
+/* Author:
+ * Bryce W. Harrington <bryce@bryceharrington.org>
+ * Andrius R. <knutux@gmail.com>
+ * Abhishek Sharma
+ *
+ * Copyright (C) 2004 Bryce Harrington
+ * Copyright (C) 2006 Andrius R.
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "layer-properties.h"
+
+#include <glibmm/i18n.h>
+#include <glibmm/main.h>
+
+#include "inkscape.h"
+#include "desktop.h"
+#include "document.h"
+#include "document-undo.h"
+#include "layer-manager.h"
+#include "message-stack.h"
+#include "preferences.h"
+#include "selection-chemistry.h"
+
+#include "ui/icon-names.h"
+#include "ui/widget/imagetoggler.h"
+#include "ui/tools/tool-base.h"
+#include "object/sp-root.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialogs {
+
+LayerPropertiesDialog::LayerPropertiesDialog(LayerPropertiesDialogType type)
+ : _type{type}
+ , _close_button(_("_Cancel"), true)
+{
+ auto mainVBox = get_content_area();
+ _layout_table.set_row_spacing(4);
+ _layout_table.set_column_spacing(4);
+
+ // Layer name widgets
+ _layer_name_entry.set_activates_default(true);
+ _layer_name_label.set_label(_("Layer name:"));
+ _layer_name_label.set_halign(Gtk::ALIGN_START);
+ _layer_name_label.set_valign(Gtk::ALIGN_CENTER);
+
+ _layout_table.attach(_layer_name_label, 0, 0, 1, 1);
+
+ _layer_name_entry.set_halign(Gtk::ALIGN_FILL);
+ _layer_name_entry.set_valign(Gtk::ALIGN_FILL);
+ _layer_name_entry.set_hexpand();
+ _layout_table.attach(_layer_name_entry, 1, 0, 1, 1);
+
+ mainVBox->pack_start(_layout_table, true, true, 4);
+
+ // Buttons
+ _close_button.set_can_default();
+
+ _apply_button.set_use_underline(true);
+ _apply_button.set_can_default();
+
+ _close_button.signal_clicked().connect([=]() {_close();});
+ _apply_button.signal_clicked().connect([=]() {_apply();});
+
+ signal_delete_event().connect([=](GdkEventAny*) -> bool {
+ _close();
+ return true;
+ });
+
+ add_action_widget(_close_button, Gtk::RESPONSE_CLOSE);
+ add_action_widget(_apply_button, Gtk::RESPONSE_APPLY);
+
+ _apply_button.grab_default();
+
+ show_all_children();
+}
+
+LayerPropertiesDialog::~LayerPropertiesDialog() = default;
+
+/** Static member function which displays a modal dialog of the given type */
+void LayerPropertiesDialog::_showDialog(LayerPropertiesDialogType type, SPDesktop *desktop, SPObject *layer)
+{
+ auto dialog = new LayerPropertiesDialog(type); // Will be destroyed on idle - see _close()
+
+ dialog->_setDesktop(desktop);
+ dialog->_setLayer(layer);
+
+ dialog->_setup();
+
+ dialog->set_modal(true);
+ desktop->setWindowTransient(dialog->gobj());
+ dialog->property_destroy_with_parent() = true;
+
+ dialog->show();
+ dialog->present();
+}
+
+/** Performs an action depending on the type of the dialog */
+void LayerPropertiesDialog::_apply()
+{
+ switch (_type) {
+ case LayerPropertiesDialogType::CREATE:
+ _doCreate();
+ break;
+
+ case LayerPropertiesDialogType::MOVE:
+ _doMove();
+ break;
+
+ case LayerPropertiesDialogType::RENAME:
+ _doRename();
+ break;
+
+ case LayerPropertiesDialogType::NONE:
+ default:
+ break;
+ }
+ _close();
+}
+
+/** Closes the dialog and asks the idle thread to destroy it */
+void LayerPropertiesDialog::_close()
+{
+ _setLayer(nullptr);
+ _setDesktop(nullptr);
+ destroy_();
+ Glib::signal_idle().connect_once([=]() {delete this;});
+}
+
+/** Creates a new layer based on the input entered in the dialog window */
+void LayerPropertiesDialog::_doCreate()
+{
+ LayerRelativePosition position = LPOS_ABOVE;
+ if (_position_visible) {
+ Gtk::ListStore::iterator activeRow(_layer_position_combo.get_active());
+ position = activeRow->get_value(_dropdown_columns.position);
+ int index = _layer_position_combo.get_active_row_number();
+ Preferences::get()->setInt("/dialogs/layerProp/addLayerPosition", index);
+ }
+ Glib::ustring name(_layer_name_entry.get_text());
+ if (name.empty()) {
+ return;
+ }
+
+ auto root = _desktop->getDocument()->getRoot();
+ SPObject *new_layer = Inkscape::create_layer(root, _layer, position);
+
+ if (!name.empty()) {
+ _desktop->layerManager().renameLayer(new_layer, name.c_str(), true);
+ }
+ _desktop->getSelection()->clear();
+ _desktop->layerManager().setCurrentLayer(new_layer);
+ DocumentUndo::done(_desktop->getDocument(), _("Add layer"), INKSCAPE_ICON("layer-new"));
+ _desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("New layer created."));
+}
+
+/** Moves selection to the chosen layer */
+void LayerPropertiesDialog::_doMove()
+{
+ if (auto moveto = _selectedLayer()) {
+ _desktop->getSelection()->toLayer(moveto);
+ DocumentUndo::done(_desktop->getDocument(), _("Move selection to layer"), INKSCAPE_ICON("selection-move-to-layer"));
+ }
+}
+
+/** Renames a layer based on the user input in the dialog window */
+void LayerPropertiesDialog::_doRename()
+{
+ Glib::ustring name(_layer_name_entry.get_text());
+ if (name.empty()) {
+ return;
+ }
+ LayerManager &layman = _desktop->layerManager();
+ layman.renameLayer(layman.currentLayer(), name.c_str(), false);
+
+ DocumentUndo::done(_desktop->getDocument(), _("Rename layer"), INKSCAPE_ICON("layer-rename"));
+ // TRANSLATORS: This means "The layer has been renamed"
+ _desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Renamed layer"));
+}
+
+/** Sets up the dialog depending on its type */
+void LayerPropertiesDialog::_setup()
+{
+ g_assert(_desktop != nullptr);
+ LayerManager &layman = _desktop->layerManager();
+
+ switch (_type) {
+ case LayerPropertiesDialogType::CREATE: {
+ set_title(_("Add Layer"));
+ Glib::ustring new_name = layman.getNextLayerName(nullptr, layman.currentLayer()->label());
+ _layer_name_entry.set_text(new_name);
+ _apply_button.set_label(_("_Add"));
+ _setup_position_controls();
+ break;
+ }
+
+ case LayerPropertiesDialogType::MOVE: {
+ set_title(_("Move to Layer"));
+ _layer_name_entry.set_text(_("Layer"));
+ _apply_button.set_label(_("_Move"));
+ _apply_button.set_sensitive(layman.getLayerCount());
+ _setup_layers_controls();
+ break;
+ }
+
+ case LayerPropertiesDialogType::RENAME: {
+ set_title(_("Rename Layer"));
+ gchar const *name = layman.currentLayer()->label();
+ _layer_name_entry.set_text(name ? name : _("Layer"));
+ _apply_button.set_label(_("_Rename"));
+ break;
+ }
+
+ case LayerPropertiesDialogType::NONE:
+ default:
+ break;
+ }
+}
+
+/** Sets up the combo box for choosing the relative position of the new layer */
+void LayerPropertiesDialog::_setup_position_controls()
+{
+ if (!_layer || _desktop->getDocument()->getRoot() == _layer) {
+ // no layers yet, so option above/below/sublayer is useless
+ return;
+ }
+
+ _position_visible = true;
+ _dropdown_list = Gtk::ListStore::create(_dropdown_columns);
+ _layer_position_combo.set_model(_dropdown_list);
+ _layer_position_combo.pack_start(_label_renderer);
+ _layer_position_combo.set_cell_data_func(_label_renderer,
+ [=](Gtk::TreeModel::const_iterator const &row) {
+ _prepareLabelRenderer(row);
+ });
+
+ Gtk::ListStore::iterator row;
+ row = _dropdown_list->append();
+ row->set_value(_dropdown_columns.position, LPOS_ABOVE);
+ row->set_value(_dropdown_columns.name, Glib::ustring(_("Above current")));
+ _layer_position_combo.set_active(row);
+ row = _dropdown_list->append();
+ row->set_value(_dropdown_columns.position, LPOS_BELOW);
+ row->set_value(_dropdown_columns.name, Glib::ustring(_("Below current")));
+ row = _dropdown_list->append();
+ row->set_value(_dropdown_columns.position, LPOS_CHILD);
+ row->set_value(_dropdown_columns.name, Glib::ustring(_("As sublayer of current")));
+
+ int position = Preferences::get()->getIntLimited("/dialogs/layerProp/addLayerPosition", 0, 0, 2);
+ _layer_position_combo.set_active(position);
+
+ _layer_position_label.set_label(_("Position:"));
+ _layer_position_label.set_halign(Gtk::ALIGN_START);
+ _layer_position_label.set_valign(Gtk::ALIGN_CENTER);
+
+ _layer_position_combo.set_halign(Gtk::ALIGN_FILL);
+ _layer_position_combo.set_valign(Gtk::ALIGN_FILL);
+ _layer_position_combo.set_hexpand();
+ _layout_table.attach(_layer_position_combo, 1, 1, 1, 1);
+
+ _layout_table.attach(_layer_position_label, 0, 1, 1, 1);
+
+ show_all_children();
+}
+
+/** Sets up the tree view of current layers */
+void LayerPropertiesDialog::_setup_layers_controls()
+{
+ ModelColumns *zoop = new ModelColumns();
+ _model = zoop;
+ _store = Gtk::TreeStore::create( *zoop );
+ _tree.set_model( _store );
+ _tree.set_headers_visible(false);
+
+ auto *eyeRenderer = Gtk::manage(new UI::Widget::ImageToggler(INKSCAPE_ICON("object-visible"),
+ INKSCAPE_ICON("object-hidden")));
+ int visibleColNum = _tree.append_column("vis", *eyeRenderer) - 1;
+ Gtk::TreeViewColumn *col = _tree.get_column(visibleColNum);
+ if (col) {
+ col->add_attribute(eyeRenderer->property_active(), _model->_colVisible);
+ }
+
+ auto *renderer = Gtk::manage(new UI::Widget::ImageToggler(INKSCAPE_ICON("object-locked"),
+ INKSCAPE_ICON("object-unlocked")));
+ int lockedColNum = _tree.append_column("lock", *renderer) - 1;
+ col = _tree.get_column(lockedColNum);
+ if (col) {
+ col->add_attribute(renderer->property_active(), _model->_colLocked);
+ }
+
+ Gtk::CellRendererText *_text_renderer = Gtk::manage(new Gtk::CellRendererText());
+ int nameColNum = _tree.append_column("Name", *_text_renderer) - 1;
+ Gtk::TreeView::Column *_name_column = _tree.get_column(nameColNum);
+ _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel);
+
+ _tree.set_expander_column(*_tree.get_column(nameColNum));
+ _tree.signal_key_press_event().connect([=](GdkEventKey *ev) {return _handleKeyEvent(ev);}, false);
+ _tree.signal_button_press_event().connect_notify([=](GdkEventButton *b) {_handleButtonEvent(b);});
+
+ _scroller.add(_tree);
+ _scroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+ _scroller.set_shadow_type(Gtk::SHADOW_IN);
+ _scroller.set_size_request(220, 180);
+
+ SPDocument* document = _desktop->doc();
+ SPRoot* root = document->getRoot();
+ if (root) {
+ SPObject* target = _desktop->layerManager().currentLayer();
+ _store->clear();
+ _addLayer(root, nullptr, target, 0);
+ }
+
+ _layout_table.remove(_layer_name_entry);
+ _layout_table.remove(_layer_name_label);
+
+ _scroller.set_halign(Gtk::ALIGN_FILL);
+ _scroller.set_valign(Gtk::ALIGN_FILL);
+ _scroller.set_hexpand();
+ _scroller.set_vexpand();
+ _scroller.set_propagate_natural_width(true);
+ _scroller.set_propagate_natural_height(true);
+ _layout_table.attach(_scroller, 0, 1, 2, 1);
+
+ show_all_children();
+}
+
+/** Inserts the new layer into the document */
+void LayerPropertiesDialog::_addLayer(SPObject* layer, Gtk::TreeModel::Row* parentRow, SPObject* target,
+ int level)
+{
+ int const max_nest_depth = 20;
+ if (!_desktop || !layer || level >= max_nest_depth) {
+ g_warn_message("Inkscape", __FILE__, __LINE__, __func__, "Maximum layer nesting reached.");
+ return;
+ }
+ LayerManager &layman = _desktop->layerManager();
+ unsigned int counter = layman.childCount(layer);
+ for (unsigned int i = 0; i < counter; i++) {
+ SPObject *child = _desktop->layerManager().nthChildOf(layer, i);
+ if (!child) {
+ continue;
+ }
+#if DUMP_LAYERS
+ g_message(" %3d layer:%p {%s} [%s]", level, child, child->id, child->label() );
+#endif // DUMP_LAYERS
+
+ Gtk::TreeModel::iterator iter = parentRow ? _store->prepend(parentRow->children()) : _store->prepend();
+ Gtk::TreeModel::Row row = *iter;
+ row[_model->_colObject] = child;
+ row[_model->_colLabel] = child->label() ? child->label() : child->getId();
+ row[_model->_colVisible] = is<SPItem>(child) ? !cast_unsafe<SPItem>(child)->isHidden() : false;
+ row[_model->_colLocked] = is<SPItem>(child) ? cast_unsafe<SPItem>(child)->isLocked() : false;
+
+ if (target && child == target) {
+ _tree.expand_to_path(_store->get_path(iter));
+ Glib::RefPtr<Gtk::TreeSelection> select = _tree.get_selection();
+ select->select(iter);
+ }
+
+ _addLayer(child, &row, target, level + 1);
+ }
+}
+
+SPObject* LayerPropertiesDialog::_selectedLayer()
+{
+ SPObject* obj = nullptr;
+
+ Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ obj = row[_model->_colObject];
+ }
+
+ return obj;
+}
+
+bool LayerPropertiesDialog::_handleKeyEvent(GdkEventKey *event)
+{
+ switch (Inkscape::UI::Tools::get_latin_keyval(event)) {
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter: {
+ _apply();
+ return true;
+ }
+ }
+ return false;
+}
+
+void LayerPropertiesDialog::_handleButtonEvent(GdkEventButton* event)
+{
+ if ((event->type == GDK_2BUTTON_PRESS) && (event->button == 1)) {
+ _apply();
+ }
+}
+
+/** Formats the label for a given layer row
+ */
+void LayerPropertiesDialog::_prepareLabelRenderer(Gtk::TreeModel::const_iterator const &row)
+{
+ Glib::ustring name = (*row)[_dropdown_columns.name];
+ _label_renderer.property_markup() = name;
+}
+
+
+void LayerPropertiesDialog::_setLayer(SPObject *layer) {
+ if (layer) {
+ sp_object_ref(layer, nullptr);
+ }
+ if (_layer) {
+ sp_object_unref(_layer, nullptr);
+ }
+ _layer = layer;
+}
+
+} // namespace Dialogs
+} // 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 :