object-properties.cpp revision 7491f9e9bd8edddeae51d23290d0b3ef42bd70a9
134317cfacab36327a4792fbb7206310a4ade107theAdib/**
605434cbdb48bd60f0d5d4fec43a502f6e76feb4theAdib * @file Object properties dialog.
605434cbdb48bd60f0d5d4fec43a502f6e76feb4theAdib */
605434cbdb48bd60f0d5d4fec43a502f6e76feb4theAdib/*
f5bfbb0b97a5f1602e9379f3d431f59902d188c2theAdib * Inkscape, an Open Source vector graphics editor
f5bfbb0b97a5f1602e9379f3d431f59902d188c2theAdib *
f5bfbb0b97a5f1602e9379f3d431f59902d188c2theAdib * This program is free software; you can redistribute it and/or
134317cfacab36327a4792fbb7206310a4ade107theAdib * modify it under the terms of the GNU General Public License
134317cfacab36327a4792fbb7206310a4ade107theAdib * as published by the Free Software Foundation; either version 2
134317cfacab36327a4792fbb7206310a4ade107theAdib * of the License, or (at your option) any later version.
134317cfacab36327a4792fbb7206310a4ade107theAdib *
134317cfacab36327a4792fbb7206310a4ade107theAdib * This program is distributed in the hope that it will be useful,
134317cfacab36327a4792fbb7206310a4ade107theAdib * but WITHOUT ANY WARRANTY; without even the implied warranty of
134317cfacab36327a4792fbb7206310a4ade107theAdib * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
134317cfacab36327a4792fbb7206310a4ade107theAdib * GNU General Public License for more details.
f5bfbb0b97a5f1602e9379f3d431f59902d188c2theAdib *
134317cfacab36327a4792fbb7206310a4ade107theAdib * You should have received a copy of the GNU General Public License
134317cfacab36327a4792fbb7206310a4ade107theAdib * along with this program; if not, write to the Free Software
134317cfacab36327a4792fbb7206310a4ade107theAdib * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
3a0760a0ffbee7fe5eb72ac8f16ef9b53340fccdtheAdib *
134317cfacab36327a4792fbb7206310a4ade107theAdib * Copyright (C) 2012 Kris De Gussem <Kris.DeGussem@gmail.com>
134317cfacab36327a4792fbb7206310a4ade107theAdib * c++ version based on former C-version (GPL v2) with authors:
134317cfacab36327a4792fbb7206310a4ade107theAdib * Lauris Kaplinski <lauris@kaplinski.com>
f5bfbb0b97a5f1602e9379f3d431f59902d188c2theAdib * bulia byak <buliabyak@users.sf.net>
134317cfacab36327a4792fbb7206310a4ade107theAdib * Johan Engelen <goejendaagh@zonnet.nl>
134317cfacab36327a4792fbb7206310a4ade107theAdib * Abhishek Sharma
134317cfacab36327a4792fbb7206310a4ade107theAdib */
f5bfbb0b97a5f1602e9379f3d431f59902d188c2theAdib
134317cfacab36327a4792fbb7206310a4ade107theAdib#include "object-properties.h"
134317cfacab36327a4792fbb7206310a4ade107theAdib#include "widgets/sp-attribute-widget.h"
134317cfacab36327a4792fbb7206310a4ade107theAdib#include "../../desktop-handles.h"
134317cfacab36327a4792fbb7206310a4ade107theAdib#include "../../document.h"
#include "../../document-undo.h"
#include "verbs.h"
#include "inkscape.h"
#include "selection.h"
#include "desktop.h"
#include "sp-item.h"
#include <glibmm/i18n.h>
namespace Inkscape {
namespace UI {
namespace Dialog {
ObjectProperties::ObjectProperties (void) :
UI::Widget::Panel ("", "/dialogs/object/", SP_VERB_DIALOG_ITEM),
blocked (false),
CurrentItem(NULL),
#if WITH_GTKMM_3_0
TopTable(Gtk::manage(new Gtk::Grid())),
#else
TopTable(Gtk::manage(new Gtk::Table(3, 4))),
#endif
LabelID(_("_ID:"), 1),
LabelLabel(_("_Label:"), 1),
LabelTitle(_("_Title:"),1),
LabelDescription(_("_Description:"),1),
FrameDescription("", FALSE),
HBoxCheck(FALSE, 0),
#if WITH_GTKMM_3_0
CheckTable(Gtk::manage(new Gtk::Grid())),
#else
CheckTable(Gtk::manage(new Gtk::Table(1, 2, true))),
#endif
CBHide(_("_Hide"), 1),
CBLock(_("L_ock"), 1),
BSet (_("_Set"), 1),
LabelInteractivity(_("_Interactivity"), 1),
attrTable(Gtk::manage(new SPAttributeTable())),
desktop(NULL),
deskTrack(),
selectChangedConn(),
subselChangedConn()
{
//initialize labels for the table at the bottom of the dialog
int_attrs.push_back("onclick");
int_attrs.push_back("onmouseover");
int_attrs.push_back("onmouseout");
int_attrs.push_back("onmousedown");
int_attrs.push_back("onmouseup");
int_attrs.push_back("onmousemove");
int_attrs.push_back("onfocusin");
int_attrs.push_back("onfocusout");
int_attrs.push_back("onload");
int_labels.push_back("onclick:");
int_labels.push_back("onmouseover:");
int_labels.push_back("onmouseout:");
int_labels.push_back("onmousedown:");
int_labels.push_back("onmouseup:");
int_labels.push_back("onmousemove:");
int_labels.push_back("onfocusin:");
int_labels.push_back("onfocusout:");
int_labels.push_back("onload:");
desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &ObjectProperties::setTargetDesktop) );
deskTrack.connect(GTK_WIDGET(gobj()));
#if WITH_GTKMM_3_0
CheckTable->set_row_homogeneous();
CheckTable->set_column_homogeneous(true);
#endif
MakeWidget();
}
ObjectProperties::~ObjectProperties (void)
{
subselChangedConn.disconnect();
selectChangedConn.disconnect();
desktopChangeConn.disconnect();
deskTrack.disconnect();
}
void ObjectProperties::MakeWidget(void)
{
Gtk::Box *contents = _getContents();
contents->set_spacing(0);
TopTable->set_border_width(4);
#if WITH_GTKMM_3_0
TopTable->set_row_spacing(4);
TopTable->set_column_spacing(0);
#else
TopTable->set_row_spacings(4);
TopTable->set_col_spacings(0);
#endif
contents->pack_start (*TopTable, false, false, 0);
/* Create the label for the object id */
LabelID.set_label (LabelID.get_label() + " ");
LabelID.set_alignment (1, 0.5);
#if WITH_GTKMM_3_0
LabelID.set_valign(Gtk::ALIGN_CENTER);
TopTable->attach(LabelID, 0, 0, 1, 1);
#else
TopTable->attach(LabelID, 0, 1, 0, 1,
Gtk::SHRINK | Gtk::FILL,
Gtk::AttachOptions(), 0, 0 );
#endif
/* Create the entry box for the object id */
EntryID.set_tooltip_text (_("The id= attribute (only letters, digits, and the characters .-_: allowed)"));
EntryID.set_max_length (64);
#if WITH_GTKMM_3_0
EntryID.set_valign(Gtk::ALIGN_CENTER);
TopTable->attach(EntryID, 1, 0, 1, 1);
#else
TopTable->attach(EntryID, 1, 2, 0, 1,
Gtk::EXPAND | Gtk::FILL,
Gtk::AttachOptions(), 0, 0 );
#endif
LabelID.set_mnemonic_widget (EntryID);
// pressing enter in the id field is the same as clicking Set:
EntryID.signal_activate().connect(sigc::mem_fun(this, &ObjectProperties::label_changed));
// focus is in the id field initially:
EntryID.grab_focus();
/* Create the label for the object label */
LabelLabel.set_label (LabelLabel.get_label() + " ");
LabelLabel.set_alignment (1, 0.5);
#if WITH_GTKMM_3_0
LabelLabel.set_valign(Gtk::ALIGN_CENTER);
TopTable->attach(LabelLabel, 0, 1, 1, 1);
#else
TopTable->attach(LabelLabel, 0, 1, 1, 2,
Gtk::SHRINK | Gtk::FILL,
Gtk::AttachOptions(), 0, 0 );
#endif
/* Create the entry box for the object label */
EntryLabel.set_tooltip_text (_("A freeform label for the object"));
EntryLabel.set_max_length (256);
#if WITH_GTKMM_3_0
EntryLabel.set_hexpand();
EntryLabel.set_valign(Gtk::ALIGN_CENTER);
TopTable->attach(EntryLabel, 1, 1, 1, 1);
#else
TopTable->attach(EntryLabel, 1, 2, 1, 2,
Gtk::EXPAND | Gtk::FILL,
Gtk::AttachOptions(), 0, 0 );
#endif
LabelLabel.set_mnemonic_widget (EntryLabel);
// pressing enter in the label field is the same as clicking Set:
EntryLabel.signal_activate().connect(sigc::mem_fun(this, &ObjectProperties::label_changed));
/* Create the label for the object title */
LabelTitle.set_label (LabelTitle.get_label() + " ");
LabelTitle.set_alignment (1, 0.5);
#if WITH_GTKMM_3_0
LabelTitle.set_valign(Gtk::ALIGN_CENTER);
TopTable->attach(LabelTitle, 0, 2, 1, 1);
#else
TopTable->attach(LabelTitle, 0, 1, 2, 3,
Gtk::SHRINK | Gtk::FILL,
Gtk::AttachOptions(), 0, 0 );
#endif
/* Create the entry box for the object title */
EntryTitle.set_sensitive (FALSE);
EntryTitle.set_max_length (256);
#if WITH_GTKMM_3_0
EntryTitle.set_hexpand();
EntryTitle.set_valign(Gtk::ALIGN_CENTER);
TopTable->attach(EntryTitle, 1, 2, 1, 1);
#else
TopTable->attach(EntryTitle, 1, 2, 2, 3,
Gtk::EXPAND | Gtk::FILL,
Gtk::AttachOptions(), 0, 0 );
#endif
LabelTitle.set_mnemonic_widget (EntryTitle);
// pressing enter in the label field is the same as clicking Set:
EntryTitle.signal_activate().connect(sigc::mem_fun(this, &ObjectProperties::label_changed));
/* Create the frame for the object description */
FrameDescription.set_label_widget (LabelDescription);
FrameDescription.set_padding (0,0,0,0);
contents->pack_start (FrameDescription, true, true, 0);
/* Create the text view box for the object description */
FrameTextDescription.set_border_width(4);
FrameTextDescription.set_sensitive (FALSE);
FrameDescription.add (FrameTextDescription);
FrameTextDescription.set_shadow_type (Gtk::SHADOW_IN);
TextViewDescription.set_wrap_mode(Gtk::WRAP_WORD);
TextViewDescription.get_buffer()->set_text("");
FrameTextDescription.add (TextViewDescription);
TextViewDescription.add_mnemonic_label(LabelDescription);
/* Check boxes */
contents->pack_start (HBoxCheck, FALSE, FALSE, 0);
CheckTable->set_border_width(4);
HBoxCheck.pack_start(*CheckTable, true, true, 0);
/* Hide */
CBHide.set_tooltip_text (_("Check to make the object invisible"));
#if WITH_GTKMM_3_0
CBHide.set_hexpand();
CBHide.set_valign(Gtk::ALIGN_CENTER);
CheckTable->attach(CBHide, 0, 0, 1, 1);
#else
CheckTable->attach(CBHide, 0, 1, 0, 1,
Gtk::EXPAND | Gtk::FILL,
Gtk::AttachOptions(), 0, 0 );
#endif
CBHide.signal_toggled().connect(sigc::mem_fun(this, &ObjectProperties::hidden_toggled));
/* Lock */
// TRANSLATORS: "Lock" is a verb here
CBLock.set_tooltip_text (_("Check to make the object insensitive (not selectable by mouse)"));
#if WITH_GTKMM_3_0
CBLock.set_hexpand();
CBLock.set_valign(Gtk::ALIGN_CENTER);
CheckTable->attach(CBLock, 1, 0, 1, 1);
#else
CheckTable->attach(CBLock, 1, 2, 0, 1,
Gtk::EXPAND | Gtk::FILL,
Gtk::AttachOptions(), 0, 0 );
#endif
CBLock.signal_toggled().connect(sigc::mem_fun(this, &ObjectProperties::sensitivity_toggled));
/* Button for setting the object's id, label, title and description. */
#if WITH_GTKMM_3_0
BSet.set_hexpand();
BSet.set_valign(Gtk::ALIGN_CENTER);
CheckTable->attach(BSet, 2, 0, 1, 1);
#else
CheckTable->attach(BSet, 2, 3, 0, 1,
Gtk::EXPAND | Gtk::FILL,
Gtk::AttachOptions(), 0, 0 );
#endif
BSet.signal_clicked().connect(sigc::mem_fun(this, &ObjectProperties::label_changed));
/* Create the frame for interactivity options */
EInteractivity.set_label_widget (LabelInteractivity);
contents->pack_start (EInteractivity, FALSE, FALSE, 0);
show_all ();
widget_setup();
}
void ObjectProperties::widget_setup(void)
{
if (blocked || !desktop)
{
return;
}
if (SP_ACTIVE_DESKTOP != desktop)
{
return;
}
Inkscape::Selection *selection = sp_desktop_selection (SP_ACTIVE_DESKTOP);
Gtk::Box *contents = _getContents();
if (!selection->singleItem()) {
contents->set_sensitive (false);
CurrentItem = NULL;
//no selection anymore or multiple objects selected, means that we need
//to close the connections to the previously selected object
attrTable->clear();
return;
} else {
contents->set_sensitive (true);
}
SPItem *item = selection->singleItem();
if (CurrentItem == item)
{
//otherwise we would end up wasting resources through the modify selection
//callback when moving an object (endlessly setting the labels and recreating attrTable)
return;
}
blocked = true;
CBLock.set_active (item->isLocked()); /* Sensitive */
CBHide.set_active (item->isExplicitlyHidden()); /* Hidden */
if (item->cloned) {
/* ID */
EntryID.set_text ("");
EntryID.set_sensitive (FALSE);
LabelID.set_text (_("Ref"));
/* Label */
EntryLabel.set_text ("");
EntryLabel.set_sensitive (FALSE);
LabelLabel.set_text (_("Ref"));
} else {
SPObject *obj = static_cast<SPObject*>(item);
/* ID */
EntryID.set_text (obj->getId());
EntryID.set_sensitive (TRUE);
LabelID.set_markup_with_mnemonic (_("_ID:"));
/* Label */
EntryLabel.set_text(obj->defaultLabel());
EntryLabel.set_sensitive (TRUE);
/* Title */
gchar *title = obj->title();
if (title) {
EntryTitle.set_text(title);
g_free(title);
}
else {
EntryTitle.set_text("");
}
EntryTitle.set_sensitive(TRUE);
/* Description */
gchar *desc = obj->desc();
if (desc) {
TextViewDescription.get_buffer()->set_text(desc);
g_free(desc);
} else {
TextViewDescription.get_buffer()->set_text("");
}
FrameTextDescription.set_sensitive(TRUE);
if (CurrentItem == NULL)
{
attrTable->set_object(obj, int_labels, int_attrs, (GtkWidget*)EInteractivity.gobj());
}
else
{
attrTable->change_object(obj);
}
attrTable->show_all();
}
CurrentItem = item;
blocked = false;
}
void ObjectProperties::label_changed(void)
{
if (blocked)
{
return;
}
SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
g_return_if_fail (item != NULL);
blocked = true;
/* Retrieve the label widget for the object's id */
gchar *id = g_strdup(EntryID.get_text().c_str());
g_strcanon (id, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.:", '_');
if (!strcmp (id, item->getId())) {
LabelID.set_markup_with_mnemonic(_("_ID:"));
} else if (!*id || !isalnum (*id)) {
LabelID.set_text (_("Id invalid! "));
} else if (SP_ACTIVE_DOCUMENT->getObjectById(id) != NULL) {
LabelID.set_text (_("Id exists! "));
} else {
SPException ex;
LabelID.set_markup_with_mnemonic(_("_ID:"));
SP_EXCEPTION_INIT (&ex);
item->setAttribute("id", id, &ex);
DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM, _("Set object ID"));
}
g_free (id);
/* Retrieve the label widget for the object's label */
Glib::ustring label = EntryLabel.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);
if (label.compare (obj->defaultLabel())) {
obj->setLabel(label.c_str());
DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM,
_("Set object label"));
}
/* Retrieve the title */
if (obj->setTitle(EntryTitle.get_text().c_str()))
DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM,
_("Set object title"));
/* Retrieve the description */
Gtk::TextBuffer::iterator start, end;
TextViewDescription.get_buffer()->get_bounds(start, end);
Glib::ustring desc = TextViewDescription.get_buffer()->get_text(start, end, TRUE);
if (obj->setDesc(desc.c_str()))
DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM,
_("Set object description"));
blocked = false;
}
void ObjectProperties::sensitivity_toggled (void)
{
if (blocked)
{
return;
}
SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
g_return_if_fail (item != NULL);
blocked = true;
item->setLocked(CBLock.get_active());
DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM,
CBLock.get_active()? _("Lock object") : _("Unlock object"));
blocked = false;
}
void ObjectProperties::hidden_toggled(void)
{
if (blocked)
{
return;
}
SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
g_return_if_fail (item != NULL);
blocked = true;
item->setExplicitlyHidden(CBHide.get_active());
DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM,
CBHide.get_active()? _("Hide object") : _("Unhide object"));
blocked = false;
}
void ObjectProperties::setDesktop(SPDesktop *desktop)
{
Panel::setDesktop(desktop);
deskTrack.setBase(desktop);
}
void ObjectProperties::setTargetDesktop(SPDesktop *desktop)
{
if (this->desktop != desktop) {
if (this->desktop) {
subselChangedConn.disconnect();
selectChangedConn.disconnect();
}
this->desktop = desktop;
if (desktop && desktop->selection) {
selectChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(*this, &ObjectProperties::widget_setup)));
subselChangedConn = desktop->connectToolSubselectionChanged(sigc::hide(sigc::mem_fun(*this, &ObjectProperties::widget_setup)));
}
widget_setup();
}
}
}
}
}
/*
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 :