Code

patch from Gustav Broberg: undo annotations and history dialog
[inkscape.git] / src / dialogs / item-properties.cpp
1 #define __SP_ITEM_PROPERTIES_C__
3 /*
4  * Object properties dialog
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Copyright (C) 1999-2005 Authors
11  * Copyright (C) 2001 Ximian, Inc.
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
19 #include <gtk/gtkvbox.h>
20 #include <gtk/gtkhbox.h>
21 #include <gtk/gtktable.h>
22 #include <gtk/gtkcheckbutton.h>
23 #include <gtk/gtkspinbutton.h>
24 #include <gtk/gtklabel.h>
25 #include <gtk/gtkframe.h>
26 #include <gtk/gtktextview.h>
27 #include <gtk/gtktooltips.h>
29 #include <glibmm/i18n.h>
30 #include "helper/window.h"
31 #include "../widgets/sp-widget.h"
32 #include "../inkscape.h"
33 #include "../document.h"
34 #include "../desktop-handles.h"
35 #include "../selection.h"
36 #include "../sp-item.h"
37 #include "../macros.h"
38 #include "../verbs.h"
39 #include "../interface.h"
41 #include "dialog-events.h"
42 #include "../prefs-utils.h"
45 static GtkWidget *dlg = NULL;
46 static win_data wd;
48 // impossible original values to make sure they are read from prefs
49 static gint x = -1000, y = -1000, w = 0, h = 0;
50 static gchar *prefs_path = "dialogs.object";
52 static void sp_item_widget_modify_selection (SPWidget *spw, Inkscape::Selection *selection, guint flags, GtkWidget *itemw);
53 static void sp_item_widget_change_selection (SPWidget *spw, Inkscape::Selection *selection, GtkWidget *itemw);
54 static void sp_item_widget_setup (SPWidget *spw, Inkscape::Selection *selection);
55 static void sp_item_widget_sensitivity_toggled (GtkWidget *widget, SPWidget *spw);
56 static void sp_item_widget_hidden_toggled (GtkWidget *widget, SPWidget *spw);
57 static void sp_item_widget_label_changed (GtkWidget *widget, SPWidget *spw);
59 static void
60 sp_item_dialog_destroy (GtkObject *object, gpointer data)
61 {
62     sp_signal_disconnect_by_data (INKSCAPE, dlg);
63     wd.win = dlg = NULL;
64     wd.stop = 0;
65 }
67 static gboolean
68 sp_item_dialog_delete (GtkObject *object, GdkEvent *event, gpointer data)
69 {
70     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
71     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
73     if (x<0) x=0;
74     if (y<0) y=0;
76     prefs_set_int_attribute (prefs_path, "x", x);
77     prefs_set_int_attribute (prefs_path, "y", y);
78     prefs_set_int_attribute (prefs_path, "w", w);
79     prefs_set_int_attribute (prefs_path, "h", h);
81     return FALSE; // which means, go ahead and destroy it
83
85 /**
86  * \brief  Creates new instance of item properties widget 
87  *
88  */
89 GtkWidget *
90 sp_item_widget_new (void)
91 {
93     GtkWidget *spw, *vb, *t, *cb, *l, *f, *tf, *pb;
94     GtkTextBuffer *desc_buffer;
96     GtkTooltips *tt = gtk_tooltips_new();
98     /* Create container widget */
99     spw = sp_widget_new_global (INKSCAPE);
100     gtk_signal_connect ( GTK_OBJECT (spw), "modify_selection",
101                          GTK_SIGNAL_FUNC (sp_item_widget_modify_selection),
102                          spw );
103     gtk_signal_connect ( GTK_OBJECT (spw), "change_selection",
104                          GTK_SIGNAL_FUNC (sp_item_widget_change_selection),
105                          spw );
107     vb = gtk_vbox_new (FALSE, 0);
108     gtk_container_add (GTK_CONTAINER (spw), vb);
110     t = gtk_table_new (3, 4, FALSE);
111     gtk_container_set_border_width(GTK_CONTAINER(t), 4);
112     gtk_table_set_row_spacings (GTK_TABLE (t), 4);
113     gtk_table_set_col_spacings (GTK_TABLE (t), 4);
114     gtk_box_pack_start (GTK_BOX (vb), t, TRUE, TRUE, 0);
117     /* Create the label for the object id */
118     l = gtk_label_new_with_mnemonic (_("_Id"));
119     gtk_misc_set_alignment (GTK_MISC (l), 1, 0.5);
120     gtk_table_attach ( GTK_TABLE (t), l, 0, 1, 0, 1,
121                        (GtkAttachOptions)( GTK_SHRINK | GTK_FILL ),
122                        (GtkAttachOptions)0, 0, 0 );
123     gtk_object_set_data (GTK_OBJECT (spw), "id_label", l);
125     /* Create the entry box for the object id */
126     tf = gtk_entry_new ();
127     gtk_tooltips_set_tip (tt, tf, _("The id= attribute (only letters, digits, and the characters .-_: allowed)"), NULL);
128     gtk_entry_set_max_length (GTK_ENTRY (tf), 64);
129     gtk_table_attach ( GTK_TABLE (t), tf, 1, 2, 0, 1,
130                        (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
131                        (GtkAttachOptions)0, 0, 0 );
132     gtk_object_set_data (GTK_OBJECT (spw), "id", tf);
133     gtk_label_set_mnemonic_widget (GTK_LABEL(l), tf);
135     // pressing enter in the id field is the same as clicking Set:
136     g_signal_connect ( G_OBJECT (tf), "activate", G_CALLBACK (sp_item_widget_label_changed), spw);
137     // focus is in the id field initially:
138     gtk_widget_grab_focus (GTK_WIDGET (tf));
140     /* Button for setting the object's id, label, title and description. */
141     pb = gtk_button_new_with_mnemonic (_("_Set"));
142     gtk_table_attach ( GTK_TABLE (t), pb, 2, 3, 0, 1,
143                        (GtkAttachOptions)( GTK_SHRINK | GTK_FILL ),
144                        (GtkAttachOptions)0, 0, 0 );
145     gtk_signal_connect ( GTK_OBJECT (pb), "clicked",
146                          GTK_SIGNAL_FUNC (sp_item_widget_label_changed),
147                          spw );
149     /* Create the label for the object label */
150     l = gtk_label_new_with_mnemonic (_("_Label"));
151     gtk_misc_set_alignment (GTK_MISC (l), 1, 0.5);
152     gtk_table_attach ( GTK_TABLE (t), l, 0, 1, 1, 2,
153                        (GtkAttachOptions)( GTK_SHRINK | GTK_FILL ),
154                        (GtkAttachOptions)0, 0, 0 );
155     gtk_object_set_data (GTK_OBJECT (spw), "label_label", l);
157     /* Create the entry box for the object label */
158     tf = gtk_entry_new ();
159     gtk_tooltips_set_tip (tt, tf, _("A freeform label for the object"), NULL);
160     gtk_entry_set_max_length (GTK_ENTRY (tf), 256);
161     gtk_table_attach ( GTK_TABLE (t), tf, 1, 2, 1, 2,
162                        (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
163                        (GtkAttachOptions)0, 0, 0 );
164     gtk_object_set_data (GTK_OBJECT (spw), "label", tf);
165     gtk_label_set_mnemonic_widget (GTK_LABEL(l), tf);
167     // pressing enter in the label field is the same as clicking Set:
168     g_signal_connect ( G_OBJECT (tf), "activate", G_CALLBACK (sp_item_widget_label_changed), spw);
170     /* Create the label for the object title */
171     l = gtk_label_new (_("Title"));
172     gtk_misc_set_alignment (GTK_MISC (l), 1, 0.5);
173     gtk_table_attach ( GTK_TABLE (t), l, 0, 1, 2, 3,
174                        (GtkAttachOptions)( GTK_SHRINK | GTK_FILL ),
175                        (GtkAttachOptions)0, 0, 0 );
176     gtk_object_set_data (GTK_OBJECT (spw), "title_label", l);
178     /* Create the entry box for the object title */
179     tf = gtk_entry_new ();
180     gtk_widget_set_sensitive (GTK_WIDGET (tf), FALSE);
181     gtk_entry_set_max_length (GTK_ENTRY (tf), 256);
182     gtk_table_attach ( GTK_TABLE (t), tf, 1, 3, 2, 3,
183                        (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
184                        (GtkAttachOptions)0, 0, 0 );
185     gtk_object_set_data (GTK_OBJECT (spw), "title", tf);
187     /* Create the frame for the object description */
188     f = gtk_frame_new (_("Description"));
189     gtk_table_attach ( GTK_TABLE (t), f, 0, 3, 3, 4,
190                        (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
191                        (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), 0, 0 );
192     gtk_object_set_data (GTK_OBJECT (spw), "desc_frame", l);
194     /* Create the text view box for the object description */
195     GtkWidget *textframe = gtk_frame_new(NULL);
196     gtk_container_set_border_width(GTK_CONTAINER(textframe), 4);
197     gtk_widget_set_sensitive (GTK_WIDGET (textframe), FALSE);
198     gtk_container_add (GTK_CONTAINER (f), textframe);
199     gtk_frame_set_shadow_type (GTK_FRAME (textframe), GTK_SHADOW_IN);
201     tf = gtk_text_view_new();
202     desc_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tf));
203     gtk_text_buffer_set_text(desc_buffer, "", -1);
204     gtk_container_add (GTK_CONTAINER (textframe), tf);
205     gtk_object_set_data (GTK_OBJECT (spw), "desc", tf);
207     /* Check boxes */
208     GtkWidget *hb_cb = gtk_hbox_new (FALSE, 0);
209     gtk_box_pack_start (GTK_BOX (vb), hb_cb, FALSE, FALSE, 0);
210     t = gtk_table_new (1, 2, TRUE);
211     gtk_container_set_border_width(GTK_CONTAINER(t), 0);
212     gtk_box_pack_start (GTK_BOX (hb_cb), t, TRUE, TRUE, 10);
214     /* Hide */
215     cb = gtk_check_button_new_with_mnemonic (_("_Hide"));
216     gtk_tooltips_set_tip (tt, cb, _("Check to make the object invisible"), NULL);
217     gtk_table_attach ( GTK_TABLE (t), cb, 0, 1, 0, 1,
218                        (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
219                        (GtkAttachOptions)0, 0, 0 );
220     g_signal_connect (G_OBJECT(cb), "toggled", G_CALLBACK(sp_item_widget_hidden_toggled), spw);
221     gtk_object_set_data(GTK_OBJECT(spw), "hidden", cb);
223     /* Lock */
224     // TRANSLATORS: "Lock" is a verb here
225     cb = gtk_check_button_new_with_mnemonic (_("L_ock"));
226     gtk_tooltips_set_tip (tt, cb, _("Check to make the object insensitive (not selectable by mouse)"), NULL);
227     gtk_table_attach ( GTK_TABLE (t), cb, 1, 2, 0, 1,
228                        (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
229                        (GtkAttachOptions)0, 0, 0 );
230     gtk_signal_connect ( GTK_OBJECT (cb), "toggled",
231                          GTK_SIGNAL_FUNC (sp_item_widget_sensitivity_toggled),
232                          spw );
233     gtk_object_set_data (GTK_OBJECT (spw), "sensitive", cb);
235     gtk_widget_show_all (spw);
237     sp_item_widget_setup (SP_WIDGET (spw), sp_desktop_selection (SP_ACTIVE_DESKTOP));
239     return (GtkWidget *) spw;
241 } //end of sp_item_widget_new()
245 static void
246 sp_item_widget_modify_selection ( SPWidget *spw,
247                                   Inkscape::Selection *selection,
248                                   guint flags,
249                                   GtkWidget *itemw )
251     sp_item_widget_setup (spw, selection);
256 static void
257 sp_item_widget_change_selection ( SPWidget *spw,
258                                   Inkscape::Selection *selection,
259                                   GtkWidget *itemw )
261     sp_item_widget_setup (spw, selection);
265 /**
266 *  \param selection Selection to use; should not be NULL.
267 */
268 static void
269 sp_item_widget_setup ( SPWidget *spw, Inkscape::Selection *selection )
271     g_assert (selection != NULL);
273     if (gtk_object_get_data (GTK_OBJECT (spw), "blocked"))
274         return;
276     if (!selection->singleItem()) {
277         gtk_widget_set_sensitive (GTK_WIDGET (spw), FALSE);
278         return;
279     } else {
280         gtk_widget_set_sensitive (GTK_WIDGET (spw), TRUE);
281     }
283     gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (TRUE));
285     SPItem *item = selection->singleItem();
287     /* Sensitive */
288     GtkWidget *w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "sensitive"));
289     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), item->isLocked());
291     /* Hidden */
292     w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "hidden"));
293     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), item->isExplicitlyHidden());
295     if (SP_OBJECT_IS_CLONED (item)) {
297         /* ID */
298         w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id"));
299         gtk_entry_set_text (GTK_ENTRY (w), "");
300         gtk_widget_set_sensitive (w, FALSE);
301         w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id_label"));
302         gtk_label_set_text (GTK_LABEL (w), _("Ref"));
304         /* Label */
305         w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label"));
306         gtk_entry_set_text (GTK_ENTRY (w), "");
307         gtk_widget_set_sensitive (w, FALSE);
308         w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label_label"));
309         gtk_label_set_text (GTK_LABEL (w), _("Ref"));
311     } else {
312         SPObject *obj = (SPObject*)item;
314         /* ID */
315         w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id"));
316         gtk_entry_set_text (GTK_ENTRY (w), obj->id);
317         gtk_widget_set_sensitive (w, TRUE);
318         w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id_label"));
319         gtk_label_set_markup_with_mnemonic (GTK_LABEL (w), _("_Id"));
321         /* Label */
322         w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label"));
323         gtk_entry_set_text (GTK_ENTRY (w), obj->defaultLabel());
324         gtk_widget_set_sensitive (w, TRUE);
325         w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label_label"));
326     }
328     gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (FALSE));
331 } // end of sp_item_widget_setup()
335 static void
336 sp_item_widget_sensitivity_toggled (GtkWidget *widget, SPWidget *spw)
338     if (gtk_object_get_data (GTK_OBJECT (spw), "blocked"))
339         return;
341     SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
342     g_return_if_fail (item != NULL);
344     gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (TRUE));
346     item->setLocked(gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
348     sp_document_maybe_done (SP_ACTIVE_DOCUMENT,  "ItemDialog:insensitive", SP_VERB_NONE, 
349                             /* TODO: annotate */ "item-properties.cpp:349");
351     gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (FALSE));
354 void
355 sp_item_widget_hidden_toggled(GtkWidget *widget, SPWidget *spw)
357     if (gtk_object_get_data (GTK_OBJECT (spw), "blocked"))
358         return;
360     SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
361     g_return_if_fail (item != NULL);
363     gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (TRUE));
365     item->setExplicitlyHidden(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
367     sp_document_maybe_done (SP_ACTIVE_DOCUMENT,  "ItemDialog:visiblity", SP_VERB_NONE, 
368                             /* TODO: annotate */ "item-properties.cpp:368");
370     gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (FALSE));
373 static void
374 sp_item_widget_label_changed (GtkWidget *widget, SPWidget *spw)
376     if (gtk_object_get_data (GTK_OBJECT (spw), "blocked"))
377         return;
379     SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
380     g_return_if_fail (item != NULL);
382     gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (TRUE));
384     /* Retrieve the label widget for the object's id */
385     GtkWidget *id_entry = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id"));
386     gchar *id = (gchar *) gtk_entry_get_text (GTK_ENTRY (id_entry));
387     g_strcanon (id, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.:", '_');
388     GtkWidget *id_label = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id_label"));
389     if (!strcmp (id, SP_OBJECT_ID(item))) {
390         gtk_label_set_markup_with_mnemonic (GTK_LABEL (id_label), _("_Id"));
391     } else if (!*id || !isalnum (*id)) {
392         gtk_label_set_text (GTK_LABEL (id_label), _("Id invalid! "));
393     } else if (SP_ACTIVE_DOCUMENT->getObjectById(id) != NULL) {
394         gtk_label_set_text (GTK_LABEL (id_label), _("Id exists! "));
395     } else {
396         SPException ex;
397         gtk_label_set_markup_with_mnemonic (GTK_LABEL (id_label), _("_Id"));
398         SP_EXCEPTION_INIT (&ex);
399         sp_object_setAttribute (SP_OBJECT (item), "id", id, &ex);
400         sp_document_maybe_done (SP_ACTIVE_DOCUMENT, "ItemDialog:id", SP_VERB_NONE, 
401                                 /* TODO: annotate */ "item-properties.cpp:401");
402     }
404     /* Retrieve the label widget for the object's label */
405     GtkWidget *label_entry = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label"));
406     gchar *label = (gchar *)gtk_entry_get_text (GTK_ENTRY (label_entry));
407     g_assert(label != NULL);
409     /* Give feedback on success of setting the drawing object's label
410      * using the widget's label text
411      */
412     SPObject *obj = (SPObject*)item;
413     if (strcmp (label, obj->defaultLabel())) {
414         obj->setLabel(label);
415         sp_document_maybe_done (SP_ACTIVE_DOCUMENT, "inkscape:label", SP_VERB_NONE,
416                                 /* TODO: annotate */ "item-properties.cpp:416");
417     }
419     /* Retrieve the title */
420     GtkWidget *w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "title"));
421     gchar *title = (gchar *)gtk_entry_get_text (GTK_ENTRY (w));
422     if (title != NULL) {
423         obj->setTitle(title);
424         sp_document_maybe_done (SP_ACTIVE_DOCUMENT, "title", SP_VERB_NONE, 
425                                 /* TODO: annotate */ "item-properties.cpp:425");
426     }
428     /* Retrieve the description */
429     gchar *desc = NULL; /* TODO:  get text from text buffer */
430     if (desc != NULL) {
431         obj->setDesc(desc);
432         sp_document_maybe_done (SP_ACTIVE_DOCUMENT, "desc", SP_VERB_NONE, 
433                                 /* TODO: annotate */ "item-properties.cpp:433");
434     }
436     gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (FALSE));
438 } // end of sp_item_widget_label_changed()
441 /**
442  * \brief  Dialog
443  *
444  */
445 void
446 sp_item_dialog (void)
448     if (dlg == NULL) {
450         gchar title[500];
451         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_ITEM), title);
453         dlg = sp_window_new (title, TRUE);
454         if (x == -1000 || y == -1000) {
455             x = prefs_get_int_attribute (prefs_path, "x", 0);
456             y = prefs_get_int_attribute (prefs_path, "y", 0);
457         }
459         if (w ==0 || h == 0) {
460             w = prefs_get_int_attribute (prefs_path, "w", 0);
461             h = prefs_get_int_attribute (prefs_path, "h", 0);
462         }
464         if (x<0) x=0;
465         if (y<0) y=0;
467         if (x != 0 || y != 0) {
468             gtk_window_move ((GtkWindow *) dlg, x, y);
469         } else {
470             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
471         }
473         if (w && h) {
474             gtk_window_resize ((GtkWindow *) dlg, w, h);
475         }
477         sp_transientize (dlg);
478         wd.win = dlg;
479         wd.stop = 0;
481         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd);
482         gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
483         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_item_dialog_destroy), dlg);
484         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_item_dialog_delete), dlg);
485         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_item_dialog_delete), dlg);
486         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg);
487         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg);
489         // Dialog-specific stuff
490         GtkWidget *itemw = sp_item_widget_new ();
491         gtk_widget_show (itemw);
492         gtk_container_add (GTK_CONTAINER (dlg), itemw);
494     }
496     gtk_window_present ((GtkWindow *) dlg);
500 /*
501   Local Variables:
502   mode:c++
503   c-file-style:"stroustrup"
504   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
505   indent-tabs-mode:nil
506   fill-column:99
507   End:
508 */
509 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :