summaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/object-properties.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/dialog/object-properties.cpp')
-rw-r--r--src/ui/dialog/object-properties.cpp581
1 files changed, 581 insertions, 0 deletions
diff --git a/src/ui/dialog/object-properties.cpp b/src/ui/dialog/object-properties.cpp
new file mode 100644
index 0000000..6acce53
--- /dev/null
+++ b/src/ui/dialog/object-properties.cpp
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file Object properties dialog.
+ */
+/*
+ * Inkscape, an Open Source vector graphics editor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright (C) 2012 Kris De Gussem <Kris.DeGussem@gmail.com>
+ * c++ version based on former C-version (GPL v2+) with authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ * Johan Engelen <goejendaagh@zonnet.nl>
+ * Abhishek Sharma
+ */
+
+#include "object-properties.h"
+
+#include <glibmm/i18n.h>
+
+#include <gtkmm/grid.h>
+
+#include "document-undo.h"
+#include "document.h"
+#include "inkscape.h"
+#include "style.h"
+#include "style-enums.h"
+
+#include "object/sp-image.h"
+#include "ui/icon-names.h"
+#include "widgets/sp-attribute-widget.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+ObjectProperties::ObjectProperties()
+ : DialogBase("/dialogs/object/", "ObjectProperties")
+ , _blocked(false)
+ , _current_item(nullptr)
+ , _label_id(_("_ID:"), true)
+ , _label_label(_("_Label:"), true)
+ , _label_title(_("_Title:"), true)
+ , _label_dpi(_("_DPI SVG:"), true)
+ , _label_image_rendering(_("_Image Rendering:"), true)
+ , _label_color(_("Highlight Color:"), true)
+ , _highlight_color(_("Highlight Color"), "", 0xff0000ff, true)
+ , _cb_hide(_("_Hide"), true)
+ , _cb_lock(_("L_ock"), true)
+ , _cb_aspect_ratio(_("Preserve Ratio"), true)
+ , _exp_interactivity(_("_Interactivity"), true)
+ , _attr_table(Gtk::manage(new SPAttributeTable()))
+{
+ //initialize labels for the table at the bottom of the dialog
+ _int_attrs.emplace_back("onclick");
+ _int_attrs.emplace_back("onmouseover");
+ _int_attrs.emplace_back("onmouseout");
+ _int_attrs.emplace_back("onmousedown");
+ _int_attrs.emplace_back("onmouseup");
+ _int_attrs.emplace_back("onmousemove");
+ _int_attrs.emplace_back("onfocusin");
+ _int_attrs.emplace_back("onfocusout");
+ _int_attrs.emplace_back("onload");
+
+ _int_labels.emplace_back("onclick:");
+ _int_labels.emplace_back("onmouseover:");
+ _int_labels.emplace_back("onmouseout:");
+ _int_labels.emplace_back("onmousedown:");
+ _int_labels.emplace_back("onmouseup:");
+ _int_labels.emplace_back("onmousemove:");
+ _int_labels.emplace_back("onfocusin:");
+ _int_labels.emplace_back("onfocusout:");
+ _int_labels.emplace_back("onload:");
+
+ _init();
+}
+
+void ObjectProperties::_init()
+{
+ set_spacing(0);
+
+ auto grid_top = Gtk::manage(new Gtk::Grid());
+ grid_top->set_row_spacing(4);
+ grid_top->set_column_spacing(0);
+ grid_top->set_border_width(4);
+
+ pack_start(*grid_top, false, false, 0);
+
+
+ /* Create the label for the object id */
+ _label_id.set_label(_label_id.get_label() + " ");
+ _label_id.set_halign(Gtk::ALIGN_START);
+ _label_id.set_valign(Gtk::ALIGN_CENTER);
+
+ /* Create the entry box for the object id */
+ _entry_id.set_tooltip_text(_("The id= attribute (only letters, digits, and the characters .-_: allowed)"));
+ _entry_id.set_max_length(64);
+ _entry_id.set_hexpand();
+ _entry_id.set_valign(Gtk::ALIGN_CENTER);
+
+ _label_id.set_mnemonic_widget(_entry_id);
+
+ // pressing enter in the id field is the same as clicking Set:
+ _entry_id.signal_activate().connect(sigc::mem_fun(this, &ObjectProperties::_labelChanged));
+ // focus is in the id field initially:
+ _entry_id.grab_focus();
+
+
+ /* Create the label for the object label */
+ _label_label.set_label(_label_label.get_label() + " ");
+ _label_label.set_halign(Gtk::ALIGN_START);
+ _label_label.set_valign(Gtk::ALIGN_CENTER);
+
+ /* Create the entry box for the object label */
+ _entry_label.set_tooltip_text(_("A freeform label for the object"));
+ _entry_label.set_max_length(256);
+
+ _entry_label.set_hexpand();
+ _entry_label.set_valign(Gtk::ALIGN_CENTER);
+
+ _label_label.set_mnemonic_widget(_entry_label);
+
+ // pressing enter in the label field is the same as clicking Set:
+ _entry_label.signal_activate().connect(sigc::mem_fun(this, &ObjectProperties::_labelChanged));
+
+
+ /* Create the label for the object title */
+ _label_title.set_label(_label_title.get_label() + " ");
+ _label_title.set_halign(Gtk::ALIGN_START);
+ _label_title.set_valign(Gtk::ALIGN_CENTER);
+
+ /* Create the entry box for the object title */
+ _entry_title.set_sensitive (FALSE);
+ _entry_title.set_max_length (256);
+
+ _entry_title.set_hexpand();
+ _entry_title.set_valign(Gtk::ALIGN_CENTER);
+
+ _label_title.set_mnemonic_widget(_entry_title);
+ // pressing enter in the label field is the same as clicking Set:
+ _entry_title.signal_activate().connect(sigc::mem_fun(this, &ObjectProperties::_labelChanged));
+
+ _label_color.set_mnemonic_widget(_highlight_color);
+ _label_color.set_halign(Gtk::ALIGN_START);
+ _highlight_color.connectChanged(sigc::mem_fun(*this, &ObjectProperties::_highlightChanged));
+
+ /* Create the frame for the object description */
+ Gtk::Label *label_desc = Gtk::manage(new Gtk::Label(_("_Description:"), true));
+ UI::Widget::Frame *frame_desc = Gtk::manage(new UI::Widget::Frame("", FALSE));
+ frame_desc->set_label_widget(*label_desc);
+ frame_desc->set_padding (0,0,0,0);
+ pack_start(*frame_desc, true, true, 0);
+
+ /* Create the text view box for the object description */
+ _ft_description.set_border_width(4);
+ _ft_description.set_sensitive(FALSE);
+ frame_desc->add(_ft_description);
+ _ft_description.set_shadow_type(Gtk::SHADOW_IN);
+
+ _tv_description.set_wrap_mode(Gtk::WRAP_WORD);
+ _tv_description.get_buffer()->set_text("");
+ _ft_description.add(_tv_description);
+ _tv_description.add_mnemonic_label(*label_desc);
+
+ /* Create the label for the object title */
+ _label_dpi.set_label(_label_dpi.get_label() + " ");
+ _label_dpi.set_halign(Gtk::ALIGN_START);
+ _label_dpi.set_valign(Gtk::ALIGN_CENTER);
+
+ /* Create the entry box for the SVG DPI */
+ _spin_dpi.set_digits(2);
+ _spin_dpi.set_range(1, 1200);
+
+ _label_dpi.set_mnemonic_widget(_spin_dpi);
+ // pressing enter in the label field is the same as clicking Set:
+ _spin_dpi.signal_activate().connect(sigc::mem_fun(this, &ObjectProperties::_labelChanged));
+
+ /* Image rendering */
+ /* Create the label for the object ImageRendering */
+ _label_image_rendering.set_label(_label_image_rendering.get_label() + " ");
+ _label_image_rendering.set_halign(Gtk::ALIGN_START);
+ _label_image_rendering.set_valign(Gtk::ALIGN_CENTER);
+
+ /* Create the combo box text for the 'image-rendering' property */
+ for (unsigned i = 0; enum_image_rendering[i].key; ++i) {
+ _combo_image_rendering.append(enum_image_rendering[i].key);
+ }
+ _combo_image_rendering.set_tooltip_text(_("The 'image-rendering' property can influence how a bitmap is re-scaled:\n"
+ "\t• 'auto': no preference (scaled image is usually smooth but blurred)\n"
+ "\t• 'optimizeQuality': prefer rendering quality (usually smooth but blurred)\n"
+ "\t• 'optimizeSpeed': prefer rendering speed (usually blocky)\n"
+ "\t• 'crisp-edges': rescale without blurring edges (often blocky)\n"
+ "\t• 'pixelated': render blocky\n"
+ "Note that the specification of this property is not finalized. "
+ "Support and interpretation of these values varies between renderers."));
+
+ _combo_image_rendering.set_valign(Gtk::ALIGN_CENTER);
+
+ _label_image_rendering.set_mnemonic_widget(_combo_image_rendering);
+
+ _combo_image_rendering.signal_changed().connect(
+ sigc::mem_fun(this, &ObjectProperties::_imageRenderingChanged)
+ );
+
+
+ grid_top->attach(_label_id, 0, 0, 1, 1);
+ grid_top->attach(_entry_id, 1, 0, 1, 1);
+ grid_top->attach(_label_label, 0, 1, 1, 1);
+ grid_top->attach(_entry_label, 1, 1, 1, 1);
+ grid_top->attach(_label_title, 0, 2, 1, 1);
+ grid_top->attach(_entry_title, 1, 2, 1, 1);
+ grid_top->attach(_label_color, 0, 3, 1, 1);
+ grid_top->attach(_highlight_color, 1, 3, 1, 1);
+ grid_top->attach(_label_dpi, 0, 4, 1, 1);
+ grid_top->attach(_spin_dpi, 1, 4, 1, 1);
+ grid_top->attach(_label_image_rendering, 0, 5, 1, 1);
+ grid_top->attach(_combo_image_rendering, 1, 5, 1, 1);
+
+ /* Check boxes */
+ Gtk::Box *hb_checkboxes = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
+ pack_start(*hb_checkboxes, Gtk::PACK_SHRINK, 0);
+
+ auto grid_cb = Gtk::manage(new Gtk::Grid());
+ grid_cb->set_row_homogeneous();
+ grid_cb->set_column_homogeneous(true);
+
+ grid_cb->set_border_width(4);
+ hb_checkboxes->pack_start(*grid_cb, true, true, 0);
+
+ /* Hide */
+ _cb_hide.set_tooltip_text (_("Check to make the object invisible"));
+ _cb_hide.set_hexpand();
+ _cb_hide.set_valign(Gtk::ALIGN_CENTER);
+ grid_cb->attach(_cb_hide, 0, 0, 1, 1);
+
+ _cb_hide.signal_toggled().connect(sigc::mem_fun(this, &ObjectProperties::_hiddenToggled));
+
+ /* Lock */
+ // TRANSLATORS: "Lock" is a verb here
+ _cb_lock.set_tooltip_text(_("Check to make the object insensitive (not selectable by mouse)"));
+ _cb_lock.set_hexpand();
+ _cb_lock.set_valign(Gtk::ALIGN_CENTER);
+ grid_cb->attach(_cb_lock, 1, 0, 1, 1);
+
+ _cb_lock.signal_toggled().connect(sigc::mem_fun(this, &ObjectProperties::_sensitivityToggled));
+
+ /* Preserve aspect ratio */
+ _cb_aspect_ratio.set_tooltip_text(_("Check to preserve aspect ratio on images"));
+ _cb_aspect_ratio.set_hexpand();
+ _cb_aspect_ratio.set_valign(Gtk::ALIGN_CENTER);
+ grid_cb->attach(_cb_aspect_ratio, 0, 1, 1, 1);
+
+ _cb_aspect_ratio.signal_toggled().connect(sigc::mem_fun(this, &ObjectProperties::_aspectRatioToggled));
+
+
+ /* Button for setting the object's id, label, title and description. */
+ Gtk::Button *btn_set = Gtk::manage(new Gtk::Button(_("_Set"), true));
+ btn_set->set_hexpand();
+ btn_set->set_valign(Gtk::ALIGN_CENTER);
+ grid_cb->attach(*btn_set, 1, 1, 1, 1);
+
+ btn_set->signal_clicked().connect(sigc::mem_fun(this, &ObjectProperties::_labelChanged));
+
+ /* Interactivity options */
+ _exp_interactivity.set_vexpand(false);
+ pack_start(_exp_interactivity, Gtk::PACK_SHRINK);
+ show_all();
+}
+
+void ObjectProperties::update_entries()
+{
+ if (_blocked || !getDesktop()) {
+ return;
+ }
+
+ auto selection = getSelection();
+ if (!selection) return;
+
+ if (!selection->singleItem()) {
+ set_sensitive (false);
+ _current_item = nullptr;
+ //no selection anymore or multiple objects selected, means that we need
+ //to close the connections to the previously selected object
+ _attr_table->clear();
+ _highlight_color.setRgba32(0x0);
+ return;
+ } else {
+ set_sensitive (true);
+ }
+
+ SPItem *item = selection->singleItem();
+ if (_current_item == item)
+ {
+ //otherwise we would end up wasting resources through the modify selection
+ //callback when moving an object (endlessly setting the labels and recreating _attr_table)
+ return;
+ }
+ _blocked = true;
+ _cb_aspect_ratio.set_active(g_strcmp0(item->getAttribute("preserveAspectRatio"), "none") != 0);
+ _cb_lock.set_active(item->isLocked()); /* Sensitive */
+ _cb_hide.set_active(item->isExplicitlyHidden()); /* Hidden */
+ _highlight_color.setRgba32(item->highlight_color());
+ _highlight_color.closeWindow();
+
+ if (item->cloned) {
+ /* ID */
+ _entry_id.set_text("");
+ _entry_id.set_sensitive(FALSE);
+ _label_id.set_text(_("Ref"));
+
+ /* Label */
+ _entry_label.set_text("");
+ _entry_label.set_sensitive(FALSE);
+ _label_label.set_text(_("Ref"));
+
+ } else {
+ SPObject *obj = static_cast<SPObject*>(item);
+
+ /* ID */
+ _entry_id.set_text(obj->getId() ? obj->getId() : "");
+ _entry_id.set_sensitive(TRUE);
+ _label_id.set_markup_with_mnemonic(_("_ID:") + Glib::ustring(" "));
+
+ /* Label */
+ char const *currentlabel = obj->label();
+ char const *placeholder = "";
+ if (!currentlabel) {
+ currentlabel = "";
+ placeholder = obj->defaultLabel();
+ }
+ _entry_label.set_text(currentlabel);
+ _entry_label.set_placeholder_text(placeholder);
+ _entry_label.set_sensitive(TRUE);
+
+ /* Title */
+ gchar *title = obj->title();
+ if (title) {
+ _entry_title.set_text(title);
+ g_free(title);
+ }
+ else {
+ _entry_title.set_text("");
+ }
+ _entry_title.set_sensitive(TRUE);
+
+ /* Image Rendering */
+ if (SP_IS_IMAGE(item)) {
+ _combo_image_rendering.show();
+ _label_image_rendering.show();
+ _combo_image_rendering.set_active(obj->style->image_rendering.value);
+ if (obj->getAttribute("inkscape:svg-dpi")) {
+ _spin_dpi.set_value(std::stod(obj->getAttribute("inkscape:svg-dpi")));
+ _spin_dpi.show();
+ _label_dpi.show();
+ } else {
+ _spin_dpi.hide();
+ _label_dpi.hide();
+ }
+ } else {
+ _combo_image_rendering.hide();
+ _combo_image_rendering.unset_active();
+ _label_image_rendering.hide();
+ _spin_dpi.hide();
+ _label_dpi.hide();
+ }
+
+ /* Description */
+ gchar *desc = obj->desc();
+ if (desc) {
+ _tv_description.get_buffer()->set_text(desc);
+ g_free(desc);
+ } else {
+ _tv_description.get_buffer()->set_text("");
+ }
+ _ft_description.set_sensitive(TRUE);
+
+ if (_current_item == nullptr) {
+ _attr_table->set_object(obj, _int_labels, _int_attrs, (GtkWidget*) _exp_interactivity.gobj());
+ } else {
+ _attr_table->change_object(obj);
+ }
+ _attr_table->show_all();
+ }
+ _current_item = item;
+ _blocked = false;
+}
+
+void ObjectProperties::_labelChanged()
+{
+ if (_blocked) {
+ return;
+ }
+
+ SPItem *item = getSelection()->singleItem();
+ g_return_if_fail (item != nullptr);
+
+ _blocked = true;
+
+ /* Retrieve the label widget for the object's id */
+ gchar *id = g_strdup(_entry_id.get_text().c_str());
+ g_strcanon(id, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.:", '_');
+ if (g_strcmp0(id, item->getId()) == 0) {
+ _label_id.set_markup_with_mnemonic(_("_ID:") + Glib::ustring(" "));
+ } else if (!*id || !isalnum (*id)) {
+ _label_id.set_text(_("Id invalid! "));
+ } else if (getDocument()->getObjectById(id) != nullptr) {
+ _label_id.set_text(_("Id exists! "));
+ } else {
+ _label_id.set_markup_with_mnemonic(_("_ID:") + Glib::ustring(" "));
+ item->setAttribute("id", id);
+ DocumentUndo::done(getDocument(), _("Set object ID"), INKSCAPE_ICON("dialog-object-properties"));
+ }
+ g_free(id);
+
+ /* Retrieve the label widget for the object's label */
+ Glib::ustring label = _entry_label.get_text();
+
+ /* Give feedback on success of setting the drawing object's label
+ * using the widget's label text
+ */
+ SPObject *obj = static_cast<SPObject*>(item);
+ char const *currentlabel = obj->label();
+ if (label.compare(currentlabel ? currentlabel : "")) {
+ obj->setLabel(label.c_str());
+ DocumentUndo::done(getDocument(), _("Set object label"), INKSCAPE_ICON("dialog-object-properties"));
+ }
+
+ /* Retrieve the title */
+ if (obj->setTitle(_entry_title.get_text().c_str())) {
+ DocumentUndo::done(getDocument(), _("Set object title"), INKSCAPE_ICON("dialog-object-properties"));
+ }
+
+ /* Retrieve the DPI */
+ if (SP_IS_IMAGE(obj)) {
+ Glib::ustring dpi_value = Glib::ustring::format(_spin_dpi.get_value());
+ obj->setAttribute("inkscape:svg-dpi", dpi_value);
+ DocumentUndo::done(getDocument(), _("Set image DPI"), INKSCAPE_ICON("dialog-object-properties"));
+ }
+
+ /* Retrieve the description */
+ Gtk::TextBuffer::iterator start, end;
+ _tv_description.get_buffer()->get_bounds(start, end);
+ Glib::ustring desc = _tv_description.get_buffer()->get_text(start, end, TRUE);
+ if (obj->setDesc(desc.c_str())) {
+ DocumentUndo::done(getDocument(), _("Set object description"), INKSCAPE_ICON("dialog-object-properties"));
+ }
+
+ _blocked = false;
+}
+
+void ObjectProperties::_highlightChanged(guint rgba)
+{
+ if (_blocked)
+ return;
+
+ if (auto item = getSelection()->singleItem()) {
+ item->setHighlight(rgba);
+ DocumentUndo::done(getDocument(), _("Set item highlight color"), INKSCAPE_ICON("dialog-object-properties"));
+ }
+}
+
+void ObjectProperties::_imageRenderingChanged()
+{
+ if (_blocked) {
+ return;
+ }
+
+ SPItem *item = getSelection()->singleItem();
+ g_return_if_fail (item != nullptr);
+
+ _blocked = true;
+
+ Glib::ustring scale = _combo_image_rendering.get_active_text();
+
+ // We should unset if the parent computed value is auto and the desired value is auto.
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "image-rendering", scale.c_str());
+ Inkscape::XML::Node *image_node = item->getRepr();
+ if (image_node) {
+ sp_repr_css_change(image_node, css, "style");
+ DocumentUndo::done(getDocument(), _("Set image rendering option"), INKSCAPE_ICON("dialog-object-properties"));
+ }
+ sp_repr_css_attr_unref(css);
+
+ _blocked = false;
+}
+
+void ObjectProperties::_sensitivityToggled()
+{
+ if (_blocked) {
+ return;
+ }
+
+ SPItem *item = getSelection()->singleItem();
+ g_return_if_fail(item != nullptr);
+
+ _blocked = true;
+ item->setLocked(_cb_lock.get_active());
+ DocumentUndo::done(getDocument(), _cb_lock.get_active() ? _("Lock object") : _("Unlock object"), INKSCAPE_ICON("dialog-object-properties"));
+ _blocked = false;
+}
+
+void ObjectProperties::_aspectRatioToggled()
+{
+ if (_blocked) {
+ return;
+ }
+
+ SPItem *item = getSelection()->singleItem();
+ g_return_if_fail(item != nullptr);
+
+ _blocked = true;
+
+ const char *active;
+ if (_cb_aspect_ratio.get_active()) {
+ active = "xMidYMid";
+ }
+ else {
+ active = "none";
+ }
+ /* Retrieve the DPI */
+ if (SP_IS_IMAGE(item)) {
+ Glib::ustring dpi_value = Glib::ustring::format(_spin_dpi.get_value());
+ item->setAttribute("preserveAspectRatio", active);
+ DocumentUndo::done(getDocument(), _("Set preserve ratio"), INKSCAPE_ICON("dialog-object-properties"));
+ }
+ _blocked = false;
+}
+
+void ObjectProperties::_hiddenToggled()
+{
+ if (_blocked) {
+ return;
+ }
+
+ SPItem *item = getSelection()->singleItem();
+ g_return_if_fail(item != nullptr);
+
+ _blocked = true;
+ item->setExplicitlyHidden(_cb_hide.get_active());
+ DocumentUndo::done(getDocument(), _cb_hide.get_active() ? _("Hide object") : _("Unhide object"), INKSCAPE_ICON("dialog-object-properties"));
+ _blocked = false;
+}
+
+void ObjectProperties::selectionChanged(Selection *selection)
+{
+ update_entries();
+}
+
+void ObjectProperties::desktopReplaced()
+{
+ update_entries();
+}
+
+}
+}
+}
+
+/*
+ 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 :