1 /** @file
2 * @brief Widget that listens and modifies repr attributes
3 */
4 /* Authors:
5 * Lauris Kaplinski <lauris@ximian.com>
6 *
7 * Copyright (C) 2001 Ximian, Inc.
8 * Released under GNU GPL, read the file 'COPYING' for more information
9 */
11 #include <gtk/gtktable.h>
12 #include <gtk/gtklabel.h>
13 #include "xml/repr.h"
14 #include "macros.h"
15 #include "document.h"
16 #include "sp-object.h"
17 #include <glibmm/i18n.h>
19 #include <sigc++/functors/ptr_fun.h>
20 #include <sigc++/adaptors/bind.h>
22 #include "sp-attribute-widget.h"
24 static void sp_attribute_widget_class_init (SPAttributeWidgetClass *klass);
25 static void sp_attribute_widget_init (SPAttributeWidget *widget);
26 static void sp_attribute_widget_destroy (GtkObject *object);
28 static void sp_attribute_widget_changed (GtkEditable *editable);
30 static void sp_attribute_widget_object_modified ( SPObject *object,
31 guint flags,
32 SPAttributeWidget *spaw );
33 static void sp_attribute_widget_object_release ( SPObject *object,
34 SPAttributeWidget *spaw );
36 static GtkEntryClass *parent_class;
41 GType sp_attribute_widget_get_type(void)
42 {
43 static GtkType type = 0;
44 if (!type) {
45 GTypeInfo info = {
46 sizeof(SPAttributeWidgetClass),
47 0, // base_init
48 0, // base_finalize
49 (GClassInitFunc)sp_attribute_widget_class_init,
50 0, // class_finalize
51 0, // class_data
52 sizeof(SPAttributeWidget),
53 0, // n_preallocs
54 (GInstanceInitFunc)sp_attribute_widget_init,
55 0 // value_table
56 };
57 type = g_type_register_static(GTK_TYPE_ENTRY, "SPAttributeWidget", &info, static_cast<GTypeFlags>(0));
58 }
59 return type;
60 } // end of sp_attribute_widget_get_type()
64 static void
65 sp_attribute_widget_class_init (SPAttributeWidgetClass *klass)
66 {
67 GtkObjectClass *object_class;
68 GtkWidgetClass *widget_class;
69 GtkEditableClass *editable_class;
71 object_class = GTK_OBJECT_CLASS (klass);
72 widget_class = GTK_WIDGET_CLASS (klass);
73 editable_class = GTK_EDITABLE_CLASS (klass);
75 parent_class = (GtkEntryClass*)gtk_type_class (GTK_TYPE_ENTRY);
77 object_class->destroy = sp_attribute_widget_destroy;
79 editable_class->changed = sp_attribute_widget_changed;
81 } // end of sp_attribute_widget_class_init()
85 static void
86 sp_attribute_widget_init (SPAttributeWidget *spaw)
87 {
88 spaw->blocked = FALSE;
89 spaw->hasobj = FALSE;
91 spaw->src.object = NULL;
93 spaw->attribute = NULL;
95 new (&spaw->modified_connection) sigc::connection();
96 new (&spaw->release_connection) sigc::connection();
97 }
101 static void
102 sp_attribute_widget_destroy (GtkObject *object)
103 {
105 SPAttributeWidget *spaw;
107 spaw = SP_ATTRIBUTE_WIDGET (object);
109 if (spaw->attribute) {
110 g_free (spaw->attribute);
111 spaw->attribute = NULL;
112 }
115 if (spaw->hasobj) {
117 if (spaw->src.object) {
118 spaw->modified_connection.disconnect();
119 spaw->release_connection.disconnect();
120 spaw->src.object = NULL;
121 }
122 } else {
124 if (spaw->src.repr) {
125 spaw->src.repr = Inkscape::GC::release(spaw->src.repr);
126 }
127 } // end of if()
129 spaw->modified_connection.~connection();
130 spaw->release_connection.~connection();
132 ((GtkObjectClass *) parent_class)->destroy (object);
134 }
138 static void
139 sp_attribute_widget_changed (GtkEditable *editable)
140 {
142 SPAttributeWidget *spaw;
144 spaw = SP_ATTRIBUTE_WIDGET (editable);
146 if (!spaw->blocked) {
148 const gchar *text;
149 spaw->blocked = TRUE;
150 text = gtk_entry_get_text (GTK_ENTRY (spaw));
151 if (!*text)
152 text = NULL;
154 if (spaw->hasobj && spaw->src.object) {
156 SP_OBJECT_REPR (spaw->src.object)->setAttribute(spaw->attribute, text, false);
157 sp_document_done (SP_OBJECT_DOCUMENT (spaw->src.object), SP_VERB_NONE,
158 _("Set attribute"));
160 } else if (spaw->src.repr) {
162 spaw->src.repr->setAttribute(spaw->attribute, text, false);
163 /* TODO: Warning! Undo will not be flushed in given case */
164 }
165 spaw->blocked = FALSE;
166 }
168 } // end of sp_attribute_widget_changed()
172 GtkWidget *
173 sp_attribute_widget_new ( SPObject *object, const gchar *attribute )
174 {
175 SPAttributeWidget *spaw;
177 g_return_val_if_fail (!object || SP_IS_OBJECT (object), NULL);
178 g_return_val_if_fail (!object || attribute, NULL);
180 spaw = (SPAttributeWidget*)gtk_type_new (SP_TYPE_ATTRIBUTE_WIDGET);
182 sp_attribute_widget_set_object (spaw, object, attribute);
184 return GTK_WIDGET (spaw);
186 } // end of sp_attribute_widget_new()
190 GtkWidget *
191 sp_attribute_widget_new_repr ( Inkscape::XML::Node *repr, const gchar *attribute )
192 {
193 SPAttributeWidget *spaw;
195 spaw = (SPAttributeWidget*)gtk_type_new (SP_TYPE_ATTRIBUTE_WIDGET);
197 sp_attribute_widget_set_repr (spaw, repr, attribute);
199 return GTK_WIDGET (spaw);
200 }
204 void
205 sp_attribute_widget_set_object ( SPAttributeWidget *spaw,
206 SPObject *object,
207 const gchar *attribute )
208 {
210 g_return_if_fail (spaw != NULL);
211 g_return_if_fail (SP_IS_ATTRIBUTE_WIDGET (spaw));
212 g_return_if_fail (!object || SP_IS_OBJECT (object));
213 g_return_if_fail (!object || attribute);
214 g_return_if_fail (attribute != NULL);
216 if (spaw->attribute) {
217 g_free (spaw->attribute);
218 spaw->attribute = NULL;
219 }
221 if (spaw->hasobj) {
223 if (spaw->src.object) {
224 spaw->modified_connection.disconnect();
225 spaw->release_connection.disconnect();
226 spaw->src.object = NULL;
227 }
228 } else {
230 if (spaw->src.repr) {
231 spaw->src.repr = Inkscape::GC::release(spaw->src.repr);
232 }
233 }
235 spaw->hasobj = TRUE;
237 if (object) {
238 const gchar *val;
240 spaw->blocked = TRUE;
241 spaw->src.object = object;
243 spaw->modified_connection = object->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_attribute_widget_object_modified), spaw));
244 spaw->release_connection = object->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_attribute_widget_object_release), spaw));
246 spaw->attribute = g_strdup (attribute);
248 val = SP_OBJECT_REPR (object)->attribute(attribute);
249 gtk_entry_set_text (GTK_ENTRY (spaw), val ? val : (const gchar *) "");
250 spaw->blocked = FALSE;
251 }
253 gtk_widget_set_sensitive (GTK_WIDGET (spaw), (spaw->src.object != NULL));
255 } // end of sp_attribute_widget_set_object()
259 void
260 sp_attribute_widget_set_repr ( SPAttributeWidget *spaw,
261 Inkscape::XML::Node *repr,
262 const gchar *attribute )
263 {
265 g_return_if_fail (spaw != NULL);
266 g_return_if_fail (SP_IS_ATTRIBUTE_WIDGET (spaw));
267 g_return_if_fail (attribute != NULL);
269 if (spaw->attribute) {
270 g_free (spaw->attribute);
271 spaw->attribute = NULL;
272 }
274 if (spaw->hasobj) {
276 if (spaw->src.object) {
277 spaw->modified_connection.disconnect();
278 spaw->release_connection.disconnect();
279 spaw->src.object = NULL;
280 }
281 } else {
283 if (spaw->src.repr) {
284 spaw->src.repr = Inkscape::GC::release(spaw->src.repr);
285 }
286 }
288 spaw->hasobj = FALSE;
290 if (repr) {
291 const gchar *val;
293 spaw->blocked = TRUE;
294 spaw->src.repr = Inkscape::GC::anchor(repr);
295 spaw->attribute = g_strdup (attribute);
297 val = repr->attribute(attribute);
298 gtk_entry_set_text (GTK_ENTRY (spaw), val ? val : (const gchar *) "");
299 spaw->blocked = FALSE;
300 }
302 gtk_widget_set_sensitive (GTK_WIDGET (spaw), (spaw->src.repr != NULL));
304 } // end of sp_attribute_widget_set_repr()
308 static void
309 sp_attribute_widget_object_modified ( SPObject */*object*/,
310 guint flags,
311 SPAttributeWidget *spaw )
312 {
314 if (flags && SP_OBJECT_MODIFIED_FLAG) {
316 const gchar *val, *text;
317 val = SP_OBJECT_REPR (spaw->src.object)->attribute(spaw->attribute);
318 text = gtk_entry_get_text (GTK_ENTRY (spaw));
320 if (val || text) {
322 if (!val || !text || strcmp (val, text)) {
323 /* We are different */
324 spaw->blocked = TRUE;
325 gtk_entry_set_text ( GTK_ENTRY (spaw),
326 val ? val : (const gchar *) "");
327 spaw->blocked = FALSE;
328 } // end of if()
330 } // end of if()
332 } //end of if()
334 } // end of sp_attribute_widget_object_modified()
338 static void
339 sp_attribute_widget_object_release ( SPObject */*object*/,
340 SPAttributeWidget *spaw )
341 {
342 sp_attribute_widget_set_object (spaw, NULL, NULL);
343 }
347 /* SPAttributeTable */
349 static void sp_attribute_table_class_init (SPAttributeTableClass *klass);
350 static void sp_attribute_table_init (SPAttributeTable *widget);
351 static void sp_attribute_table_destroy (GtkObject *object);
353 static void sp_attribute_table_object_modified (SPObject *object, guint flags, SPAttributeTable *spaw);
354 static void sp_attribute_table_object_release (SPObject *object, SPAttributeTable *spaw);
355 static void sp_attribute_table_entry_changed (GtkEditable *editable, SPAttributeTable *spat);
357 static GtkVBoxClass *table_parent_class;
362 GType sp_attribute_table_get_type(void)
363 {
364 static GtkType type = 0;
365 if (!type) {
366 GTypeInfo info = {
367 sizeof(SPAttributeTableClass),
368 0, // base_init
369 0, // base_finalize
370 (GClassInitFunc)sp_attribute_table_class_init,
371 0, // class_finalize
372 0, // class_data
373 sizeof(SPAttributeTable),
374 0, // n_preallocs
375 (GInstanceInitFunc)sp_attribute_table_init,
376 0 // value_table
377 };
378 type = g_type_register_static(GTK_TYPE_VBOX, "SPAttributeTable", &info, static_cast<GTypeFlags>(0));
379 }
380 return type;
381 } // end of sp_attribute_table_get_type()
385 static void
386 sp_attribute_table_class_init (SPAttributeTableClass *klass)
387 {
388 GtkObjectClass *object_class;
389 GtkWidgetClass *widget_class;
391 object_class = GTK_OBJECT_CLASS (klass);
392 widget_class = GTK_WIDGET_CLASS (klass);
394 table_parent_class = (GtkVBoxClass*)gtk_type_class (GTK_TYPE_VBOX);
396 object_class->destroy = sp_attribute_table_destroy;
398 } // end of sp_attribute_table_class_init()
402 static void
403 sp_attribute_table_init ( SPAttributeTable *spat )
404 {
405 spat->blocked = FALSE;
406 spat->hasobj = FALSE;
407 spat->table = NULL;
408 spat->src.object = NULL;
409 spat->num_attr = 0;
410 spat->attributes = NULL;
411 spat->entries = NULL;
413 new (&spat->modified_connection) sigc::connection();
414 new (&spat->release_connection) sigc::connection();
415 }
417 static void
418 sp_attribute_table_destroy ( GtkObject *object )
419 {
420 SPAttributeTable *spat;
422 spat = SP_ATTRIBUTE_TABLE (object);
424 if (spat->attributes) {
425 gint i;
426 for (i = 0; i < spat->num_attr; i++) {
427 g_free (spat->attributes[i]);
428 }
429 g_free (spat->attributes);
430 spat->attributes = NULL;
431 }
433 if (spat->hasobj) {
435 if (spat->src.object) {
436 spat->modified_connection.disconnect();
437 spat->release_connection.disconnect();
438 spat->src.object = NULL;
439 }
440 } else {
441 if (spat->src.repr) {
442 spat->src.repr = Inkscape::GC::release(spat->src.repr);
443 }
444 } // end of if()
446 spat->modified_connection.~connection();
447 spat->release_connection.~connection();
449 if (spat->entries) {
450 g_free (spat->entries);
451 spat->entries = NULL;
452 }
454 spat->table = NULL;
456 if (((GtkObjectClass *) table_parent_class)->destroy) {
457 (* ((GtkObjectClass *) table_parent_class)->destroy) (object);
458 }
460 } // end of sp_attribute_table_destroy()
463 GtkWidget *
464 sp_attribute_table_new ( SPObject *object,
465 gint num_attr,
466 const gchar **labels,
467 const gchar **attributes )
468 {
469 SPAttributeTable *spat;
471 g_return_val_if_fail (!object || SP_IS_OBJECT (object), NULL);
472 g_return_val_if_fail (!object || (num_attr > 0), NULL);
473 g_return_val_if_fail (!num_attr || (labels && attributes), NULL);
475 spat = (SPAttributeTable*)gtk_type_new (SP_TYPE_ATTRIBUTE_TABLE);
477 sp_attribute_table_set_object (spat, object, num_attr, labels, attributes);
479 return GTK_WIDGET (spat);
481 } // end of sp_attribute_table_new()
485 GtkWidget *
486 sp_attribute_table_new_repr ( Inkscape::XML::Node *repr,
487 gint num_attr,
488 const gchar **labels,
489 const gchar **attributes )
490 {
491 SPAttributeTable *spat;
493 g_return_val_if_fail (!num_attr || (labels && attributes), NULL);
495 spat = (SPAttributeTable*)gtk_type_new (SP_TYPE_ATTRIBUTE_TABLE);
497 sp_attribute_table_set_repr (spat, repr, num_attr, labels, attributes);
499 return GTK_WIDGET (spat);
501 } // end of sp_attribute_table_new_repr()
505 #define XPAD 4
506 #define YPAD 0
508 void
509 sp_attribute_table_set_object ( SPAttributeTable *spat,
510 SPObject *object,
511 gint num_attr,
512 const gchar **labels,
513 const gchar **attributes )
514 {
516 g_return_if_fail (spat != NULL);
517 g_return_if_fail (SP_IS_ATTRIBUTE_TABLE (spat));
518 g_return_if_fail (!object || SP_IS_OBJECT (object));
519 g_return_if_fail (!object || (num_attr > 0));
520 g_return_if_fail (!num_attr || (labels && attributes));
522 if (spat->table) {
523 gtk_widget_destroy (spat->table);
524 spat->table = NULL;
525 }
527 if (spat->attributes) {
528 gint i;
529 for (i = 0; i < spat->num_attr; i++) {
530 g_free (spat->attributes[i]);
531 }
532 g_free (spat->attributes);
533 spat->attributes = NULL;
534 }
536 if (spat->entries) {
537 g_free (spat->entries);
538 spat->entries = NULL;
539 }
541 if (spat->hasobj) {
542 if (spat->src.object) {
543 spat->modified_connection.disconnect();
544 spat->release_connection.disconnect();
545 spat->src.object = NULL;
546 }
547 } else {
548 if (spat->src.repr) {
549 spat->src.repr = Inkscape::GC::release(spat->src.repr);
550 }
551 }
553 spat->hasobj = TRUE;
555 if (object) {
556 gint i;
558 spat->blocked = TRUE;
560 /* Set up object */
561 spat->src.object = object;
562 spat->num_attr = num_attr;
564 spat->modified_connection = object->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_attribute_table_object_modified), spat));
565 spat->release_connection = object->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_attribute_table_object_release), spat));
567 /* Create table */
568 spat->table = gtk_table_new (num_attr, 2, FALSE);
569 gtk_container_add (GTK_CONTAINER (spat), spat->table);
570 /* Arrays */
571 spat->attributes = g_new0 (gchar *, num_attr);
572 spat->entries = g_new0 (GtkWidget *, num_attr);
573 /* Fill rows */
574 for (i = 0; i < num_attr; i++) {
575 GtkWidget *w;
576 const gchar *val;
578 spat->attributes[i] = g_strdup (attributes[i]);
579 w = gtk_label_new (_(labels[i]));
580 gtk_widget_show (w);
581 gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
582 gtk_table_attach ( GTK_TABLE (spat->table), w, 0, 1, i, i + 1,
583 GTK_FILL,
584 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
585 XPAD, YPAD );
586 w = gtk_entry_new ();
587 gtk_widget_show (w);
588 val = SP_OBJECT_REPR (object)->attribute(attributes[i]);
589 gtk_entry_set_text (GTK_ENTRY (w), val ? val : (const gchar *) "");
590 gtk_table_attach ( GTK_TABLE (spat->table), w, 1, 2, i, i + 1,
591 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
592 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
593 XPAD, YPAD );
594 spat->entries[i] = w;
595 g_signal_connect ( G_OBJECT (w), "changed",
596 G_CALLBACK (sp_attribute_table_entry_changed),
597 spat );
598 }
599 /* Show table */
600 gtk_widget_show (spat->table);
602 spat->blocked = FALSE;
603 }
605 gtk_widget_set_sensitive ( GTK_WIDGET (spat),
606 (spat->src.object != NULL) );
608 } // end of sp_attribute_table_set_object()
612 void
613 sp_attribute_table_set_repr ( SPAttributeTable *spat,
614 Inkscape::XML::Node *repr,
615 gint num_attr,
616 const gchar **labels,
617 const gchar **attributes )
618 {
619 g_return_if_fail (spat != NULL);
620 g_return_if_fail (SP_IS_ATTRIBUTE_TABLE (spat));
621 g_return_if_fail (!num_attr || (labels && attributes));
623 if (spat->table) {
624 gtk_widget_destroy (spat->table);
625 spat->table = NULL;
626 }
628 if (spat->attributes) {
629 gint i;
630 for (i = 0; i < spat->num_attr; i++) {
631 g_free (spat->attributes[i]);
632 }
633 g_free (spat->attributes);
634 spat->attributes = NULL;
635 }
637 if (spat->entries) {
638 g_free (spat->entries);
639 spat->entries = NULL;
640 }
642 if (spat->hasobj) {
643 if (spat->src.object) {
644 spat->modified_connection.disconnect();
645 spat->release_connection.disconnect();
646 spat->src.object = NULL;
647 }
648 } else {
649 if (spat->src.repr) {
650 spat->src.repr = Inkscape::GC::release(spat->src.repr);
651 }
652 }
654 spat->hasobj = FALSE;
656 if (repr) {
657 gint i;
659 spat->blocked = TRUE;
661 /* Set up repr */
662 spat->src.repr = Inkscape::GC::anchor(repr);
663 spat->num_attr = num_attr;
664 /* Create table */
665 spat->table = gtk_table_new (num_attr, 2, FALSE);
666 gtk_container_add (GTK_CONTAINER (spat), spat->table);
667 /* Arrays */
668 spat->attributes = g_new0 (gchar *, num_attr);
669 spat->entries = g_new0 (GtkWidget *, num_attr);
671 /* Fill rows */
672 for (i = 0; i < num_attr; i++) {
673 GtkWidget *w;
674 const gchar *val;
676 spat->attributes[i] = g_strdup (attributes[i]);
677 w = gtk_label_new (labels[i]);
678 gtk_widget_show (w);
679 gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
680 gtk_table_attach ( GTK_TABLE (spat->table), w, 0, 1, i, i + 1,
681 GTK_FILL,
682 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
683 XPAD, YPAD );
684 w = gtk_entry_new ();
685 gtk_widget_show (w);
686 val = repr->attribute(attributes[i]);
687 gtk_entry_set_text (GTK_ENTRY (w), val ? val : (const gchar *) "");
688 gtk_table_attach ( GTK_TABLE (spat->table), w, 1, 2, i, i + 1,
689 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
690 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
691 XPAD, YPAD );
692 spat->entries[i] = w;
693 g_signal_connect ( G_OBJECT (w), "changed",
694 G_CALLBACK (sp_attribute_table_entry_changed),
695 spat );
696 }
697 /* Show table */
698 gtk_widget_show (spat->table);
700 spat->blocked = FALSE;
701 }
703 gtk_widget_set_sensitive (GTK_WIDGET (spat), (spat->src.repr != NULL));
705 } // end of sp_attribute_table_set_repr()
709 static void
710 sp_attribute_table_object_modified ( SPObject */*object*/,
711 guint flags,
712 SPAttributeTable *spat )
713 {
714 if (flags && SP_OBJECT_MODIFIED_FLAG)
715 {
716 gint i;
717 for (i = 0; i < spat->num_attr; i++) {
718 const gchar *val, *text;
719 val = SP_OBJECT_REPR (spat->src.object)->attribute(spat->attributes[i]);
720 text = gtk_entry_get_text (GTK_ENTRY (spat->entries[i]));
721 if (val || text) {
722 if (!val || !text || strcmp (val, text)) {
723 /* We are different */
724 spat->blocked = TRUE;
725 gtk_entry_set_text ( GTK_ENTRY (spat->entries[i]),
726 val ? val : (const gchar *) "");
727 spat->blocked = FALSE;
728 }
729 }
730 }
731 } // end of if()
733 } // end of sp_attribute_table_object_modified()
737 static void
738 sp_attribute_table_object_release (SPObject */*object*/, SPAttributeTable *spat)
739 {
740 sp_attribute_table_set_object (spat, NULL, 0, NULL, NULL);
741 }
745 static void
746 sp_attribute_table_entry_changed ( GtkEditable *editable,
747 SPAttributeTable *spat )
748 {
749 if (!spat->blocked)
750 {
751 gint i;
752 for (i = 0; i < spat->num_attr; i++) {
754 if (GTK_WIDGET (editable) == spat->entries[i]) {
755 const gchar *text;
756 spat->blocked = TRUE;
757 text = gtk_entry_get_text (GTK_ENTRY (spat->entries[i]));
759 if (!*text)
760 text = NULL;
762 if (spat->hasobj && spat->src.object) {
763 SP_OBJECT_REPR (spat->src.object)->setAttribute(spat->attributes[i], text, false);
764 sp_document_done (SP_OBJECT_DOCUMENT (spat->src.object), SP_VERB_NONE,
765 _("Set attribute"));
767 } else if (spat->src.repr) {
769 spat->src.repr->setAttribute(spat->attributes[i], text, false);
770 /* TODO: Warning! Undo will not be flushed in given case */
771 }
772 spat->blocked = FALSE;
773 return;
774 }
775 }
776 g_warning ("file %s: line %d: Entry signalled change, but there is no such entry", __FILE__, __LINE__);
777 } // end of if()
779 } // end of sp_attribute_table_entry_changed()
781 /*
782 Local Variables:
783 mode:c++
784 c-file-style:"stroustrup"
785 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
786 indent-tabs-mode:nil
787 fill-column:99
788 End:
789 */
790 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :