1 #define __SP_ATTRIBUTE_WIDGET_C__
3 /**
4 * \brief SPAttributeWidget
5 *
6 * Widget, that listens and modifies repr attributes
7 *
8 * Authors:
9 * Lauris Kaplinski <lauris@ximian.com>
10 *
11 * Copyright (C) 2001 Ximian, Inc.
12 *
13 * Licensed under GNU GPL
14 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #include <gtk/gtktable.h>
20 #include <gtk/gtklabel.h>
21 #include "xml/repr.h"
22 #include "macros.h"
23 #include "document.h"
24 #include "sp-object.h"
25 #include <glibmm/i18n.h>
27 #include <sigc++/functors/ptr_fun.h>
28 #include <sigc++/adaptors/bind.h>
30 #include "sp-attribute-widget.h"
32 static void sp_attribute_widget_class_init (SPAttributeWidgetClass *klass);
33 static void sp_attribute_widget_init (SPAttributeWidget *widget);
34 static void sp_attribute_widget_destroy (GtkObject *object);
36 static void sp_attribute_widget_changed (GtkEditable *editable);
38 static void sp_attribute_widget_object_modified ( SPObject *object,
39 guint flags,
40 SPAttributeWidget *spaw );
41 static void sp_attribute_widget_object_release ( SPObject *object,
42 SPAttributeWidget *spaw );
44 static GtkEntryClass *parent_class;
49 GtkType
50 sp_attribute_widget_get_type (void)
51 {
52 static GtkType type = 0;
53 if (!type) {
54 static const GtkTypeInfo info = {
55 "SPAttributeWidget",
56 sizeof (SPAttributeWidget),
57 sizeof (SPAttributeWidgetClass),
58 (GtkClassInitFunc) sp_attribute_widget_class_init,
59 (GtkObjectInitFunc) sp_attribute_widget_init,
60 NULL, NULL, NULL
61 };
62 type = gtk_type_unique (GTK_TYPE_ENTRY, &info);
63 }
64 return type;
66 } // end of sp_attribute_widget_get_type()
70 static void
71 sp_attribute_widget_class_init (SPAttributeWidgetClass *klass)
72 {
73 GtkObjectClass *object_class;
74 GtkWidgetClass *widget_class;
75 GtkEditableClass *editable_class;
77 object_class = GTK_OBJECT_CLASS (klass);
78 widget_class = GTK_WIDGET_CLASS (klass);
79 editable_class = GTK_EDITABLE_CLASS (klass);
81 parent_class = (GtkEntryClass*)gtk_type_class (GTK_TYPE_ENTRY);
83 object_class->destroy = sp_attribute_widget_destroy;
85 editable_class->changed = sp_attribute_widget_changed;
87 } // end of sp_attribute_widget_class_init()
91 static void
92 sp_attribute_widget_init (SPAttributeWidget *spaw)
93 {
94 spaw->blocked = FALSE;
95 spaw->hasobj = FALSE;
97 spaw->src.object = NULL;
99 spaw->attribute = NULL;
101 new (&spaw->modified_connection) sigc::connection();
102 new (&spaw->release_connection) sigc::connection();
103 }
107 static void
108 sp_attribute_widget_destroy (GtkObject *object)
109 {
111 SPAttributeWidget *spaw;
113 spaw = SP_ATTRIBUTE_WIDGET (object);
115 if (spaw->attribute) {
116 g_free (spaw->attribute);
117 spaw->attribute = NULL;
118 }
121 if (spaw->hasobj) {
123 if (spaw->src.object) {
124 spaw->modified_connection.disconnect();
125 spaw->release_connection.disconnect();
126 spaw->src.object = NULL;
127 }
128 } else {
130 if (spaw->src.repr) {
131 spaw->src.repr = Inkscape::GC::release(spaw->src.repr);
132 }
133 } // end of if()
135 spaw->modified_connection.~connection();
136 spaw->release_connection.~connection();
138 ((GtkObjectClass *) parent_class)->destroy (object);
140 }
144 static void
145 sp_attribute_widget_changed (GtkEditable *editable)
146 {
148 SPAttributeWidget *spaw;
150 spaw = SP_ATTRIBUTE_WIDGET (editable);
152 if (!spaw->blocked) {
154 const gchar *text;
155 spaw->blocked = TRUE;
156 text = gtk_entry_get_text (GTK_ENTRY (spaw));
157 if (!*text)
158 text = NULL;
160 if (spaw->hasobj && spaw->src.object) {
162 SP_OBJECT_REPR (spaw->src.object)->setAttribute(spaw->attribute, text, false);
163 sp_document_done (SP_OBJECT_DOCUMENT (spaw->src.object), SP_VERB_NONE,
164 _("Set attribute"));
166 } else if (spaw->src.repr) {
168 spaw->src.repr->setAttribute(spaw->attribute, text, false);
169 /* TODO: Warning! Undo will not be flushed in given case */
170 }
171 spaw->blocked = FALSE;
172 }
174 } // end of sp_attribute_widget_changed()
178 GtkWidget *
179 sp_attribute_widget_new ( SPObject *object, const gchar *attribute )
180 {
181 SPAttributeWidget *spaw;
183 g_return_val_if_fail (!object || SP_IS_OBJECT (object), NULL);
184 g_return_val_if_fail (!object || attribute, NULL);
186 spaw = (SPAttributeWidget*)gtk_type_new (SP_TYPE_ATTRIBUTE_WIDGET);
188 sp_attribute_widget_set_object (spaw, object, attribute);
190 return GTK_WIDGET (spaw);
192 } // end of sp_attribute_widget_new()
196 GtkWidget *
197 sp_attribute_widget_new_repr ( Inkscape::XML::Node *repr, const gchar *attribute )
198 {
199 SPAttributeWidget *spaw;
201 spaw = (SPAttributeWidget*)gtk_type_new (SP_TYPE_ATTRIBUTE_WIDGET);
203 sp_attribute_widget_set_repr (spaw, repr, attribute);
205 return GTK_WIDGET (spaw);
206 }
210 void
211 sp_attribute_widget_set_object ( SPAttributeWidget *spaw,
212 SPObject *object,
213 const gchar *attribute )
214 {
216 g_return_if_fail (spaw != NULL);
217 g_return_if_fail (SP_IS_ATTRIBUTE_WIDGET (spaw));
218 g_return_if_fail (!object || SP_IS_OBJECT (object));
219 g_return_if_fail (!object || attribute);
220 g_return_if_fail (attribute != NULL);
222 if (spaw->attribute) {
223 g_free (spaw->attribute);
224 spaw->attribute = NULL;
225 }
227 if (spaw->hasobj) {
229 if (spaw->src.object) {
230 spaw->modified_connection.disconnect();
231 spaw->release_connection.disconnect();
232 spaw->src.object = NULL;
233 }
234 } else {
236 if (spaw->src.repr) {
237 spaw->src.repr = Inkscape::GC::release(spaw->src.repr);
238 }
239 }
241 spaw->hasobj = TRUE;
243 if (object) {
244 const gchar *val;
246 spaw->blocked = TRUE;
247 spaw->src.object = object;
249 spaw->modified_connection = object->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_attribute_widget_object_modified), spaw));
250 spaw->release_connection = object->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_attribute_widget_object_release), spaw));
252 spaw->attribute = g_strdup (attribute);
254 val = SP_OBJECT_REPR (object)->attribute(attribute);
255 gtk_entry_set_text (GTK_ENTRY (spaw), val ? val : (const gchar *) "");
256 spaw->blocked = FALSE;
257 }
259 gtk_widget_set_sensitive (GTK_WIDGET (spaw), (spaw->src.object != NULL));
261 } // end of sp_attribute_widget_set_object()
265 void
266 sp_attribute_widget_set_repr ( SPAttributeWidget *spaw,
267 Inkscape::XML::Node *repr,
268 const gchar *attribute )
269 {
271 g_return_if_fail (spaw != NULL);
272 g_return_if_fail (SP_IS_ATTRIBUTE_WIDGET (spaw));
273 g_return_if_fail (attribute != NULL);
275 if (spaw->attribute) {
276 g_free (spaw->attribute);
277 spaw->attribute = NULL;
278 }
280 if (spaw->hasobj) {
282 if (spaw->src.object) {
283 spaw->modified_connection.disconnect();
284 spaw->release_connection.disconnect();
285 spaw->src.object = NULL;
286 }
287 } else {
289 if (spaw->src.repr) {
290 spaw->src.repr = Inkscape::GC::release(spaw->src.repr);
291 }
292 }
294 spaw->hasobj = FALSE;
296 if (repr) {
297 const gchar *val;
299 spaw->blocked = TRUE;
300 spaw->src.repr = Inkscape::GC::anchor(repr);
301 spaw->attribute = g_strdup (attribute);
303 val = repr->attribute(attribute);
304 gtk_entry_set_text (GTK_ENTRY (spaw), val ? val : (const gchar *) "");
305 spaw->blocked = FALSE;
306 }
308 gtk_widget_set_sensitive (GTK_WIDGET (spaw), (spaw->src.repr != NULL));
310 } // end of sp_attribute_widget_set_repr()
314 static void
315 sp_attribute_widget_object_modified ( SPObject */*object*/,
316 guint flags,
317 SPAttributeWidget *spaw )
318 {
320 if (flags && SP_OBJECT_MODIFIED_FLAG) {
322 const gchar *val, *text;
323 val = SP_OBJECT_REPR (spaw->src.object)->attribute(spaw->attribute);
324 text = gtk_entry_get_text (GTK_ENTRY (spaw));
326 if (val || text) {
328 if (!val || !text || strcmp (val, text)) {
329 /* We are different */
330 spaw->blocked = TRUE;
331 gtk_entry_set_text ( GTK_ENTRY (spaw),
332 val ? val : (const gchar *) "");
333 spaw->blocked = FALSE;
334 } // end of if()
336 } // end of if()
338 } //end of if()
340 } // end of sp_attribute_widget_object_modified()
344 static void
345 sp_attribute_widget_object_release ( SPObject */*object*/,
346 SPAttributeWidget *spaw )
347 {
348 sp_attribute_widget_set_object (spaw, NULL, NULL);
349 }
353 /* SPAttributeTable */
355 static void sp_attribute_table_class_init (SPAttributeTableClass *klass);
356 static void sp_attribute_table_init (SPAttributeTable *widget);
357 static void sp_attribute_table_destroy (GtkObject *object);
359 static void sp_attribute_table_object_modified (SPObject *object, guint flags, SPAttributeTable *spaw);
360 static void sp_attribute_table_object_release (SPObject *object, SPAttributeTable *spaw);
361 static void sp_attribute_table_entry_changed (GtkEditable *editable, SPAttributeTable *spat);
363 static GtkVBoxClass *table_parent_class;
368 GtkType
369 sp_attribute_table_get_type (void)
370 {
371 static GtkType type = 0;
372 if (!type) {
373 static const GtkTypeInfo info = {
374 "SPAttributeTable",
375 sizeof (SPAttributeTable),
376 sizeof (SPAttributeTableClass),
377 (GtkClassInitFunc) sp_attribute_table_class_init,
378 (GtkObjectInitFunc) sp_attribute_table_init,
379 NULL, NULL, NULL
380 };
381 type = gtk_type_unique (GTK_TYPE_VBOX, &info);
382 }
383 return type;
385 } // end of sp_attribute_table_get_type()
389 static void
390 sp_attribute_table_class_init (SPAttributeTableClass *klass)
391 {
392 GtkObjectClass *object_class;
393 GtkWidgetClass *widget_class;
395 object_class = GTK_OBJECT_CLASS (klass);
396 widget_class = GTK_WIDGET_CLASS (klass);
398 table_parent_class = (GtkVBoxClass*)gtk_type_class (GTK_TYPE_VBOX);
400 object_class->destroy = sp_attribute_table_destroy;
402 } // end of sp_attribute_table_class_init()
406 static void
407 sp_attribute_table_init ( SPAttributeTable *spat )
408 {
409 spat->blocked = FALSE;
410 spat->hasobj = FALSE;
411 spat->table = NULL;
412 spat->src.object = NULL;
413 spat->num_attr = 0;
414 spat->attributes = NULL;
415 spat->entries = NULL;
417 new (&spat->modified_connection) sigc::connection();
418 new (&spat->release_connection) sigc::connection();
419 }
421 static void
422 sp_attribute_table_destroy ( GtkObject *object )
423 {
424 SPAttributeTable *spat;
426 spat = SP_ATTRIBUTE_TABLE (object);
428 if (spat->attributes) {
429 gint i;
430 for (i = 0; i < spat->num_attr; i++) {
431 g_free (spat->attributes[i]);
432 }
433 g_free (spat->attributes);
434 spat->attributes = NULL;
435 }
437 if (spat->hasobj) {
439 if (spat->src.object) {
440 spat->modified_connection.disconnect();
441 spat->release_connection.disconnect();
442 spat->src.object = NULL;
443 }
444 } else {
445 if (spat->src.repr) {
446 spat->src.repr = Inkscape::GC::release(spat->src.repr);
447 }
448 } // end of if()
450 spat->modified_connection.~connection();
451 spat->release_connection.~connection();
453 if (spat->entries) {
454 g_free (spat->entries);
455 spat->entries = NULL;
456 }
458 spat->table = NULL;
460 if (((GtkObjectClass *) table_parent_class)->destroy) {
461 (* ((GtkObjectClass *) table_parent_class)->destroy) (object);
462 }
464 } // end of sp_attribute_table_destroy()
467 GtkWidget *
468 sp_attribute_table_new ( SPObject *object,
469 gint num_attr,
470 const gchar **labels,
471 const gchar **attributes )
472 {
473 SPAttributeTable *spat;
475 g_return_val_if_fail (!object || SP_IS_OBJECT (object), NULL);
476 g_return_val_if_fail (!object || (num_attr > 0), NULL);
477 g_return_val_if_fail (!num_attr || (labels && attributes), NULL);
479 spat = (SPAttributeTable*)gtk_type_new (SP_TYPE_ATTRIBUTE_TABLE);
481 sp_attribute_table_set_object (spat, object, num_attr, labels, attributes);
483 return GTK_WIDGET (spat);
485 } // end of sp_attribute_table_new()
489 GtkWidget *
490 sp_attribute_table_new_repr ( Inkscape::XML::Node *repr,
491 gint num_attr,
492 const gchar **labels,
493 const gchar **attributes )
494 {
495 SPAttributeTable *spat;
497 g_return_val_if_fail (!num_attr || (labels && attributes), NULL);
499 spat = (SPAttributeTable*)gtk_type_new (SP_TYPE_ATTRIBUTE_TABLE);
501 sp_attribute_table_set_repr (spat, repr, num_attr, labels, attributes);
503 return GTK_WIDGET (spat);
505 } // end of sp_attribute_table_new_repr()
509 #define XPAD 4
510 #define YPAD 0
512 void
513 sp_attribute_table_set_object ( SPAttributeTable *spat,
514 SPObject *object,
515 gint num_attr,
516 const gchar **labels,
517 const gchar **attributes )
518 {
520 g_return_if_fail (spat != NULL);
521 g_return_if_fail (SP_IS_ATTRIBUTE_TABLE (spat));
522 g_return_if_fail (!object || SP_IS_OBJECT (object));
523 g_return_if_fail (!object || (num_attr > 0));
524 g_return_if_fail (!num_attr || (labels && attributes));
526 if (spat->table) {
527 gtk_widget_destroy (spat->table);
528 spat->table = NULL;
529 }
531 if (spat->attributes) {
532 gint i;
533 for (i = 0; i < spat->num_attr; i++) {
534 g_free (spat->attributes[i]);
535 }
536 g_free (spat->attributes);
537 spat->attributes = NULL;
538 }
540 if (spat->entries) {
541 g_free (spat->entries);
542 spat->entries = NULL;
543 }
545 if (spat->hasobj) {
546 if (spat->src.object) {
547 spat->modified_connection.disconnect();
548 spat->release_connection.disconnect();
549 spat->src.object = NULL;
550 }
551 } else {
552 if (spat->src.repr) {
553 spat->src.repr = Inkscape::GC::release(spat->src.repr);
554 }
555 }
557 spat->hasobj = TRUE;
559 if (object) {
560 gint i;
562 spat->blocked = TRUE;
564 /* Set up object */
565 spat->src.object = object;
566 spat->num_attr = num_attr;
568 spat->modified_connection = object->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_attribute_table_object_modified), spat));
569 spat->release_connection = object->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_attribute_table_object_release), spat));
571 /* Create table */
572 spat->table = gtk_table_new (num_attr, 2, FALSE);
573 gtk_container_add (GTK_CONTAINER (spat), spat->table);
574 /* Arrays */
575 spat->attributes = g_new0 (gchar *, num_attr);
576 spat->entries = g_new0 (GtkWidget *, num_attr);
577 /* Fill rows */
578 for (i = 0; i < num_attr; i++) {
579 GtkWidget *w;
580 const gchar *val;
582 spat->attributes[i] = g_strdup (attributes[i]);
583 w = gtk_label_new (_(labels[i]));
584 gtk_widget_show (w);
585 gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
586 gtk_table_attach ( GTK_TABLE (spat->table), w, 0, 1, i, i + 1,
587 GTK_FILL,
588 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
589 XPAD, YPAD );
590 w = gtk_entry_new ();
591 gtk_widget_show (w);
592 val = SP_OBJECT_REPR (object)->attribute(attributes[i]);
593 gtk_entry_set_text (GTK_ENTRY (w), val ? val : (const gchar *) "");
594 gtk_table_attach ( GTK_TABLE (spat->table), w, 1, 2, i, i + 1,
595 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
596 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
597 XPAD, YPAD );
598 spat->entries[i] = w;
599 g_signal_connect ( G_OBJECT (w), "changed",
600 G_CALLBACK (sp_attribute_table_entry_changed),
601 spat );
602 }
603 /* Show table */
604 gtk_widget_show (spat->table);
606 spat->blocked = FALSE;
607 }
609 gtk_widget_set_sensitive ( GTK_WIDGET (spat),
610 (spat->src.object != NULL) );
612 } // end of sp_attribute_table_set_object()
616 void
617 sp_attribute_table_set_repr ( SPAttributeTable *spat,
618 Inkscape::XML::Node *repr,
619 gint num_attr,
620 const gchar **labels,
621 const gchar **attributes )
622 {
623 g_return_if_fail (spat != NULL);
624 g_return_if_fail (SP_IS_ATTRIBUTE_TABLE (spat));
625 g_return_if_fail (!num_attr || (labels && attributes));
627 if (spat->table) {
628 gtk_widget_destroy (spat->table);
629 spat->table = NULL;
630 }
632 if (spat->attributes) {
633 gint i;
634 for (i = 0; i < spat->num_attr; i++) {
635 g_free (spat->attributes[i]);
636 }
637 g_free (spat->attributes);
638 spat->attributes = NULL;
639 }
641 if (spat->entries) {
642 g_free (spat->entries);
643 spat->entries = NULL;
644 }
646 if (spat->hasobj) {
647 if (spat->src.object) {
648 spat->modified_connection.disconnect();
649 spat->release_connection.disconnect();
650 spat->src.object = NULL;
651 }
652 } else {
653 if (spat->src.repr) {
654 spat->src.repr = Inkscape::GC::release(spat->src.repr);
655 }
656 }
658 spat->hasobj = FALSE;
660 if (repr) {
661 gint i;
663 spat->blocked = TRUE;
665 /* Set up repr */
666 spat->src.repr = Inkscape::GC::anchor(repr);
667 spat->num_attr = num_attr;
668 /* Create table */
669 spat->table = gtk_table_new (num_attr, 2, FALSE);
670 gtk_container_add (GTK_CONTAINER (spat), spat->table);
671 /* Arrays */
672 spat->attributes = g_new0 (gchar *, num_attr);
673 spat->entries = g_new0 (GtkWidget *, num_attr);
675 /* Fill rows */
676 for (i = 0; i < num_attr; i++) {
677 GtkWidget *w;
678 const gchar *val;
680 spat->attributes[i] = g_strdup (attributes[i]);
681 w = gtk_label_new (labels[i]);
682 gtk_widget_show (w);
683 gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
684 gtk_table_attach ( GTK_TABLE (spat->table), w, 0, 1, i, i + 1,
685 GTK_FILL,
686 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
687 XPAD, YPAD );
688 w = gtk_entry_new ();
689 gtk_widget_show (w);
690 val = repr->attribute(attributes[i]);
691 gtk_entry_set_text (GTK_ENTRY (w), val ? val : (const gchar *) "");
692 gtk_table_attach ( GTK_TABLE (spat->table), w, 1, 2, i, i + 1,
693 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
694 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
695 XPAD, YPAD );
696 spat->entries[i] = w;
697 g_signal_connect ( G_OBJECT (w), "changed",
698 G_CALLBACK (sp_attribute_table_entry_changed),
699 spat );
700 }
701 /* Show table */
702 gtk_widget_show (spat->table);
704 spat->blocked = FALSE;
705 }
707 gtk_widget_set_sensitive (GTK_WIDGET (spat), (spat->src.repr != NULL));
709 } // end of sp_attribute_table_set_repr()
713 static void
714 sp_attribute_table_object_modified ( SPObject */*object*/,
715 guint flags,
716 SPAttributeTable *spat )
717 {
718 if (flags && SP_OBJECT_MODIFIED_FLAG)
719 {
720 gint i;
721 for (i = 0; i < spat->num_attr; i++) {
722 const gchar *val, *text;
723 val = SP_OBJECT_REPR (spat->src.object)->attribute(spat->attributes[i]);
724 text = gtk_entry_get_text (GTK_ENTRY (spat->entries[i]));
725 if (val || text) {
726 if (!val || !text || strcmp (val, text)) {
727 /* We are different */
728 spat->blocked = TRUE;
729 gtk_entry_set_text ( GTK_ENTRY (spat->entries[i]),
730 val ? val : (const gchar *) "");
731 spat->blocked = FALSE;
732 }
733 }
734 }
735 } // end of if()
737 } // end of sp_attribute_table_object_modified()
741 static void
742 sp_attribute_table_object_release (SPObject */*object*/, SPAttributeTable *spat)
743 {
744 sp_attribute_table_set_object (spat, NULL, 0, NULL, NULL);
745 }
749 static void
750 sp_attribute_table_entry_changed ( GtkEditable *editable,
751 SPAttributeTable *spat )
752 {
753 if (!spat->blocked)
754 {
755 gint i;
756 for (i = 0; i < spat->num_attr; i++) {
758 if (GTK_WIDGET (editable) == spat->entries[i]) {
759 const gchar *text;
760 spat->blocked = TRUE;
761 text = gtk_entry_get_text (GTK_ENTRY (spat->entries[i]));
763 if (!*text)
764 text = NULL;
766 if (spat->hasobj && spat->src.object) {
767 SP_OBJECT_REPR (spat->src.object)->setAttribute(spat->attributes[i], text, false);
768 sp_document_done (SP_OBJECT_DOCUMENT (spat->src.object), SP_VERB_NONE,
769 _("Set attribute"));
771 } else if (spat->src.repr) {
773 spat->src.repr->setAttribute(spat->attributes[i], text, false);
774 /* TODO: Warning! Undo will not be flushed in given case */
775 }
776 spat->blocked = FALSE;
777 return;
778 }
779 }
780 g_warning ("file %s: line %d: Entry signalled change, but there is no such entry", __FILE__, __LINE__);
781 } // end of if()
783 } // end of sp_attribute_table_entry_changed()
785 /*
786 Local Variables:
787 mode:c++
788 c-file-style:"stroustrup"
789 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
790 indent-tabs-mode:nil
791 fill-column:99
792 End:
793 */
794 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :